From stefan.wahren at i2se.com  Wed Jun  1 01:27:49 2016
From: stefan.wahren at i2se.com (Stefan Wahren)
Date: Wed, 1 Jun 2016 10:27:49 +0200
Subject: [PATCH v3 0/2] nvmem: remove regmap dependency
In-Reply-To: <1462214174-469-1-git-send-email-srinivas.kandagatla@linaro.org>
References: <1462214174-469-1-git-send-email-srinivas.kandagatla@linaro.org>
Message-ID: <7eec3215-b455-6567-28a2-e2a860e1d3ec@i2se.com>

Hi Greg,

Am 02.05.2016 um 20:36 schrieb Srinivas Kandagatla:
> Hi Greg, 
>
> This is v3 patchset for the leftover 2 patches for nvmem regmap
> removal series [1]. These patches are based on char-misc tree.
>
> nvmem uses regmap_raw_read/write apis to read/write data from providers,
> With recent patch 922a9f936e40 ("regmap: mmio: Convert to regmap_bus
> and fix accessor usage") nvmem providers based on regmap-mmio stopped
> working, as nvmem core was using raw accessors.
> This issue can be fixed temporarly by moving to other regmap apis,
> but we might hit same issue in future, and regmap looks like an
> overdo for nvmem. Moving to interfaces based on read/write callbacks
> from providers would be more robust.
>
> This patchset converts the nvmem core and nvmem provider drivers to
> use the new callbacks. Tested this patchset on qfprom and at24 drivers.
> Other driver are only compile tested, any testing on them would be great.
>
> Most of the patches have been applied to char-misc tree, these are the
> two patches which had some outstanding comments on mxs nvmemprovider,
> which are now fixed.
>
> Changes since v2:
>  - Fixed the mxs size and dt data pointer spotted by Stefan and Fabio
>
> Changes since v1:
>  - rebased mtk-efuse on top of char-misc
>  - addressed concerns raised by Stefan Wahren.
>
> [1] https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1130026.html
>
> Thanks,
> srini
>
> Srinivas Kandagatla (2):
>   nvmem: mtk-efuse: remove nvmem regmap dependency
>   nvmem: mxs-ocotp: remove nvmem regmap dependency
>
>  drivers/nvmem/Kconfig     |  1 -
>  drivers/nvmem/mtk-efuse.c | 47 ++++++++++++++++++---------
>  drivers/nvmem/mxs-ocotp.c | 83 +++++++++++++----------------------------------
>  3 files changed, 54 insertions(+), 77 deletions(-)
>

any objections about this series or is it still in queue?

Stefan




From daniel.lezcano at linaro.org  Wed Jun  1 01:34:47 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Wed,  1 Jun 2016 10:34:47 +0200
Subject: [PATCH 4/9] clocksource/drivers/mkt_timer: Convert init function to
 return error
In-Reply-To: <1464770093-12667-1-git-send-email-daniel.lezcano@linaro.org>
References: <1464770093-12667-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1464770093-12667-5-git-send-email-daniel.lezcano@linaro.org>

The init functions do not return any error. They behave as the following:

 - panic, thus leading to a kernel crash while another timer may work and
   make the system boot up correctly

 or

 - print an error and let the caller unaware if the state of the system

Change that by converting the init functions to return an error conforming
to the CLOCKSOURCE_OF_RET prototype.

Proper error handling (rollback, errno value) will be changed later case
by case, thus this change just return back an error or success in the init
function.

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 drivers/clocksource/mtk_timer.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 7e583f8..432a2c0 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -181,7 +181,7 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
 			evt->gpt_base + GPT_IRQ_EN_REG);
 }
 
-static void __init mtk_timer_init(struct device_node *node)
+static int __init mtk_timer_init(struct device_node *node)
 {
 	struct mtk_clock_event_device *evt;
 	struct resource res;
@@ -190,7 +190,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	evt = kzalloc(sizeof(*evt), GFP_KERNEL);
 	if (!evt)
-		return;
+		return -ENOMEM;
 
 	evt->dev.name = "mtk_tick";
 	evt->dev.rating = 300;
@@ -248,7 +248,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	mtk_timer_enable_irq(evt, GPT_CLK_EVT);
 
-	return;
+	return 0;
 
 err_clk_disable:
 	clk_disable_unprepare(clk);
@@ -262,5 +262,7 @@ err_mem:
 	release_mem_region(res.start, resource_size(&res));
 err_kzalloc:
 	kfree(evt);
+
+	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
-- 
1.9.1



From yt.shen at mediatek.com  Wed Jun  1 02:09:05 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Wed, 1 Jun 2016 17:09:05 +0800
Subject: [RFC v2 1/5] drm/mediatek: rename macros, add chip suffix
In-Reply-To: <20160530104129.GA8388@ulmo.ba.sec>
References: <1463756736-46573-1-git-send-email-yt.shen@mediatek.com>
 <1463756736-46573-2-git-send-email-yt.shen@mediatek.com>
 <20160530104129.GA8388@ulmo.ba.sec>
Message-ID: <1464772145.10410.27.camel@mtksdaap41>

Hi Thierry,

On Mon, 2016-05-30 at 12:41 +0200, Thierry Reding wrote:
> On Fri, May 20, 2016 at 11:05:32PM +0800, yt.shen at mediatek.com wrote:
> > From: YT Shen <yt.shen at mediatek.com>
> > 
> > Add MT8173 suffix for hardware related macros.
> > 
> > Signed-off-by: YT Shen <yt.shen at mediatek.com>
> > ---
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp.c |   62 ++++++++++++++++----------------
> >  1 file changed, 31 insertions(+), 31 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > index 17ba935..d6aafd4 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > @@ -36,21 +36,21 @@
> >  #define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
> >  #define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
> >  
> > -#define MUTEX_MOD_DISP_OVL0		BIT(11)
> > -#define MUTEX_MOD_DISP_OVL1		BIT(12)
> > -#define MUTEX_MOD_DISP_RDMA0		BIT(13)
> > -#define MUTEX_MOD_DISP_RDMA1		BIT(14)
> > -#define MUTEX_MOD_DISP_RDMA2		BIT(15)
> > -#define MUTEX_MOD_DISP_WDMA0		BIT(16)
> > -#define MUTEX_MOD_DISP_WDMA1		BIT(17)
> > -#define MUTEX_MOD_DISP_COLOR0		BIT(18)
> > -#define MUTEX_MOD_DISP_COLOR1		BIT(19)
> > -#define MUTEX_MOD_DISP_AAL		BIT(20)
> > -#define MUTEX_MOD_DISP_GAMMA		BIT(21)
> > -#define MUTEX_MOD_DISP_UFOE		BIT(22)
> > -#define MUTEX_MOD_DISP_PWM0		BIT(23)
> > -#define MUTEX_MOD_DISP_PWM1		BIT(24)
> > -#define MUTEX_MOD_DISP_OD		BIT(25)
> > +#define MUTEX_MOD_DISP_OVL0_MT8173		BIT(11)
> > +#define MUTEX_MOD_DISP_OVL1_MT8173		BIT(12)
> > +#define MUTEX_MOD_DISP_RDMA0_MT8173		BIT(13)
> > +#define MUTEX_MOD_DISP_RDMA1_MT8173		BIT(14)
> > +#define MUTEX_MOD_DISP_RDMA2_MT8173		BIT(15)
> > +#define MUTEX_MOD_DISP_WDMA0_MT8173		BIT(16)
> > +#define MUTEX_MOD_DISP_WDMA1_MT8173		BIT(17)
> > +#define MUTEX_MOD_DISP_COLOR0_MT8173		BIT(18)
> > +#define MUTEX_MOD_DISP_COLOR1_MT8173		BIT(19)
> > +#define MUTEX_MOD_DISP_AAL_MT8173		BIT(20)
> > +#define MUTEX_MOD_DISP_GAMMA_MT8173		BIT(21)
> > +#define MUTEX_MOD_DISP_UFOE_MT8173		BIT(22)
> > +#define MUTEX_MOD_DISP_PWM0_MT8173		BIT(23)
> > +#define MUTEX_MOD_DISP_PWM1_MT8173		BIT(24)
> > +#define MUTEX_MOD_DISP_OD_MT8173		BIT(25)
> 
> Just a random fly-by comment: this looks like a hardware spinlock, have
> you ever considered implementing this as a hwspinlock driver? See the
> drivers/hwspinlock subdirectory for existing examples.
> 
> Thierry

I see the drivers/hwspinlock and Documentation/hwspinlock.txt
Yes, we can implement this like a hardware spinlock.  But I have some
questions, the document says:

"Hardware spinlock modules provide hardware assistance for
synchronization and mutual exclusion between heterogeneous processors
and those not operating under a single, shared operating system"

The mutex here is a handshake interface between software and hardware.
The hardware is the display controller, the software is the drm display
driver, and no other consumers need to access this mutex.  So I think
that using hwspinlock to implement a bit too complicated, am I right?

I will use iopoll macros to implement this part in the next version.
Thanks.

yt.shen





From yt.shen at mediatek.com  Wed Jun  1 02:10:26 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Wed, 1 Jun 2016 17:10:26 +0800
Subject: [RFC v2 3/5] drm/mediatek: add *driver_data for different
 hardware settings
In-Reply-To: <20160530104553.GB8388@ulmo.ba.sec>
References: <1463756736-46573-1-git-send-email-yt.shen@mediatek.com>
 <1463756736-46573-4-git-send-email-yt.shen@mediatek.com>
 <1463996582.30093.5.camel@mtksdaap41> <20160530104553.GB8388@ulmo.ba.sec>
Message-ID: <1464772226.10410.28.camel@mtksdaap41>

Hi Thierry,

On Mon, 2016-05-30 at 12:45 +0200, Thierry Reding wrote:
> On Mon, May 23, 2016 at 05:43:02PM +0800, CK Hu wrote:
> > Hi, YT:
> > 
> > One comment below.
> > 
> > On Fri, 2016-05-20 at 23:05 +0800, yt.shen at mediatek.com wrote:
> > > From: YT Shen <yt.shen at mediatek.com>
> > > 
> > > There are some hardware settings changed, between MT8173 & MT2701:
> > > DISP_OVL address offset changed, color format definition changed.
> > > DISP_RDMA fifo size changed.
> > > DISP_COLOR offset changed.
> > > 
> > > Signed-off-by: YT Shen <yt.shen at mediatek.com>
> > > ---
> > > +
> > > +static inline struct mtk_ddp_comp_driver_data *mtk_ovl_get_driver_data(
> > > +	struct platform_device *pdev)
> > > +{
> > > +	const struct of_device_id *of_id =
> > > +		of_match_device(mtk_disp_ovl_driver_dt_match, &pdev->dev);
> > > +
> > > +	return (struct mtk_ddp_comp_driver_data *)of_id->data;
> > > +}
> > > +
> > > +static inline struct mtk_ddp_comp_driver_data *mtk_rdma_get_driver_data(
> > > +	struct platform_device *pdev)
> > > +{
> > > +	const struct of_device_id *of_id =
> > > +		of_match_device(mtk_disp_rdma_driver_dt_match, &pdev->dev);
> > > +
> > > +	return (struct mtk_ddp_comp_driver_data *)of_id->data;
> > > +}
> > > +
> > > +static inline struct mtk_ddp_comp_driver_data *mtk_color_get_driver_data(
> > > +	struct device_node *node)
> > > +{
> > > +	const struct of_device_id *of_id =
> > > +		of_match_node(mtk_disp_color_driver_dt_match, node);
> > > +
> > > +	return (struct mtk_ddp_comp_driver_data *)of_id->data;
> > > +}
> > > + 
> > 
> > These three functions looks the same with different parameter:
> > mtk_disp_ovl_driver_dt_match, mtk_disp_rdma_driver_dt_match, and
> > mtk_disp_color_driver_dt_match. So merge them to prevent duplicated
> > code.
> 
> I think what you really want is of_device_get_match_data().
> 
> Thierry

Great, that function is really what we need, thanks.

yt.shen




From hs.liao at mediatek.com  Wed Jun  1 02:57:00 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Wed, 1 Jun 2016 17:57:00 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <574DEE40.9010008@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com>
Message-ID: <1464775020.11122.40.camel@mtksdaap41>

Hi Mathias,

Please see my inline reply.

On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
> 
> On 31/05/16 10:36, Horng-Shyang Liao wrote:
> > Hi Mathias,
> >
> > Please see my inline reply.
> >
> > On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
> >>
> >> On 30/05/16 05:19, HS Liao wrote:
> >>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> >>> CMDQ is used to help read/write registers with critical time limitation,
> >>> such as updating display configuration during the vblank. It controls
> >>> Global Command Engine (GCE) hardware to achieve this requirement.
> >>> Currently, CMDQ only supports display related hardwares, but we expect
> >>> it can be extended to other hardwares for future requirements.
> >>>
> >>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> >>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> >>> ---
> >>>    drivers/soc/mediatek/Kconfig    |  10 +
> >>>    drivers/soc/mediatek/Makefile   |   1 +
> >>>    drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> >>>    include/soc/mediatek/cmdq.h     | 197 +++++++++
> >>>    4 files changed, 1151 insertions(+)
> >>>    create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> >>>    create mode 100644 include/soc/mediatek/cmdq.h
> >>>
> >>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> >>> index 0a4ea80..c4ad75c 100644
> >>> --- a/drivers/soc/mediatek/Kconfig
> >>> +++ b/drivers/soc/mediatek/Kconfig
> >>> @@ -1,6 +1,16 @@
> >>>    #
> >>>    # MediaTek SoC drivers
> >>>    #
> >>> +config MTK_CMDQ
> >>> +	bool "MediaTek CMDQ Support"
> >>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> 
> depends on ARM64 ?

Will add ARM64.

> >>> +	select MTK_INFRACFG
> >>> +	help
> >>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> >>> +	  driver. The CMDQ is used to help read/write registers with critical
> >>> +	  time limitation, such as updating display configuration during the
> >>> +	  vblank.
> >>> +
> >>>    config MTK_INFRACFG
> >>>    	bool "MediaTek INFRACFG Support"
> >>>    	depends on ARCH_MEDIATEK || COMPILE_TEST
> >>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> >>> index 12998b0..f7397ef 100644
> >>> --- a/drivers/soc/mediatek/Makefile
> >>> +++ b/drivers/soc/mediatek/Makefile
> >>> @@ -1,3 +1,4 @@
> >>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
> >>>    obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> >>>    obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
> >>>    obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
> >>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
> >>> new file mode 100644
> >>> index 0000000..e9d6e1c
> >>> --- /dev/null
> >>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
> >>> @@ -0,0 +1,943 @@
> >>> +/*
> >>> + * Copyright (c) 2015 MediaTek Inc.
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or modify
> >>> + * it under the terms of the GNU General Public License version 2 as
> >>> + * published by the Free Software Foundation.
> >>> + *
> >>> + * This program is distributed in the hope that it will be useful,
> >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>> + * GNU General Public License for more details.
> >>> + */
> >>> +
> >>> +#include <linux/clk.h>
> >>> +#include <linux/clk-provider.h>
> >>> +#include <linux/completion.h>
> >>> +#include <linux/dma-mapping.h>
> >>> +#include <linux/errno.h>
> >>> +#include <linux/interrupt.h>
> >>> +#include <linux/iopoll.h>
> >>> +#include <linux/kernel.h>
> >>> +#include <linux/kthread.h>
> >>> +#include <linux/module.h>
> >>> +#include <linux/mutex.h>
> >>> +#include <linux/of_address.h>
> >>> +#include <linux/of_irq.h>
> >>> +#include <linux/platform_device.h>
> >>> +#include <linux/slab.h>
> >>> +#include <linux/spinlock.h>
> >>> +#include <linux/suspend.h>
> >>> +#include <linux/workqueue.h>
> >>> +#include <soc/mediatek/cmdq.h>
> >>> +
> >>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
> >>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
> >>> +#define CMDQ_TIMEOUT_MS			1000
> >>> +#define CMDQ_IRQ_MASK			0xffff
> >>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
> >>> +#define CMDQ_CLK_NAME			"gce"
> >>
> >> We can put the names in directly to un-bloat the defines.
> >
> > I will use the names directly and remove defines.
> >
> >>> +
> >>> +#define CMDQ_CURR_IRQ_STATUS		0x010
> >>> +#define CMDQ_CURR_LOADED_THR		0x018
> >>> +#define CMDQ_THR_SLOT_CYCLES		0x030
> >>> +
> >>> +#define CMDQ_THR_BASE			0x100
> >>> +#define CMDQ_THR_SHIFT			0x080
> >>
> >> Wouldn't be CMDQ_THR_SIZE more accurate?
> >
> > Will rename it.
> >
> >>> +#define CMDQ_THR_WARM_RESET		0x00
> >>> +#define CMDQ_THR_ENABLE_TASK		0x04
> >>> +#define CMDQ_THR_SUSPEND_TASK		0x08
> >>> +#define CMDQ_THR_CURR_STATUS		0x0c
> >>> +#define CMDQ_THR_IRQ_STATUS		0x10
> >>> +#define CMDQ_THR_IRQ_ENABLE		0x14
> >>> +#define CMDQ_THR_CURR_ADDR		0x20
> >>> +#define CMDQ_THR_END_ADDR		0x24
> >>> +#define CMDQ_THR_CFG			0x40
> >>> +
> >>> +#define CMDQ_THR_ENABLED		0x1
> >>> +#define CMDQ_THR_DISABLED		0x0
> >>> +#define CMDQ_THR_SUSPEND		0x1
> >>> +#define CMDQ_THR_RESUME			0x0
> >>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
> >>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
> >>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
> >>> +#define CMDQ_THR_PRIORITY		3
> >>> +#define CMDQ_THR_IRQ_DONE		0x1
> >>> +#define CMDQ_THR_IRQ_ERROR		0x12
> >>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
> >>
> >> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
> >
> > Will do.
> >
> >>> +#define CMDQ_THR_IRQ_MASK		0x13
> >>
> >> never used.
> >
> > Will remove.
> >
> >>> +#define CMDQ_THR_EXECUTING		BIT(31)
> >>> +
> >>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
> >>> +#define CMDQ_SUBSYS_MASK		0x1f
> >>> +#define CMDQ_OP_CODE_MASK		0xff000000
> >>> +
> >>> +#define CMDQ_OP_CODE_SHIFT		24
> >>
> >> Couldn't we connect the mask with the shift, or aren't they related?
> >>
> >> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
> >
> > Will do.
> >
> >>> +#define CMDQ_SUBSYS_SHIFT		16
> >>> +
> >>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
> >>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
> >>> +#define CMDQ_JUMP_BY_PA			0x10000001
> >>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
> >>> +#define CMDQ_WFE_UPDATE			BIT(31)
> >>> +#define CMDQ_WFE_WAIT			BIT(15)
> >>> +#define CMDQ_WFE_WAIT_VALUE		0x1
> >>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
> >>> +
> >>> +enum cmdq_thread_index {
> >>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
> >>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
> >>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
> >>> +	CMDQ_THR_MAX_COUNT,	/* max */
> >>> +};
> >>> +
> >>> +/*
> >>> + * CMDQ_CODE_MOVE:
> >>> + *   move value into internal register as mask
> >>> + *   format: op mask
> >>> + * CMDQ_CODE_WRITE:
> >>> + *   write value into target register
> >>> + *   format: op subsys address value
> >>> + * CMDQ_CODE_JUMP:
> >>> + *   jump by offset
> >>> + *   format: op offset
> >>> + * CMDQ_CODE_WFE:
> >>> + *   wait for event and clear
> >>> + *   it is just clear if no wait
> >>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
> >>> + *           [clear] op event update:1 to_wait:0 wait:0
> >>> + * CMDQ_CODE_EOC:
> >>> + *   end of command
> >>> + *   format: op irq_flag
> >>> + */
> >>
> >> I think we need more documentation of how this command queue engine is
> >> working. If not, I think it will be really complicated to understand how
> >> to use this.
> >>
> >>> +enum cmdq_code {
> >>> +	CMDQ_CODE_MOVE = 0x02,
> >>> +	CMDQ_CODE_WRITE = 0x04,
> >>> +	CMDQ_CODE_JUMP = 0x10,
> >>> +	CMDQ_CODE_WFE = 0x20,
> >>> +	CMDQ_CODE_EOC = 0x40,
> >>> +};
> >>> +
> >>> +enum cmdq_task_state {
> >>> +	TASK_STATE_BUSY, /* running on a GCE thread */
> >>> +	TASK_STATE_ERROR,
> >>> +	TASK_STATE_DONE,
> >>> +};
> >>> +
> >>> +struct cmdq_task_cb {
> >>> +	cmdq_async_flush_cb	cb;
> >>> +	void			*data;
> >>> +};
> >>> +
> >>> +struct cmdq_thread {
> >>> +	void __iomem		*base;
> >>> +	struct list_head	task_busy_list;
> >>> +	wait_queue_head_t	wait_task_done;
> >>> +};
> >>> +
> >>> +struct cmdq_task {
> >>> +	struct cmdq		*cmdq;
> >>> +	struct list_head	list_entry;
> >>> +	enum cmdq_task_state	task_state;
> >>> +	void			*va_base;
> >>> +	dma_addr_t		pa_base;
> >>> +	u64			engine_flag;
> >>> +	size_t			command_size;
> >>> +	u32			num_cmd;
> >>
> >> num_cmd is directly connected to command_size. I prefer to just keep
> >> num_cmd and calculate the command_size where necessary.
> >
> > After I trace code, I prefer to keep command_size and calculate num_cmd
> > where necessary. What do you think?
> >
> 
> I suppose you prefer this, as you are writing to the GCE depending on 
> the command_size. I think it is worth to create a macro for the 
> calculation of the number of commands, to make the code more readable.
> Would be nice if you would just pass cmdq_task to it and it would return 
> the number. Just as an idea.

Will add macro.

> >>> +	struct cmdq_thread	*thread;
> >>> +	struct cmdq_task_cb	cb;
> >>> +	struct work_struct	release_work;
> >>> +};
> >>> +
> >>> +struct cmdq {
> >>> +	struct device		*dev;
> >>> +	void __iomem		*base;
> >>> +	u32			irq;
> >>> +	struct workqueue_struct	*task_release_wq;
> >>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
> >>> +	struct mutex		task_mutex;	/* for task */
> >>> +	spinlock_t		exec_lock;	/* for exec */
> >>> +	struct clk		*clock;
> >>> +	bool			suspended;
> >>> +};
> >>> +
> >>> +struct cmdq_subsys {
> >>> +	u32	base;
> >>> +	int	id;
> >>> +};
> >>> +
> >>> +static const struct cmdq_subsys gce_subsys[] = {
> >>> +	{0x1400, 1},
> >>> +	{0x1401, 2},
> >>> +	{0x1402, 3},
> >>> +};
> >>> +
> >>> +static int cmdq_subsys_base_to_id(u32 base)
> >>> +{
> >>> +	int i;
> >>> +
> >>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
> >>> +		if (gce_subsys[i].base == base)
> >>> +			return gce_subsys[i].id;
> >>> +	return -EFAULT;
> >>> +}
> >>> +
> >>> +static int cmdq_eng_get_thread(u64 flag)
> >>> +{
> >>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> >>> +		return CMDQ_THR_DISP_MAIN_IDX;
> >>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> >>> +		return CMDQ_THR_DISP_SUB_IDX;
> >>> +	else
> >>> +		return CMDQ_THR_DISP_MISC_IDX;
> >>> +}
> >>> +
> >>> +static void cmdq_task_release(struct cmdq_task *task)
> >>> +{
> >>> +	struct cmdq *cmdq = task->cmdq;
> >>> +
> >>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
> >>> +			  task->pa_base);
> >>> +	kfree(task);
> >>> +}
> >>> +
> >>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
> >>> +					   struct cmdq_task_cb cb)
> >>> +{
> >>> +	struct cmdq *cmdq = rec->cmdq;
> >>> +	struct device *dev = cmdq->dev;
> >>> +	struct cmdq_task *task;
> >>> +
> >>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
> >>> +	INIT_LIST_HEAD(&task->list_entry);
> >>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
> >>> +					   &task->pa_base, GFP_KERNEL);
> >>> +	if (!task->va_base) {
> >>> +		dev_err(dev, "allocate command buffer failed\n");
> >>> +		kfree(task);
> >>> +		return NULL;
> >>> +	}
> >>> +
> >>> +	task->cmdq = cmdq;
> >>> +	task->command_size = rec->command_size;
> >>> +	task->engine_flag = rec->engine_flag;
> >>> +	task->task_state = TASK_STATE_BUSY;
> >>> +	task->cb = cb;
> >>> +	memcpy(task->va_base, rec->buf, rec->command_size);
> >>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
> >>> +	return task;
> >>> +}
> >>> +
> >>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
> >>> +			       u32 offset)
> >>> +{
> >>> +	writel(value, thread->base + offset);
> >>> +}
> >>> +
> >>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
> >>> +{
> >>> +	return readl(thread->base + offset);
> >>> +}
> >>
> >> We can get rid of cmdq_thread_readl/writel.
> >
> > Will do.
> >
> >>> +
> >>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>> +{
> >>> +	u32 status;
> >>> +
> >>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
> >>> +
> >>> +	/* If already disabled, treat as suspended successful. */
> >>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>> +	      CMDQ_THR_ENABLED))
> >>> +		return 0;
> >>> +
> >>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
> >>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
> >>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
> >>> +			(u32)(thread->base - cmdq->base));
> >>> +		return -EFAULT;
> >>> +	}
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
> >>> +{
> >>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
> >>> +}
> >>> +
> >>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>> +{
> >>> +	u32 warm_reset;
> >>> +
> >>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
> >>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
> >>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
> >>> +			0, 10)) {
> >>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
> >>> +			(u32)(thread->base - cmdq->base));
> >>> +		return -EFAULT;
> >>> +	}
> >>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>> +{
> >>> +	cmdq_thread_reset(cmdq, thread);
> >>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
> >>> +}
> >>> +
> >>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
> >>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
> >>> +{
> >>> +	cmdq_thread_writel(thread,
> >>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
> >>> +			   CMDQ_THR_CURR_ADDR);
> >>> +}
> >>> +
> >>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
> >>> +{
> >>> +	struct cmdq_thread *thread = task->thread;
> >>> +	struct cmdq_task *prev_task = list_last_entry(
> >>> +			&thread->task_busy_list, typeof(*task), list_entry);
> >>> +	u64 *prev_task_base = prev_task->va_base;
> >>> +
> >>> +	/* let previous task jump to this task */
> >>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
> >>> +						 task->pa_base;
> >>> +
> >>> +	cmdq_thread_invalidate_fetched_data(thread);
> >>> +}
> >>> +
> >>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
> >>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
> >>> +{
> >>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
> >>> +	u32 *base = task->va_base;
> >>> +	u32 num_cmd = task->num_cmd << 1;
> >>> +	int i;
> >>> +
> >>> +	for (i = 0; i < num_cmd; i += 2)
> >>> +		if (base[i] == wfe_option &&
> >>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
> >>> +			base[i] = CMDQ_JUMP_PASS;
> >>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
> >>> +		}
> >>
> >> After using the command buffer as a void pointer a u64 pointer, we now
> >> reference to it as u32. I would prefer to explain here, how the command
> >> looks like we are searching for and use a for loop passing task->num_cmd
> >> instead.
> >
> > Will use u64* to rewrite the above code.
> >
> >>> +}
> >>> +
> >>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
> >>> +{
> >>> +	struct cmdq *cmdq = task->cmdq;
> >>> +	unsigned long flags;
> >>> +	unsigned long curr_pa, end_pa;
> >>> +
> >>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
> >>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>
> >> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
> >> the spin_lock here? Can't we just use one of the two?
> >
> > We can drop task_mutex, but we will get some side effects.
> > 1. exec_lock needs to include more code, but I think it is not good for
> >     spinlock.
> > 2. In cmdq_rec_flush_async(), task_mutex needs to protect
> >     (1) cmdq->suspended, (2) cmdq_task_exec(), and
> >     (3) cmdq_task_wait_release_schedule().
> >     If we drop task_mutex, we have to put cmdq->suspended if condition
> >     just before cmdq_task_exec() and inside exec_lock, and we have to
> >     release task and its command buffer if error. This will let flow
> >     become more complex and enlarge code size.
> >
> > What do you think?
> 
> Why do you need to protect cmdq_task_wait_release_schedule? We don't 
> care about the order of the workqueue elements, do we?

According to CK's comment, we have to ensure order to remove previous
task.
http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html

> As far as I understand you would need to protect cmdq_task_acquire as 
> well, to "ensure" continously growing pa_base. More on that below.

We need to ensure continuous physical addresses in a task, but we don't
need to ensure continuous physical addresses between tasks.
So, I think it is unnecessary to protect by mutex or spinlock.

> >
> >>> +	task->thread = thread;
> >>> +	task->task_state = TASK_STATE_BUSY;
> >>
> >> That was already set in cmdq_task_acquire, why do we need to set it here
> >> again?
> >
> > Will drop it.
> >
> 
> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire 
> instead.

Will drop it in cmdq_task_acquire instead.

> >>> +	if (list_empty(&thread->task_busy_list)) {
> >>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
> >>> +
> >>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
> >>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>> +				   CMDQ_THR_END_ADDR);
> >>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
> >>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
> >>> +				   CMDQ_THR_IRQ_ENABLE);
> >>> +
> >>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
> >>> +				   CMDQ_THR_ENABLE_TASK);
> >>> +	} else {
> >>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>> +
> >>> +		/*
> >>> +		 * check boundary condition
> >>> +		 * PC = END - 8, EOC is executed
> >>> +		 * PC = END, all CMDs are executed
> >>> +		 */
> >>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
> >>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
> >>
> >> 8 refers to CMDQ_INST_SIZE, right?
> >
> > Yes, I will use CMDQ_INST_SIZE.
> >
> >>> +			/* set to this task directly */
> >>> +			cmdq_thread_writel(thread, task->pa_base,
> >>> +					   CMDQ_THR_CURR_ADDR);
> >>> +		} else {
> >>> +			cmdq_task_insert_into_thread(task);
> >>> +
> >>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
> >>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
> >>> +				cmdq_task_remove_wfe(task);
> >>
> >> We could do this check using the task->engine_flag, I suppose that's
> >> easier to undestand then.
> >
> > Will use task->engine_flag.
> >
> >>> +
> >>> +			smp_mb(); /* modify jump before enable thread */
> >>> +		}
> >>> +
> >>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>> +				   CMDQ_THR_END_ADDR);
> >>> +		cmdq_thread_resume(thread);
> >>> +	}
> >>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>> +}
> >>> +
> >>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>> +				   struct cmdq_thread *thread, u32 irq_flag)
> >>> +{
> >>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>> +	u32 curr_pa;
> >>> +	struct cmdq_cb_data cmdq_cb_data;
> >>> +	bool err;
> >>> +
> >>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>> +		err = true;
> >>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>> +		err = false;
> >>> +	else
> >>> +		return;
> >>> +
> >>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>> +
> >>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>> +				 list_entry) {
> >>> +		if (curr_pa >= task->pa_base &&
> >>> +		    curr_pa < (task->pa_base + task->command_size))
> >>
> >> What are you checking here? It seems as if you make some implcit
> >> assumptions about pa_base and the order of execution of commands in the
> >> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
> >> about dma_handle?
> >
> > 1. Check what is the current running task in this GCE thread.
> > 2. Yes.
> > 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >
> 
> Yes, physical addresses might be continous, but AFAIK there is no 
> guarantee that the dma_handle address is steadily growing, when calling 
> dma_alloc_coherent. And if I understand the code correctly, you use this 
> assumption to decide if the task picked from task_busy_list is currently 
> executing. So I think this mecanism is not working.

I don't use dma_handle address, and just use physical addresses.
>From CPU's point of view, tasks are linked by the busy list.
>From GCE's point of view, tasks are linked by the JUMP command.

> In which cases does the HW thread raise an interrupt.
> In case of error. When does CMDQ_THR_IRQ_DONE get raised?

GCE will raise interrupt if any task is done or error.
However, GCE is fast, so CPU may get multiple done tasks
when it is running ISR.

In case of error, that GCE thread will pause and raise interrupt.
So, CPU may get multiple done tasks and one error task.

> >>> +			curr_task = task;
> >>> +		if (task->cb.cb) {
> >>> +			cmdq_cb_data.err = curr_task ? err : false;
> >>> +			cmdq_cb_data.data = task->cb.data;
> >>> +			task->cb.cb(cmdq_cb_data);
> >>> +		}
> >>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
> >>> +				TASK_STATE_DONE;
> >>> +		list_del(&task->list_entry);
> >>> +		if (curr_task)
> >>> +			break;
> >>> +	}
> >>> +
> >>> +	wake_up(&thread->wait_task_done);
> >>> +}
> >>> +
> >>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
> >>> +{
> >>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
> >>> +	unsigned long flags = 0L;
> >>> +	u32 irq_flag;
> >>> +
> >>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>> +
> >>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>> +
> >>> +	/*
> >>> +	 * Another CPU core could run "release task" right before we acquire
> >>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
> >>> +	 * need to check the enable bit of this GCE thread.
> >>> +	 */
> >>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>> +	      CMDQ_THR_ENABLED))
> >>> +		irq_flag = 0;
> >>
> >> cmdq_handle_error_done just retuns in this case. Programming this way
> >> just makes things confusing. What about:
> >>
> >> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >> 	      CMDQ_THR_ENABLED)
> >> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >> else
> >> 		irq_flag = 0;
> >>
> >> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >
> > We still need to clear irq_flag if GCE thread is disabled.
> > So, I think we can just return here.
> >
> > 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> > 	      CMDQ_THR_ENABLED))
> > 		return;
> >
> > What do you think?
> >
> 
> No, you can't just return, you need to unlock the spinlock.
> Anyway I would prefer it the other way round, as I put it in my last 
> mail. Just delete the else branch, we don't need to set irq_flag to zero.

Sorry for my previous wrong reply since I merge your comment
and CK's comment.
http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
So, I will put this if condition into cmdq_task_handle_error_result()
and then just return it if GCE thread is disabled.

> >>> +
> >>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>> +}
> >>> +
> >>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
> >>> +{
> >>> +	struct cmdq *cmdq = dev;
> >>> +	u32 irq_status;
> >>> +	int i;
> >>> +
> >>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
> >>> +	irq_status &= CMDQ_IRQ_MASK;
> >>> +	irq_status ^= CMDQ_IRQ_MASK;
> >>
> >> irq_status can be much bigger then 3, which is the number of threads in
> >> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
> >> clear to me.
> >
> > Our GCE hardware has 16 threads, but we only use 3 threads currently.
> >
> 
> Ok, but please use bitops here.

Will use bitops.

> >>> +
> >>> +	if (!irq_status)
> >>> +		return IRQ_NONE;
> >>> +
> >>> +	while (irq_status) {
> >>> +		i = ffs(irq_status) - 1;
> >>> +		irq_status &= ~BIT(i);
> >>> +		cmdq_thread_irq_handler(cmdq, i);
> >>> +	}
> >>
> >> Can you explain how the irq status register looks like, that would it
> >> make much easier to understand what happens here.
> >
> > Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
> > interrupt. 0 means asserting interrupt; 1 means no interrupt.
> >
> 
> Thanks, that helped. :)
> 
> >>> +
> >>> +	return IRQ_HANDLED;	
> >>> +}
> >>> +
> >>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
> >>
> >> We never check the return values, why do we have them?
> >
> > Will drop return value.
> >
> >>> +{
> >>> +	struct cmdq *cmdq = task->cmdq;
> >>> +	struct device *dev = cmdq->dev;
> >>> +	struct cmdq_thread *thread = task->thread;
> >>> +	struct cmdq_task *next_task, *prev_task;
> >>> +	u32 irq_flag;
> >>> +
> >>> +	/* suspend GCE thread to ensure consistency */
> >>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>> +
> >>> +	/* ISR has handled this error task */
> >>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
> >>> +				struct cmdq_task, list_entry);
> >>> +		if (next_task) /* move to next task */
> >>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>> +					   CMDQ_THR_CURR_ADDR);
> >>
> >> We have to do this, as we suppose that the thread did not reach the jump
> >> instruction we put into it's command queue, right?
> >
> > Yes.
> >
> 
> So this should then go into it's own function. In wait_release_work, 
> something like this:
> 
> if(task->task_state == TASK_STATE_ERROR)
>       cmdq_task_handle_error(task)

OK.
I will write new function cmdq_task_handle_error() to handle error case.

> >>> +		cmdq_thread_resume(thread);
> >>> +		return -ECANCELED;
> >>> +	}
> >>> +
> >>
> >> if task_state != ERROR and != DONE. This means that the timeout of
> >> task_release_wq has timed out, right?
> >
> > Yes.
> >
> >>> +	/*
> >>> +	 * Save next_task and prev_task in advance
> >>> +	 * since cmdq_handle_error_done will remove list_entry.
> >>> +	 */
> >>> +	next_task = prev_task = NULL;
> >>> +	if (task->list_entry.next != &thread->task_busy_list)
> >>> +		next_task = list_next_entry(task, list_entry);
> >>> +	if (task->list_entry.prev != &thread->task_busy_list)
> >>> +		prev_task = list_prev_entry(task, list_entry);
> >>> +
> >>> +	/*
> >>> +	 * Although IRQ is disabled, GCE continues to execute.
> >>> +	 * It may have pending IRQ before GCE thread is suspended,
> >>> +	 * so check this condition again.
> >>> +	 */
> >>
> >> The first thing we did in this function was suspending the thread. Why
> >> do we need this then?
> >
> > Because timeout is CPU timeout not GCE timeout, GCE could just finish
> > this task before the GCE thread is suspended.
> >
> 
> What are the reasons for a timeout? An error has happend, or the task is 
> still executing.

>From GCE's point of view, this task is still executing.
But, it could be an error of client.
For example, task may never get event if display turn off hardware
before waiting for task to finish its work.

> >> To be honest this whole functions looks really like a design error. We
> >> have to sperate the states much clearer so that it is possible to
> >> understand what is happening in the GCE. Isn't it for example posible to
> >> have worker queues for timed out tasks and tasks with an error? I'm not
> >> sure how to do this, actually I'm not sure if I really understood how
> >> this is supposed to work.
> >
> > GCE doesn't have timeout. The timeout is decided and controlled by CPU,
> > so we check timeout in release work.
> > For error and done, they are easy to check by register, and we have
> > already created release work for timeout. So, I don't think we need to
> > create work queue for each case.
> >
> > What do you think?
> >
> 
> I think, if we find in here, that the irq_flag is set, then the the 
> interrupt handler was triggered and is spinning the spinlock. If this is 
> not the case, we have a timeout and we handle this.

I will write another function to handle error, and handle timeout here.

> >> How much do we win, when we patch the thread command queue for every
> >> task we add, instead of just taking one task after another from the
> >> task_busy_list?
> >
> > GCE is used to help read/write registers with critical time limitation.
> > Sometimes, client may ask to process multiple tasks in a short period
> > of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
> > shouldn't limit to process one task after another from the
> > task_busy_list. Currently, when interrupt or timeout, we will check
> > how many tasks are done, and which one is error or timeout.
> >
> 
> So I suppose the device driver who use this are interested in throughput 
> and not in latency. The callback of every
> 
> >>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>> +
> >>> +	if (task->task_state == TASK_STATE_DONE) {
> >>> +		cmdq_thread_resume(thread);
> >>> +		return 0;
> >>> +	}
> >>> +
> >>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>> +		dev_err(dev, "task 0x%p error\n", task);
> >>> +		if (next_task) /* move to next task */
> >>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>> +					   CMDQ_THR_CURR_ADDR);
> >>> +		cmdq_thread_resume(thread);
> >>> +		return -ECANCELED;
> >>> +	}
> >>> +
> >>> +	/* Task is running, so we force to remove it. */
> >>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
> >>> +	task->task_state = TASK_STATE_ERROR;
> >>> +
> >>> +	if (prev_task) {
> >>> +		u64 *prev_va = prev_task->va_base;
> >>> +		u64 *curr_va = task->va_base;
> >>> +
> >>> +		/* copy JUMP instruction */
> >>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
> >>> +
> >>> +		cmdq_thread_invalidate_fetched_data(thread);
> >>> +	} else if (next_task) { /* move to next task */
> >>> +		cmdq_thread_writel(thread, next_task->pa_base,
> >>> +				   CMDQ_THR_CURR_ADDR);
> >>> +	}
> >>> +
> >>> +	list_del(&task->list_entry);
> >>> +	cmdq_thread_resume(thread);
> >>> +
> >>> +	/* call cb here to prevent lock */
> >>> +	if (task->cb.cb) {
> >>> +		struct cmdq_cb_data cmdq_cb_data;
> >>> +
> >>> +		cmdq_cb_data.err = true;
> >>> +		cmdq_cb_data.data = task->cb.data;
> >>> +		task->cb.cb(cmdq_cb_data);
> >>> +	}
> >>> +
> >>> +	return -ECANCELED;
> >>> +}
> >>> +
> >>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
> >>> +{
> >>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
> >>> +					      release_work);
> >>> +	struct cmdq *cmdq = task->cmdq;
> >>> +	struct cmdq_thread *thread = task->thread;
> >>> +	unsigned long flags;
> >>> +
> >>> +	wait_event_timeout(thread->wait_task_done,
> >>> +			   task->task_state != TASK_STATE_BUSY,
> >>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
> >>> +
> >>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>> +	if (task->task_state != TASK_STATE_DONE)
> >>> +		cmdq_task_handle_error_result(task);
> >>> +	if (list_empty(&thread->task_busy_list))
> >>> +		cmdq_thread_disable(cmdq, thread);
> >>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>> +
> >>> +	/* release regardless of success or not */
> >>> +	clk_disable_unprepare(cmdq->clock);
> >>> +	cmdq_task_release(task);
> >>> +}
> >>> +
> >>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
> >>> +{
> >>> +	struct cmdq *cmdq = task->cmdq;
> >>> +
> >>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
> >>> +	queue_work(cmdq->task_release_wq, &task->release_work);
> >>> +}
> >>> +
> >>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
> >>> +{
> >>> +	void *new_buf;
> >>> +
> >>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
> >>> +	if (!new_buf)
> >>> +		return -ENOMEM;
> >>> +	rec->buf = new_buf;
> >>> +	rec->buf_size = size;
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +struct cmdq_base *cmdq_register_device(struct device *dev)
> >>> +{
> >>> +	struct cmdq_base *cmdq_base;
> >>> +	struct resource res;
> >>> +	int subsys;
> >>> +	u32 base;
> >>> +
> >>> +	if (of_address_to_resource(dev->of_node, 0, &res))
> >>> +		return NULL;
> >>> +	base = (u32)res.start;
> >>> +
> >>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
> >>> +	if (subsys < 0)
> >>> +		return NULL;
> >>> +
> >>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
> >>> +	if (!cmdq_base)
> >>> +		return NULL;
> >>> +	cmdq_base->subsys = subsys;
> >>> +	cmdq_base->base = base;
> >>> +
> >>> +	return cmdq_base;
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_register_device);
> >>> +
> >>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>> +		    struct cmdq_rec **rec_ptr)
> >>> +{
> >>> +	struct cmdq_rec *rec;
> >>> +	int err;
> >>> +
> >>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
> >>> +	if (!rec)
> >>> +		return -ENOMEM;
> >>> +	rec->cmdq = dev_get_drvdata(dev);
> >>> +	rec->engine_flag = engine_flag;
> >>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
> >>
> >> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
> >
> > Will do.
> >
> >>> +	if (err < 0) {
> >>> +		kfree(rec);
> >>> +		return err;
> >>> +	}
> >>> +	*rec_ptr = rec;
> >>> +	return 0;
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_create);
> >>> +
> >>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
> >>> +				   u32 arg_a, u32 arg_b)
> >>> +{
> >>> +	u64 *cmd_ptr;
> >>> +	int err;
> >>> +
> >>> +	if (WARN_ON(rec->finalized))
> >>> +		return -EBUSY;
> >>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
> >>> +		return -EINVAL;
> >>
> >> cmdq_rec_append_command is just called from inside this driver and code
> >> is a enum. We can expect it to be correct, no need for this check.
> >
> > Will drop this check.
> >
> >>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
> >>
> >> command_size is the offset into the buffer to which a new command is
> >> written, so this name is highly confusing. I wonder if this would be
> >> easier to understand if we redefine command_size to something like the
> >> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
> >
> > I can rename command_size to cmd_buf_size and calculate num_cmd by
> > dividing CMDQ_INST_SIZE.
> > What do you think?
> >
> >>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
> >>> +		if (err < 0)
> >>> +			return err;
> >>> +	}
> >>> +	cmd_ptr = rec->buf + rec->command_size;
> >>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
> >>> +	rec->command_size += CMDQ_INST_SIZE;
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
> >>> +		   u32 offset)
> >>> +{
> >>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
> >>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
> >>
> >> base->subsys is the id from gce_sybsys, so we can expect it to be
> >> correct, no need to mask with CMDQ_SUBSYS_MASK.
> >
> > Will drop it.
> >
> >>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_write);
> >>> +
> >>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>> +			struct cmdq_base *base, u32 offset, u32 mask)
> >>> +{
> >>> +	u32 offset_mask = offset;
> >>> +	int err;
> >>> +
> >>> +	if (mask != 0xffffffff) {
> >>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
> >>> +		if (err < 0)
> >>> +			return err;
> >>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> >>> +	}
> >>> +	return cmdq_rec_write(rec, value, base, offset_mask);
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
> >>> +
> >>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> >>> +{
> >>> +	u32 arg_b;
> >>> +
> >>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>> +		return -EINVAL;
> >>> +
> >>> +	/*
> >>> +	 * bit 0-11: wait value
> >>> +	 * bit 15: 1 - wait, 0 - no wait
> >>> +	 * bit 16-27: update value
> >>> +	 * bit 31: 1 - update, 0 - no update
> >>> +	 */
> >>
> >> I don't understand this comments. What are they for?
> >
> > This is for WFE command. I will comment it.
> >
> >>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_wfe);
> >>> +
> >>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
> >>> +{
> >>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>> +		return -EINVAL;
> >>> +
> >>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
> >>> +				       CMDQ_WFE_UPDATE);
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
> >>> +
> >>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
> >>> +{
> >>> +	int err;
> >>> +
> >>> +	if (rec->finalized)
> >>> +		return 0;
> >>> +
> >>> +	/* insert EOC and generate IRQ for each command iteration */
> >>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
> >>> +	if (err < 0)
> >>> +		return err;
> >>> +
> >>> +	/* JUMP to end */
> >>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
> >>> +	if (err < 0)
> >>> +		return err;
> >>> +
> >>
> >> Does this need to be atomic?
> >> What happens if after CODE_EOC and before CODE_JUMP some
> >> write/read/event gets added?
> >> What happens if more commands get added to the queue after CODE_JUMP,
> >> but before finalized is set to true. Why don't you use atomic functions
> >> to access finalized?
> >
> > Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
> > client uses cmdq_rec.
> >
> 
> Well I think that rec->finalized tries to implement this, but might 
> fail, if two kernel threads work on the same cmdq_rec.
> 
> >>> +	rec->finalized = true;
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>> +			 void *data)
> >>> +{
> >>> +	struct cmdq *cmdq = rec->cmdq;
> >>> +	struct cmdq_task *task;
> >>> +	struct cmdq_task_cb task_cb;
> >>> +	struct cmdq_thread *thread;
> >>> +	int err;
> >>> +
> >>> +	mutex_lock(&cmdq->task_mutex);
> >>> +	if (cmdq->suspended) {
> >>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
> >>> +		mutex_unlock(&cmdq->task_mutex);
> >>> +		return -EPERM;
> >>> +	}
> >>> +
> >>> +	err = cmdq_rec_finalize(rec);
> >>> +	if (err < 0) {
> >>> +		mutex_unlock(&cmdq->task_mutex);
> >>> +		return err;
> >>> +	}
> >>> +
> >>> +	task_cb.cb = cb;
> >>> +	task_cb.data = data;
> >>> +	task = cmdq_task_acquire(rec, task_cb);
> >>> +	if (!task) {
> >>> +		mutex_unlock(&cmdq->task_mutex);
> >>> +		return -EFAULT;
> >>> +	}
> >>> +
> >>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
> >>> +	cmdq_task_exec(task, thread);
> >>> +	cmdq_task_wait_release_schedule(task);
> >>> +	mutex_unlock(&cmdq->task_mutex);
> >>> +	return 0;
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
> >>> +
> >>> +struct cmdq_flush_completion {
> >>> +	struct completion cmplt;
> >>> +	bool err;
> >>> +};
> >>> +
> >>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
> >>> +{
> >>> +	struct cmdq_flush_completion *cmplt = data.data;
> >>> +
> >>> +	cmplt->err = data.err;
> >>> +	complete(&cmplt->cmplt);
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +int cmdq_rec_flush(struct cmdq_rec *rec)
> >>> +{
> >>> +	struct cmdq_flush_completion cmplt;
> >>> +	int err;
> >>> +
> >>> +	init_completion(&cmplt.cmplt);
> >>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
> >>> +	if (err < 0)
> >>> +		return err;
> >>> +	wait_for_completion(&cmplt.cmplt);
> >>> +	return cmplt.err ? -EFAULT : 0;
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_flush);
> >>> +
> >>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
> >>> +{
> >>> +	kfree(rec->buf);
> >>> +	kfree(rec);
> >>> +}
> >>> +EXPORT_SYMBOL(cmdq_rec_destroy);
> >>> +
> >>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
> >>> +{
> >>> +	struct cmdq_thread *thread;
> >>> +	int i;
> >>> +
> >>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>> +		thread = &cmdq->thread[i];
> >>> +		if (!list_empty(&thread->task_busy_list))
> >>> +			return false;
> >>> +	}
> >>> +	return true;
> >>> +}
> >>> +
> >>> +static int cmdq_suspend(struct device *dev)
> >>> +{
> >>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>> +	u32 exec_threads;
> >>> +
> >>> +	mutex_lock(&cmdq->task_mutex);
> >>> +	cmdq->suspended = true;
> >>> +	mutex_unlock(&cmdq->task_mutex);
> >>> +
> >>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
> >>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
> >>> +		dev_err(dev, "wait active tasks timeout.\n");
> >>> +		flush_workqueue(cmdq->task_release_wq);
> >>> +	}
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int cmdq_resume(struct device *dev)
> >>> +{
> >>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>> +
> >>> +	cmdq->suspended = false;
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int cmdq_remove(struct platform_device *pdev)
> >>> +{
> >>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
> >>> +
> >>> +	destroy_workqueue(cmdq->task_release_wq);
> >>> +	cmdq->task_release_wq = NULL;
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int cmdq_probe(struct platform_device *pdev)
> >>> +{
> >>> +	struct device *dev = &pdev->dev;
> >>> +	struct device_node *node = dev->of_node;
> >>> +	struct resource *res;
> >>> +	struct cmdq *cmdq;
> >>> +	int err, i;
> >>> +
> >>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
> >>> +	if (!cmdq)
> >>> +		return -ENOMEM;
> >>> +	cmdq->dev = dev;
> >>> +
> >>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>> +	cmdq->base = devm_ioremap_resource(dev, res);
> >>> +	if (IS_ERR(cmdq->base)) {
> >>> +		dev_err(dev, "failed to ioremap gce\n");
> >>> +		return PTR_ERR(cmdq->base);
> >>> +	}
> >>> +
> >>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
> >>> +	if (!cmdq->irq) {
> >>> +		dev_err(dev, "failed to get irq\n");
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
> >>> +		dev, cmdq->base, cmdq->irq);
> >>> +
> >>> +	mutex_init(&cmdq->task_mutex);
> >>> +	spin_lock_init(&cmdq->exec_lock);
> >>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
> >>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
> >>> +			"cmdq_task_wait_release");
> >>> +
> >>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
> >>> +				CMDQ_THR_SHIFT * i;
> >>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
> >>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
> >>> +	}
> >>> +
> >>> +	platform_set_drvdata(pdev, cmdq);
> >>> +
> >>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
> >>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
> >>> +	if (err < 0) {
> >>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
> >>> +		goto fail;
> >>> +	}
> >>> +
> >>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
> >>> +	if (IS_ERR(cmdq->clock)) {
> >>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
> >>> +		err = PTR_ERR(cmdq->clock);
> >>> +		goto fail;
> >>> +	}
> >>> +	return 0;
> >>> +
> >>> +fail:
> >>> +	cmdq_remove(pdev);
> >>> +	return err;
> >>> +}
> >>> +
> >>> +static const struct dev_pm_ops cmdq_pm_ops = {
> >>> +	.suspend = cmdq_suspend,
> >>> +	.resume = cmdq_resume,
> >>> +};
> >>> +
> >>> +static const struct of_device_id cmdq_of_ids[] = {
> >>> +	{.compatible = "mediatek,mt8173-gce",},
> >>> +	{}
> >>> +};
> >>> +
> >>> +static struct platform_driver cmdq_drv = {
> >>> +	.probe = cmdq_probe,
> >>> +	.remove = cmdq_remove,
> >>> +	.driver = {
> >>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
> >>> +		.owner = THIS_MODULE,
> >>> +		.pm = &cmdq_pm_ops,
> >>> +		.of_match_table = cmdq_of_ids,
> >>> +	}
> >>> +};
> >>> +
> >>> +builtin_platform_driver(cmdq_drv);
> >>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
> >>> new file mode 100644
> >>> index 0000000..60eef3d
> >>> --- /dev/null
> >>> +++ b/include/soc/mediatek/cmdq.h
> >>> @@ -0,0 +1,197 @@
> >>> +/*
> >>> + * Copyright (c) 2015 MediaTek Inc.
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or modify
> >>> + * it under the terms of the GNU General Public License version 2 as
> >>> + * published by the Free Software Foundation.
> >>> + *
> >>> + * This program is distributed in the hope that it will be useful,
> >>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>> + * GNU General Public License for more details.
> >>> + */
> >>> +
> >>> +#ifndef __MTK_CMDQ_H__
> >>> +#define __MTK_CMDQ_H__
> >>> +
> >>> +#include <linux/platform_device.h>
> >>> +#include <linux/types.h>
> >>> +
> >>> +enum cmdq_eng {
> >>> +	CMDQ_ENG_DISP_AAL,
> >>> +	CMDQ_ENG_DISP_COLOR0,
> >>> +	CMDQ_ENG_DISP_COLOR1,
> >>> +	CMDQ_ENG_DISP_DPI0,
> >>> +	CMDQ_ENG_DISP_DSI0,
> >>> +	CMDQ_ENG_DISP_DSI1,
> >>> +	CMDQ_ENG_DISP_GAMMA,
> >>> +	CMDQ_ENG_DISP_OD,
> >>> +	CMDQ_ENG_DISP_OVL0,
> >>> +	CMDQ_ENG_DISP_OVL1,
> >>> +	CMDQ_ENG_DISP_PWM0,
> >>> +	CMDQ_ENG_DISP_PWM1,
> >>> +	CMDQ_ENG_DISP_RDMA0,
> >>> +	CMDQ_ENG_DISP_RDMA1,
> >>> +	CMDQ_ENG_DISP_RDMA2,
> >>> +	CMDQ_ENG_DISP_UFOE,
> >>> +	CMDQ_ENG_DISP_WDMA0,
> >>> +	CMDQ_ENG_DISP_WDMA1,
> >>> +	CMDQ_ENG_MAX,
> >>> +};
> >>> +
> >>> +/* events for CMDQ and display */
> >>> +enum cmdq_event {
> >>> +	/* Display start of frame(SOF) events */
> >>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> >>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> >>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> >>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> >>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> >>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> >>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> >>> +	/* Display end of frame(EOF) events */
> >>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> >>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> >>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> >>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> >>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> >>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> >>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> >>> +	/* Mutex end of frame(EOF) events */
> >>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> >>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> >>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> >>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> >>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> >>> +	/* Display underrun events */
> >>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> >>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> >>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> >>> +	/* Keep this at the end of HW events */
> >>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> >>> +};
> >>> +
> >>> +struct cmdq_cb_data {
> >>> +	bool	err;
> >>> +	void	*data;
> >>> +};
> >>> +
> >>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
> >>> +
> >>> +struct cmdq_task;
> >>> +struct cmdq;
> >>> +
> >>> +struct cmdq_rec {
> >>> +	struct cmdq		*cmdq;
> >>> +	u64			engine_flag;
> >>> +	size_t			command_size;
> >>> +	void			*buf;
> >>> +	size_t			buf_size;
> >>> +	bool			finalized;
> >>> +};
> 
> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that 
> and this way make the driver less complex?

There are two main reasons for cmdq_rec.
1. It is slow to access dma too frequently.
   So, we append commands to cacheable memory, and then flush to dma.
2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
   If we merge them, we need to take care of some synchronization
   issues.

> >>> +
> >>> +struct cmdq_base {
> >>> +	int	subsys;
> >>> +	u32	base;
> >>
> >> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
> >> so we can get rid of the struct, right?
> >
> > Current subsys method is based on previous comment from Daniel Kurtz.
> > Please take a look of our previous discussion.
> > http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> > Thanks.
> >
> 
> I have to look deeper into this, but from what I read, the proposal from 
> Daniel [1] seems good to me.
> 
> [1] https://patchwork.kernel.org/patch/8068311/

The initial proposal has some problem, so please see the follow-up
discussions. Thanks.
http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html


> >>> +};
> >>> +
> >>> +/**
> >>> + * cmdq_register_device() - register device which needs CMDQ
> >>> + * @dev:		device
> >>> + *
> >>> + * Return: cmdq_base pointer or NULL for failed
> >>> + */
> >>> +struct cmdq_base *cmdq_register_device(struct device *dev);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_create() - create command queue record
> >>> + * @dev:		device
> >>> + * @engine_flag:	command queue engine flag
> >>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + */
> >>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>> +		    struct cmdq_rec **rec_ptr);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_write() - append write command to the command queue record
> >>> + * @rec:	the command queue record
> >>> + * @value:	the specified target register value
> >>> + * @base:	the command queue base
> >>> + * @offset:	register offset from module base
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + */
> >>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
> >>> +		   struct cmdq_base *base, u32 offset);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_write_mask() - append write command with mask to the command
> >>> + *			   queue record
> >>> + * @rec:	the command queue record
> >>> + * @value:	the specified target register value
> >>> + * @base:	the command queue base
> >>> + * @offset:	register offset from module base
> >>> + * @mask:	the specified target register mask
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + */
> >>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>> +			struct cmdq_base *base, u32 offset, u32 mask);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
> >>
> >> reco rd -> record
> >
> > Will fix it.
> >
> >> Regards,
> >> Matthias
> >>
> >>> + * @rec:	the command queue record
> >>> + * @event:	the desired event type to "wait and CLEAR"
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + */
> >>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_clear_event() - append clear event command to the command queue
> >>> + *			    record
> >>> + * @rec:	the command queue record
> >>> + * @event:	the desired event to be cleared
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + */
> >>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
> >>> + * @rec:	the command queue record
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + *
> >>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
> >>> + * synchronous flush function. When the function returned, the recorded
> >>> + * commands have been done.
> >>> + */
> >>> +int cmdq_rec_flush(struct cmdq_rec *rec);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
> >>> + *			    commands and call back after ISR is finished
> >>> + * @rec:	the command queue record
> >>> + * @cb:		called in the end of CMDQ ISR
> >>> + * @data:	this data will pass back to cb
> >>> + *
> >>> + * Return: 0 for success; else the error code is returned
> >>> + *
> >>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
> >>> + * after ISR is finished. Note that this is an ASYNC function. When the function
> >>> + * returned, it may or may not be finished. The ISR callback function is called
> >>> + * in the end of ISR.
> 
> "The callback is called from the ISR."
> 
> Regards,
> Matthias
> 
> >>> + */
> >>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>> +			 void *data);
> >>> +
> >>> +/**
> >>> + * cmdq_rec_destroy() - destroy command queue record
> >>> + * @rec:	the command queue record
> >>> + */
> >>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
> >>> +
> >>> +#endif	/* __MTK_CMDQ_H__ */
> >>>
> >
> > Thanks,
> > HS
> >

Thanks,
HS



From pc.liao at mediatek.com  Wed Jun  1 05:00:12 2016
From: pc.liao at mediatek.com (PC Liao)
Date: Wed, 1 Jun 2016 20:00:12 +0800
Subject: [PATCH] ASoC: mediatek: add MCLK source selection
Message-ID: <1464782412-632-1-git-send-email-pc.liao@mediatek.com>

The new machine's MCLK source is from mt8173 which is dynamic from
sampling rate*256. This patch provides the selection for device tree.

Signed-off-by: PC Liao <pc.liao at mediatek.com>
---
 .../devicetree/bindings/sound/mt8173-rt5650.txt    |    5 +++
 sound/soc/mediatek/mt8173-rt5650.c                 |   45 +++++++++++++++++++-
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
index 5bfa6b6..f250fc7 100644
--- a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
+++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
@@ -12,12 +12,17 @@ Required codec-capture subnode properties:
   <&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
   <&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
 
+- mediatek,mclk: the MCLK source
+  0 : external oscillator, MCLK = 12.288M
+  1 : internal source from mt8173, MCLK = sampling rate*256
+
 Example:
 
 	sound {
 		compatible = "mediatek,mt8173-rt5650";
 		mediatek,audio-codec = <&rt5650>;
 		mediatek,platform = <&afe>;
+		mediatek,mclk = <0>;
 		codec-capture {
 			sound-dai = <&rt5650 1>;
 		};
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c
index a27a667..072934b 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173-rt5650.c
@@ -23,6 +23,20 @@
 
 #define MCLK_FOR_CODECS		12288000
 
+enum mt8173_rt5650_mclk {
+	MT8173_RT5650_MCLK_EXTERNAL = 0,
+	MT8173_RT5650_MCLK_INTERNAL,
+};
+
+struct mt8173_rt5650_platform_data {
+	enum mt8173_rt5650_mclk pll_from;
+	/* 0 = external oscillator; 1 = internal source from mt8173 */
+};
+
+static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
+	.pll_from = MT8173_RT5650_MCLK_EXTERNAL,
+};
+
 static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
 	SND_SOC_DAPM_SPK("Speaker", NULL),
 	SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -54,13 +68,29 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
 				   struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int mclk_clock;
 	int i, ret;
 
+	switch (mt8173_rt5650_priv.pll_from) {
+	case MT8173_RT5650_MCLK_EXTERNAL:
+		/* mclk = 12.288M */
+		mclk_clock = MCLK_FOR_CODECS;
+		break;
+	case MT8173_RT5650_MCLK_INTERNAL:
+		/* mclk = sampling rate*256 */
+		mclk_clock = params_rate(params) * 256;
+		break;
+	default:
+		/* mclk = 12.288M */
+		mclk_clock = MCLK_FOR_CODECS;
+		break;
+	}
+
 	for (i = 0; i < rtd->num_codecs; i++) {
 		struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
 
-		/* pll from mclk 12.288M */
-		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+		/* pll from mclk */
+		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
 					  params_rate(params) * 512);
 		if (ret)
 			return ret;
@@ -243,6 +273,17 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
 		mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
 	}
 
+	if (device_property_present(&pdev->dev, "mediatek,mclk")) {
+		ret = device_property_read_u32(&pdev->dev,
+					       "mediatek,mclk",
+					       &mt8173_rt5650_priv.pll_from);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s snd_soc_register_card fail %d\n",
+				__func__, ret);
+		}
+	}
+
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
 
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Wed Jun  1 05:04:17 2016
From: chunfeng.yun at mediatek.com (chunfeng yun)
Date: Wed, 1 Jun 2016 20:04:17 +0800
Subject: [PATCH v2,1/5] dt-bindings: mt8173-xhci: support host side of
 dual-role mode
In-Reply-To: <7828750b-f5ac-6fa0-ee6e-d4775c227e1a@cogentembedded.com>
References: <1464673962-11506-1-git-send-email-chunfeng.yun@mediatek.com>
 <1464673962-11506-2-git-send-email-chunfeng.yun@mediatek.com>
 <7828750b-f5ac-6fa0-ee6e-d4775c227e1a@cogentembedded.com>
Message-ID: <1464782657.11651.136.camel@mhfsdcap03>

Hi,

On Tue, 2016-05-31 at 14:07 +0300, Sergei Shtylyov wrote:
> Hello.
> 
> On 5/31/2016 8:52 AM, Chunfeng Yun wrote:
> 
> >
> > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> > ---
> >  .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++++++++++++++++++++
> >  1 file changed, 48 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
> > index b3a7ffa..542e3fa 100644
> > --- a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
> > +++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
> > @@ -2,6 +2,14 @@ MT8173 xHCI
> >
> >  The device node for Mediatek SOC USB3.0 host controller
[...]
> >  Required properties:
> >   - compatible : should contain "mediatek,mt8173-xhci"
> >   - reg : specifies physical base address and size of the registers,
> > @@ -27,6 +35,9 @@ Optional properties:
> >  	control register, it depends on "mediatek,wakeup-src".
> >   - vbus-supply : reference to the VBUS regulator;
> >   - usb3-lpm-capable : supports USB3.0 LPM
> > + - pinctl-names : a pinctrl state named "default" must be defined
> 
>     pinctrl-names.
> 
Thanks.

> > + - pinctrl-0 : pin control group
> > +	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
> >
> >  Example:
> >  usb30: usb at 11270000 {
> [...]
> 
> MBR, Sergei
> 




From p.zabel at pengutronix.de  Wed Jun  1 09:50:03 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Wed, 01 Jun 2016 18:50:03 +0200
Subject: [GIT PULL] drm/mediatek: MT8173 DSI and DPI fixes
Message-ID: <1464799803.3522.73.camel@pengutronix.de>

Hi Dave,

this tag contains two fixes, one each for an oversight in the DPI and
DSI driver, respectively.

regards
Philipp

The following changes since commit 1a695a905c18548062509178b98bc91e67510864:

  Linux 4.7-rc1 (2016-05-29 09:29:24 -0700)

are available in the git repository at:

  git://git.pengutronix.de/git/pza/linux.git tags/mediatek-drm-fixes-2016-06-01

for you to fetch changes up to 2ea9f31799adce35d8c9a0bb3bc1d2b515702c6f:

  drm/mediatek: mtk_dsi: Remove spurious drm_connector_unregister (2016-06-01 16:09:56 +0200)

----------------------------------------------------------------
mediatek-drm fixes

- remove an invalid, unreachable error message and NULL pointer dereference
- remove a spurious drm_connector_unregister call from the DSI driver

----------------------------------------------------------------
Philipp Zabel (2):
      drm/mediatek: mtk_dpi: remove invalid error message
      drm/mediatek: mtk_dsi: Remove spurious drm_connector_unregister

 drivers/gpu/drm/mediatek/mtk_dpi.c | 5 -----
 drivers/gpu/drm/mediatek/mtk_dsi.c | 4 +---
 2 files changed, 1 insertion(+), 8 deletions(-)


From gregkh at linuxfoundation.org  Wed Jun  1 10:55:42 2016
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Wed, 1 Jun 2016 10:55:42 -0700
Subject: [PATCH v3 0/2] nvmem: remove regmap dependency
In-Reply-To: <7eec3215-b455-6567-28a2-e2a860e1d3ec@i2se.com>
References: <1462214174-469-1-git-send-email-srinivas.kandagatla@linaro.org>
 <7eec3215-b455-6567-28a2-e2a860e1d3ec@i2se.com>
Message-ID: <20160601175542.GA19241@kroah.com>

On Wed, Jun 01, 2016 at 10:27:49AM +0200, Stefan Wahren wrote:
> Hi Greg,
> 
> Am 02.05.2016 um 20:36 schrieb Srinivas Kandagatla:
> > Hi Greg, 
> >
> > This is v3 patchset for the leftover 2 patches for nvmem regmap
> > removal series [1]. These patches are based on char-misc tree.
> >
> > nvmem uses regmap_raw_read/write apis to read/write data from providers,
> > With recent patch 922a9f936e40 ("regmap: mmio: Convert to regmap_bus
> > and fix accessor usage") nvmem providers based on regmap-mmio stopped
> > working, as nvmem core was using raw accessors.
> > This issue can be fixed temporarly by moving to other regmap apis,
> > but we might hit same issue in future, and regmap looks like an
> > overdo for nvmem. Moving to interfaces based on read/write callbacks
> > from providers would be more robust.
> >
> > This patchset converts the nvmem core and nvmem provider drivers to
> > use the new callbacks. Tested this patchset on qfprom and at24 drivers.
> > Other driver are only compile tested, any testing on them would be great.
> >
> > Most of the patches have been applied to char-misc tree, these are the
> > two patches which had some outstanding comments on mxs nvmemprovider,
> > which are now fixed.
> >
> > Changes since v2:
> >  - Fixed the mxs size and dt data pointer spotted by Stefan and Fabio
> >
> > Changes since v1:
> >  - rebased mtk-efuse on top of char-misc
> >  - addressed concerns raised by Stefan Wahren.
> >
> > [1] https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1130026.html
> >
> > Thanks,
> > srini
> >
> > Srinivas Kandagatla (2):
> >   nvmem: mtk-efuse: remove nvmem regmap dependency
> >   nvmem: mxs-ocotp: remove nvmem regmap dependency
> >
> >  drivers/nvmem/Kconfig     |  1 -
> >  drivers/nvmem/mtk-efuse.c | 47 ++++++++++++++++++---------
> >  drivers/nvmem/mxs-ocotp.c | 83 +++++++++++++----------------------------------
> >  3 files changed, 54 insertions(+), 77 deletions(-)
> >
> 
> any objections about this series or is it still in queue?

still in my queue...


From matthias.bgg at gmail.com  Thu Jun  2 01:46:28 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Thu, 2 Jun 2016 10:46:28 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464775020.11122.40.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
Message-ID: <574FF264.7050209@gmail.com>



On 01/06/16 11:57, Horng-Shyang Liao wrote:
> Hi Mathias,
>
> Please see my inline reply.
>
> On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
>>
>> On 31/05/16 10:36, Horng-Shyang Liao wrote:
>>> Hi Mathias,
>>>
>>> Please see my inline reply.
>>>
>>> On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
>>>>
>>>> On 30/05/16 05:19, HS Liao wrote:
>>>>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
>>>>> CMDQ is used to help read/write registers with critical time limitation,
>>>>> such as updating display configuration during the vblank. It controls
>>>>> Global Command Engine (GCE) hardware to achieve this requirement.
>>>>> Currently, CMDQ only supports display related hardwares, but we expect
>>>>> it can be extended to other hardwares for future requirements.
>>>>>
>>>>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
>>>>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
>>>>> ---
>>>>>     drivers/soc/mediatek/Kconfig    |  10 +
>>>>>     drivers/soc/mediatek/Makefile   |   1 +
>>>>>     drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
>>>>>     include/soc/mediatek/cmdq.h     | 197 +++++++++
>>>>>     4 files changed, 1151 insertions(+)
>>>>>     create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
>>>>>     create mode 100644 include/soc/mediatek/cmdq.h
>>>>>
>>>>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
>>>>> index 0a4ea80..c4ad75c 100644
>>>>> --- a/drivers/soc/mediatek/Kconfig
>>>>> +++ b/drivers/soc/mediatek/Kconfig
>>>>> @@ -1,6 +1,16 @@
>>>>>     #
>>>>>     # MediaTek SoC drivers
>>>>>     #
>>>>> +config MTK_CMDQ
>>>>> +	bool "MediaTek CMDQ Support"
>>>>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
>>
>> depends on ARM64 ?
>
> Will add ARM64.
>
>>>>> +	select MTK_INFRACFG
>>>>> +	help
>>>>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
>>>>> +	  driver. The CMDQ is used to help read/write registers with critical
>>>>> +	  time limitation, such as updating display configuration during the
>>>>> +	  vblank.
>>>>> +
>>>>>     config MTK_INFRACFG
>>>>>     	bool "MediaTek INFRACFG Support"
>>>>>     	depends on ARCH_MEDIATEK || COMPILE_TEST
>>>>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
>>>>> index 12998b0..f7397ef 100644
>>>>> --- a/drivers/soc/mediatek/Makefile
>>>>> +++ b/drivers/soc/mediatek/Makefile
>>>>> @@ -1,3 +1,4 @@
>>>>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
>>>>>     obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
>>>>>     obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
>>>>>     obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
>>>>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
>>>>> new file mode 100644
>>>>> index 0000000..e9d6e1c
>>>>> --- /dev/null
>>>>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
>>>>> @@ -0,0 +1,943 @@
>>>>> +/*
>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>> + * published by the Free Software Foundation.
>>>>> + *
>>>>> + * This program is distributed in the hope that it will be useful,
>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>> + * GNU General Public License for more details.
>>>>> + */
>>>>> +
>>>>> +#include <linux/clk.h>
>>>>> +#include <linux/clk-provider.h>
>>>>> +#include <linux/completion.h>
>>>>> +#include <linux/dma-mapping.h>
>>>>> +#include <linux/errno.h>
>>>>> +#include <linux/interrupt.h>
>>>>> +#include <linux/iopoll.h>
>>>>> +#include <linux/kernel.h>
>>>>> +#include <linux/kthread.h>
>>>>> +#include <linux/module.h>
>>>>> +#include <linux/mutex.h>
>>>>> +#include <linux/of_address.h>
>>>>> +#include <linux/of_irq.h>
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/slab.h>
>>>>> +#include <linux/spinlock.h>
>>>>> +#include <linux/suspend.h>
>>>>> +#include <linux/workqueue.h>
>>>>> +#include <soc/mediatek/cmdq.h>
>>>>> +
>>>>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
>>>>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
>>>>> +#define CMDQ_TIMEOUT_MS			1000
>>>>> +#define CMDQ_IRQ_MASK			0xffff
>>>>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
>>>>> +#define CMDQ_CLK_NAME			"gce"
>>>>
>>>> We can put the names in directly to un-bloat the defines.
>>>
>>> I will use the names directly and remove defines.
>>>
>>>>> +
>>>>> +#define CMDQ_CURR_IRQ_STATUS		0x010
>>>>> +#define CMDQ_CURR_LOADED_THR		0x018
>>>>> +#define CMDQ_THR_SLOT_CYCLES		0x030
>>>>> +
>>>>> +#define CMDQ_THR_BASE			0x100
>>>>> +#define CMDQ_THR_SHIFT			0x080
>>>>
>>>> Wouldn't be CMDQ_THR_SIZE more accurate?
>>>
>>> Will rename it.
>>>
>>>>> +#define CMDQ_THR_WARM_RESET		0x00
>>>>> +#define CMDQ_THR_ENABLE_TASK		0x04
>>>>> +#define CMDQ_THR_SUSPEND_TASK		0x08
>>>>> +#define CMDQ_THR_CURR_STATUS		0x0c
>>>>> +#define CMDQ_THR_IRQ_STATUS		0x10
>>>>> +#define CMDQ_THR_IRQ_ENABLE		0x14
>>>>> +#define CMDQ_THR_CURR_ADDR		0x20
>>>>> +#define CMDQ_THR_END_ADDR		0x24
>>>>> +#define CMDQ_THR_CFG			0x40
>>>>> +
>>>>> +#define CMDQ_THR_ENABLED		0x1
>>>>> +#define CMDQ_THR_DISABLED		0x0
>>>>> +#define CMDQ_THR_SUSPEND		0x1
>>>>> +#define CMDQ_THR_RESUME			0x0
>>>>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
>>>>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
>>>>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
>>>>> +#define CMDQ_THR_PRIORITY		3
>>>>> +#define CMDQ_THR_IRQ_DONE		0x1
>>>>> +#define CMDQ_THR_IRQ_ERROR		0x12
>>>>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
>>>>
>>>> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
>>>
>>> Will do.
>>>
>>>>> +#define CMDQ_THR_IRQ_MASK		0x13
>>>>
>>>> never used.
>>>
>>> Will remove.
>>>
>>>>> +#define CMDQ_THR_EXECUTING		BIT(31)
>>>>> +
>>>>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
>>>>> +#define CMDQ_SUBSYS_MASK		0x1f
>>>>> +#define CMDQ_OP_CODE_MASK		0xff000000
>>>>> +
>>>>> +#define CMDQ_OP_CODE_SHIFT		24
>>>>
>>>> Couldn't we connect the mask with the shift, or aren't they related?
>>>>
>>>> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
>>>
>>> Will do.
>>>
>>>>> +#define CMDQ_SUBSYS_SHIFT		16
>>>>> +
>>>>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
>>>>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
>>>>> +#define CMDQ_JUMP_BY_PA			0x10000001
>>>>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
>>>>> +#define CMDQ_WFE_UPDATE			BIT(31)
>>>>> +#define CMDQ_WFE_WAIT			BIT(15)
>>>>> +#define CMDQ_WFE_WAIT_VALUE		0x1
>>>>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
>>>>> +
>>>>> +enum cmdq_thread_index {
>>>>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
>>>>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
>>>>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
>>>>> +	CMDQ_THR_MAX_COUNT,	/* max */
>>>>> +};
>>>>> +
>>>>> +/*
>>>>> + * CMDQ_CODE_MOVE:
>>>>> + *   move value into internal register as mask
>>>>> + *   format: op mask
>>>>> + * CMDQ_CODE_WRITE:
>>>>> + *   write value into target register
>>>>> + *   format: op subsys address value
>>>>> + * CMDQ_CODE_JUMP:
>>>>> + *   jump by offset
>>>>> + *   format: op offset
>>>>> + * CMDQ_CODE_WFE:
>>>>> + *   wait for event and clear
>>>>> + *   it is just clear if no wait
>>>>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
>>>>> + *           [clear] op event update:1 to_wait:0 wait:0
>>>>> + * CMDQ_CODE_EOC:
>>>>> + *   end of command
>>>>> + *   format: op irq_flag
>>>>> + */
>>>>
>>>> I think we need more documentation of how this command queue engine is
>>>> working. If not, I think it will be really complicated to understand how
>>>> to use this.
>>>>
>>>>> +enum cmdq_code {
>>>>> +	CMDQ_CODE_MOVE = 0x02,
>>>>> +	CMDQ_CODE_WRITE = 0x04,
>>>>> +	CMDQ_CODE_JUMP = 0x10,
>>>>> +	CMDQ_CODE_WFE = 0x20,
>>>>> +	CMDQ_CODE_EOC = 0x40,
>>>>> +};
>>>>> +
>>>>> +enum cmdq_task_state {
>>>>> +	TASK_STATE_BUSY, /* running on a GCE thread */
>>>>> +	TASK_STATE_ERROR,
>>>>> +	TASK_STATE_DONE,
>>>>> +};
>>>>> +
>>>>> +struct cmdq_task_cb {
>>>>> +	cmdq_async_flush_cb	cb;
>>>>> +	void			*data;
>>>>> +};
>>>>> +
>>>>> +struct cmdq_thread {
>>>>> +	void __iomem		*base;
>>>>> +	struct list_head	task_busy_list;
>>>>> +	wait_queue_head_t	wait_task_done;
>>>>> +};
>>>>> +
>>>>> +struct cmdq_task {
>>>>> +	struct cmdq		*cmdq;
>>>>> +	struct list_head	list_entry;
>>>>> +	enum cmdq_task_state	task_state;
>>>>> +	void			*va_base;
>>>>> +	dma_addr_t		pa_base;
>>>>> +	u64			engine_flag;
>>>>> +	size_t			command_size;
>>>>> +	u32			num_cmd;
>>>>
>>>> num_cmd is directly connected to command_size. I prefer to just keep
>>>> num_cmd and calculate the command_size where necessary.
>>>
>>> After I trace code, I prefer to keep command_size and calculate num_cmd
>>> where necessary. What do you think?
>>>
>>
>> I suppose you prefer this, as you are writing to the GCE depending on
>> the command_size. I think it is worth to create a macro for the
>> calculation of the number of commands, to make the code more readable.
>> Would be nice if you would just pass cmdq_task to it and it would return
>> the number. Just as an idea.
>
> Will add macro.
>
>>>>> +	struct cmdq_thread	*thread;
>>>>> +	struct cmdq_task_cb	cb;
>>>>> +	struct work_struct	release_work;
>>>>> +};
>>>>> +
>>>>> +struct cmdq {
>>>>> +	struct device		*dev;
>>>>> +	void __iomem		*base;
>>>>> +	u32			irq;
>>>>> +	struct workqueue_struct	*task_release_wq;
>>>>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
>>>>> +	struct mutex		task_mutex;	/* for task */
>>>>> +	spinlock_t		exec_lock;	/* for exec */
>>>>> +	struct clk		*clock;
>>>>> +	bool			suspended;
>>>>> +};
>>>>> +
>>>>> +struct cmdq_subsys {
>>>>> +	u32	base;
>>>>> +	int	id;
>>>>> +};
>>>>> +
>>>>> +static const struct cmdq_subsys gce_subsys[] = {
>>>>> +	{0x1400, 1},
>>>>> +	{0x1401, 2},
>>>>> +	{0x1402, 3},
>>>>> +};
>>>>> +
>>>>> +static int cmdq_subsys_base_to_id(u32 base)
>>>>> +{
>>>>> +	int i;
>>>>> +
>>>>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
>>>>> +		if (gce_subsys[i].base == base)
>>>>> +			return gce_subsys[i].id;
>>>>> +	return -EFAULT;
>>>>> +}
>>>>> +
>>>>> +static int cmdq_eng_get_thread(u64 flag)
>>>>> +{
>>>>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
>>>>> +		return CMDQ_THR_DISP_MAIN_IDX;
>>>>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
>>>>> +		return CMDQ_THR_DISP_SUB_IDX;
>>>>> +	else
>>>>> +		return CMDQ_THR_DISP_MISC_IDX;
>>>>> +}
>>>>> +
>>>>> +static void cmdq_task_release(struct cmdq_task *task)
>>>>> +{
>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>> +
>>>>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
>>>>> +			  task->pa_base);
>>>>> +	kfree(task);
>>>>> +}
>>>>> +
>>>>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
>>>>> +					   struct cmdq_task_cb cb)
>>>>> +{
>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>> +	struct device *dev = cmdq->dev;
>>>>> +	struct cmdq_task *task;
>>>>> +
>>>>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
>>>>> +	INIT_LIST_HEAD(&task->list_entry);
>>>>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
>>>>> +					   &task->pa_base, GFP_KERNEL);
>>>>> +	if (!task->va_base) {
>>>>> +		dev_err(dev, "allocate command buffer failed\n");
>>>>> +		kfree(task);
>>>>> +		return NULL;
>>>>> +	}
>>>>> +
>>>>> +	task->cmdq = cmdq;
>>>>> +	task->command_size = rec->command_size;
>>>>> +	task->engine_flag = rec->engine_flag;
>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>> +	task->cb = cb;
>>>>> +	memcpy(task->va_base, rec->buf, rec->command_size);
>>>>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
>>>>> +	return task;
>>>>> +}
>>>>> +
>>>>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
>>>>> +			       u32 offset)
>>>>> +{
>>>>> +	writel(value, thread->base + offset);
>>>>> +}
>>>>> +
>>>>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
>>>>> +{
>>>>> +	return readl(thread->base + offset);
>>>>> +}
>>>>
>>>> We can get rid of cmdq_thread_readl/writel.
>>>
>>> Will do.
>>>
>>>>> +
>>>>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>> +{
>>>>> +	u32 status;
>>>>> +
>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
>>>>> +
>>>>> +	/* If already disabled, treat as suspended successful. */
>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>> +	      CMDQ_THR_ENABLED))
>>>>> +		return 0;
>>>>> +
>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
>>>>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
>>>>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
>>>>> +			(u32)(thread->base - cmdq->base));
>>>>> +		return -EFAULT;
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
>>>>> +{
>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
>>>>> +}
>>>>> +
>>>>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>> +{
>>>>> +	u32 warm_reset;
>>>>> +
>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
>>>>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
>>>>> +			0, 10)) {
>>>>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
>>>>> +			(u32)(thread->base - cmdq->base));
>>>>> +		return -EFAULT;
>>>>> +	}
>>>>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>> +{
>>>>> +	cmdq_thread_reset(cmdq, thread);
>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
>>>>> +}
>>>>> +
>>>>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
>>>>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
>>>>> +{
>>>>> +	cmdq_thread_writel(thread,
>>>>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
>>>>> +			   CMDQ_THR_CURR_ADDR);
>>>>> +}
>>>>> +
>>>>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
>>>>> +{
>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>> +	struct cmdq_task *prev_task = list_last_entry(
>>>>> +			&thread->task_busy_list, typeof(*task), list_entry);
>>>>> +	u64 *prev_task_base = prev_task->va_base;
>>>>> +
>>>>> +	/* let previous task jump to this task */
>>>>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
>>>>> +						 task->pa_base;
>>>>> +
>>>>> +	cmdq_thread_invalidate_fetched_data(thread);
>>>>> +}
>>>>> +
>>>>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
>>>>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
>>>>> +{
>>>>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
>>>>> +	u32 *base = task->va_base;
>>>>> +	u32 num_cmd = task->num_cmd << 1;
>>>>> +	int i;
>>>>> +
>>>>> +	for (i = 0; i < num_cmd; i += 2)
>>>>> +		if (base[i] == wfe_option &&
>>>>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
>>>>> +			base[i] = CMDQ_JUMP_PASS;
>>>>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
>>>>> +		}
>>>>
>>>> After using the command buffer as a void pointer a u64 pointer, we now
>>>> reference to it as u32. I would prefer to explain here, how the command
>>>> looks like we are searching for and use a for loop passing task->num_cmd
>>>> instead.
>>>
>>> Will use u64* to rewrite the above code.
>>>
>>>>> +}
>>>>> +
>>>>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
>>>>> +{
>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>> +	unsigned long flags;
>>>>> +	unsigned long curr_pa, end_pa;
>>>>> +
>>>>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>
>>>> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
>>>> the spin_lock here? Can't we just use one of the two?
>>>
>>> We can drop task_mutex, but we will get some side effects.
>>> 1. exec_lock needs to include more code, but I think it is not good for
>>>      spinlock.
>>> 2. In cmdq_rec_flush_async(), task_mutex needs to protect
>>>      (1) cmdq->suspended, (2) cmdq_task_exec(), and
>>>      (3) cmdq_task_wait_release_schedule().
>>>      If we drop task_mutex, we have to put cmdq->suspended if condition
>>>      just before cmdq_task_exec() and inside exec_lock, and we have to
>>>      release task and its command buffer if error. This will let flow
>>>      become more complex and enlarge code size.
>>>
>>> What do you think?
>>
>> Why do you need to protect cmdq_task_wait_release_schedule? We don't
>> care about the order of the workqueue elements, do we?
>
> According to CK's comment, we have to ensure order to remove previous
> task.
> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>
>> As far as I understand you would need to protect cmdq_task_acquire as
>> well, to "ensure" continously growing pa_base. More on that below.
>
> We need to ensure continuous physical addresses in a task, but we don't
> need to ensure continuous physical addresses between tasks.
> So, I think it is unnecessary to protect by mutex or spinlock.
>

True, I didn't get that.

>>>
>>>>> +	task->thread = thread;
>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>
>>>> That was already set in cmdq_task_acquire, why do we need to set it here
>>>> again?
>>>
>>> Will drop it.
>>>
>>
>> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire
>> instead.
>
> Will drop it in cmdq_task_acquire instead.
>
>>>>> +	if (list_empty(&thread->task_busy_list)) {
>>>>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
>>>>> +
>>>>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>> +				   CMDQ_THR_END_ADDR);
>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
>>>>> +				   CMDQ_THR_IRQ_ENABLE);
>>>>> +
>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
>>>>> +				   CMDQ_THR_ENABLE_TASK);
>>>>> +	} else {
>>>>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>> +
>>>>> +		/*
>>>>> +		 * check boundary condition
>>>>> +		 * PC = END - 8, EOC is executed
>>>>> +		 * PC = END, all CMDs are executed
>>>>> +		 */
>>>>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
>>>>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
>>>>
>>>> 8 refers to CMDQ_INST_SIZE, right?
>>>
>>> Yes, I will use CMDQ_INST_SIZE.
>>>
>>>>> +			/* set to this task directly */
>>>>> +			cmdq_thread_writel(thread, task->pa_base,
>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>> +		} else {
>>>>> +			cmdq_task_insert_into_thread(task);
>>>>> +
>>>>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
>>>>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
>>>>> +				cmdq_task_remove_wfe(task);
>>>>
>>>> We could do this check using the task->engine_flag, I suppose that's
>>>> easier to undestand then.
>>>
>>> Will use task->engine_flag.
>>>
>>>>> +
>>>>> +			smp_mb(); /* modify jump before enable thread */
>>>>> +		}
>>>>> +
>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>> +				   CMDQ_THR_END_ADDR);
>>>>> +		cmdq_thread_resume(thread);
>>>>> +	}
>>>>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>> +}
>>>>> +
>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>> +				   struct cmdq_thread *thread, u32 irq_flag)
>>>>> +{
>>>>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>> +	u32 curr_pa;
>>>>> +	struct cmdq_cb_data cmdq_cb_data;
>>>>> +	bool err;
>>>>> +
>>>>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>> +		err = true;
>>>>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>> +		err = false;
>>>>> +	else
>>>>> +		return;
>>>>> +
>>>>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>> +
>>>>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>> +				 list_entry) {
>>>>> +		if (curr_pa >= task->pa_base &&
>>>>> +		    curr_pa < (task->pa_base + task->command_size))
>>>>
>>>> What are you checking here? It seems as if you make some implcit
>>>> assumptions about pa_base and the order of execution of commands in the
>>>> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
>>>> about dma_handle?
>>>
>>> 1. Check what is the current running task in this GCE thread.
>>> 2. Yes.
>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>
>>
>> Yes, physical addresses might be continous, but AFAIK there is no
>> guarantee that the dma_handle address is steadily growing, when calling
>> dma_alloc_coherent. And if I understand the code correctly, you use this
>> assumption to decide if the task picked from task_busy_list is currently
>> executing. So I think this mecanism is not working.
>
> I don't use dma_handle address, and just use physical addresses.
>  From CPU's point of view, tasks are linked by the busy list.
>  From GCE's point of view, tasks are linked by the JUMP command.
>
>> In which cases does the HW thread raise an interrupt.
>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>
> GCE will raise interrupt if any task is done or error.
> However, GCE is fast, so CPU may get multiple done tasks
> when it is running ISR.
>
> In case of error, that GCE thread will pause and raise interrupt.
> So, CPU may get multiple done tasks and one error task.
>

I think we should reimplement the ISR mechanism. Can't we just read 
CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave 
cmdq_handle_error_done to the thread_fn? You will need to pass 
information from the handler to thread_fn, but that shouldn't be an 
issue. AFAIK interrupts are disabled in the handler, so we should stay 
there as short as possible. Traversing task_busy_list is expensive, so 
we need to do it in a thread context.

I keep thinking about how to get rid of the two data structures, 
task_busy_list and the task_release_wq. We need the latter for the only 
sake of getting a timeout.

Did you have a look on how the mailbox framework handles this?
By the way, what is the reason to not implement the whole driver as a 
mailbox controller? For me, this driver looks like a good fit.


>>>>> +			curr_task = task;
>>>>> +		if (task->cb.cb) {
>>>>> +			cmdq_cb_data.err = curr_task ? err : false;
>>>>> +			cmdq_cb_data.data = task->cb.data;
>>>>> +			task->cb.cb(cmdq_cb_data);
>>>>> +		}
>>>>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
>>>>> +				TASK_STATE_DONE;
>>>>> +		list_del(&task->list_entry);
>>>>> +		if (curr_task)
>>>>> +			break;
>>>>> +	}
>>>>> +
>>>>> +	wake_up(&thread->wait_task_done);
>>>>> +}
>>>>> +
>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
>>>>> +{
>>>>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
>>>>> +	unsigned long flags = 0L;
>>>>> +	u32 irq_flag;
>>>>> +
>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>> +
>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>> +
>>>>> +	/*
>>>>> +	 * Another CPU core could run "release task" right before we acquire
>>>>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
>>>>> +	 * need to check the enable bit of this GCE thread.
>>>>> +	 */
>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>> +	      CMDQ_THR_ENABLED))
>>>>> +		irq_flag = 0;
>>>>
>>>> cmdq_handle_error_done just retuns in this case. Programming this way
>>>> just makes things confusing. What about:
>>>>
>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>> 	      CMDQ_THR_ENABLED)
>>>> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>> else
>>>> 		irq_flag = 0;
>>>>
>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>
>>> We still need to clear irq_flag if GCE thread is disabled.
>>> So, I think we can just return here.
>>>
>>> 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>> 	      CMDQ_THR_ENABLED))
>>> 		return;
>>>
>>> What do you think?
>>>
>>
>> No, you can't just return, you need to unlock the spinlock.
>> Anyway I would prefer it the other way round, as I put it in my last
>> mail. Just delete the else branch, we don't need to set irq_flag to zero.
>
> Sorry for my previous wrong reply since I merge your comment
> and CK's comment.
> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> So, I will put this if condition into cmdq_task_handle_error_result()
> and then just return it if GCE thread is disabled.
>

You mean in cmdq_task_handle_done
We should rename this functions to not create confusion.

Regards,
Matthias

>>>>> +
>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>> +}
>>>>> +
>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
>>>>> +{
>>>>> +	struct cmdq *cmdq = dev;
>>>>> +	u32 irq_status;
>>>>> +	int i;
>>>>> +
>>>>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
>>>>> +	irq_status &= CMDQ_IRQ_MASK;
>>>>> +	irq_status ^= CMDQ_IRQ_MASK;
>>>>
>>>> irq_status can be much bigger then 3, which is the number of threads in
>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
>>>> clear to me.
>>>
>>> Our GCE hardware has 16 threads, but we only use 3 threads currently.
>>>
>>
>> Ok, but please use bitops here.
>
> Will use bitops.
>
>>>>> +
>>>>> +	if (!irq_status)
>>>>> +		return IRQ_NONE;
>>>>> +
>>>>> +	while (irq_status) {
>>>>> +		i = ffs(irq_status) - 1;
>>>>> +		irq_status &= ~BIT(i);
>>>>> +		cmdq_thread_irq_handler(cmdq, i);
>>>>> +	}
>>>>
>>>> Can you explain how the irq status register looks like, that would it
>>>> make much easier to understand what happens here.
>>>
>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
>>>
>>
>> Thanks, that helped. :)
>>
>>>>> +
>>>>> +	return IRQ_HANDLED;	
>>>>> +}
>>>>> +
>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
>>>>
>>>> We never check the return values, why do we have them?
>>>
>>> Will drop return value.
>>>
>>>>> +{
>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>> +	struct device *dev = cmdq->dev;
>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>> +	struct cmdq_task *next_task, *prev_task;
>>>>> +	u32 irq_flag;
>>>>> +
>>>>> +	/* suspend GCE thread to ensure consistency */
>>>>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>> +
>>>>> +	/* ISR has handled this error task */
>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
>>>>> +				struct cmdq_task, list_entry);
>>>>> +		if (next_task) /* move to next task */
>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>
>>>> We have to do this, as we suppose that the thread did not reach the jump
>>>> instruction we put into it's command queue, right?
>>>
>>> Yes.
>>>
>>
>> So this should then go into it's own function. In wait_release_work,
>> something like this:
>>
>> if(task->task_state == TASK_STATE_ERROR)
>>        cmdq_task_handle_error(task)
>
> OK.
> I will write new function cmdq_task_handle_error() to handle error case.
>
>>>>> +		cmdq_thread_resume(thread);
>>>>> +		return -ECANCELED;
>>>>> +	}
>>>>> +
>>>>
>>>> if task_state != ERROR and != DONE. This means that the timeout of
>>>> task_release_wq has timed out, right?
>>>
>>> Yes.
>>>
>>>>> +	/*
>>>>> +	 * Save next_task and prev_task in advance
>>>>> +	 * since cmdq_handle_error_done will remove list_entry.
>>>>> +	 */
>>>>> +	next_task = prev_task = NULL;
>>>>> +	if (task->list_entry.next != &thread->task_busy_list)
>>>>> +		next_task = list_next_entry(task, list_entry);
>>>>> +	if (task->list_entry.prev != &thread->task_busy_list)
>>>>> +		prev_task = list_prev_entry(task, list_entry);
>>>>> +
>>>>> +	/*
>>>>> +	 * Although IRQ is disabled, GCE continues to execute.
>>>>> +	 * It may have pending IRQ before GCE thread is suspended,
>>>>> +	 * so check this condition again.
>>>>> +	 */
>>>>
>>>> The first thing we did in this function was suspending the thread. Why
>>>> do we need this then?
>>>
>>> Because timeout is CPU timeout not GCE timeout, GCE could just finish
>>> this task before the GCE thread is suspended.
>>>
>>
>> What are the reasons for a timeout? An error has happend, or the task is
>> still executing.
>
>  From GCE's point of view, this task is still executing.
> But, it could be an error of client.
> For example, task may never get event if display turn off hardware
> before waiting for task to finish its work.
>
>>>> To be honest this whole functions looks really like a design error. We
>>>> have to sperate the states much clearer so that it is possible to
>>>> understand what is happening in the GCE. Isn't it for example posible to
>>>> have worker queues for timed out tasks and tasks with an error? I'm not
>>>> sure how to do this, actually I'm not sure if I really understood how
>>>> this is supposed to work.
>>>
>>> GCE doesn't have timeout. The timeout is decided and controlled by CPU,
>>> so we check timeout in release work.
>>> For error and done, they are easy to check by register, and we have
>>> already created release work for timeout. So, I don't think we need to
>>> create work queue for each case.
>>>
>>> What do you think?
>>>
>>
>> I think, if we find in here, that the irq_flag is set, then the the
>> interrupt handler was triggered and is spinning the spinlock. If this is
>> not the case, we have a timeout and we handle this.
>
> I will write another function to handle error, and handle timeout here.
>
>>>> How much do we win, when we patch the thread command queue for every
>>>> task we add, instead of just taking one task after another from the
>>>> task_busy_list?
>>>
>>> GCE is used to help read/write registers with critical time limitation.
>>> Sometimes, client may ask to process multiple tasks in a short period
>>> of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
>>> shouldn't limit to process one task after another from the
>>> task_busy_list. Currently, when interrupt or timeout, we will check
>>> how many tasks are done, and which one is error or timeout.
>>>
>>
>> So I suppose the device driver who use this are interested in throughput
>> and not in latency. The callback of every
>>
>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>> +
>>>>> +	if (task->task_state == TASK_STATE_DONE) {
>>>>> +		cmdq_thread_resume(thread);
>>>>> +		return 0;
>>>>> +	}
>>>>> +
>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>> +		dev_err(dev, "task 0x%p error\n", task);
>>>>> +		if (next_task) /* move to next task */
>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>> +		cmdq_thread_resume(thread);
>>>>> +		return -ECANCELED;
>>>>> +	}
>>>>> +
>>>>> +	/* Task is running, so we force to remove it. */
>>>>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
>>>>> +	task->task_state = TASK_STATE_ERROR;
>>>>> +
>>>>> +	if (prev_task) {
>>>>> +		u64 *prev_va = prev_task->va_base;
>>>>> +		u64 *curr_va = task->va_base;
>>>>> +
>>>>> +		/* copy JUMP instruction */
>>>>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
>>>>> +
>>>>> +		cmdq_thread_invalidate_fetched_data(thread);
>>>>> +	} else if (next_task) { /* move to next task */
>>>>> +		cmdq_thread_writel(thread, next_task->pa_base,
>>>>> +				   CMDQ_THR_CURR_ADDR);
>>>>> +	}
>>>>> +
>>>>> +	list_del(&task->list_entry);
>>>>> +	cmdq_thread_resume(thread);
>>>>> +
>>>>> +	/* call cb here to prevent lock */
>>>>> +	if (task->cb.cb) {
>>>>> +		struct cmdq_cb_data cmdq_cb_data;
>>>>> +
>>>>> +		cmdq_cb_data.err = true;
>>>>> +		cmdq_cb_data.data = task->cb.data;
>>>>> +		task->cb.cb(cmdq_cb_data);
>>>>> +	}
>>>>> +
>>>>> +	return -ECANCELED;
>>>>> +}
>>>>> +
>>>>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
>>>>> +{
>>>>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
>>>>> +					      release_work);
>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>> +	unsigned long flags;
>>>>> +
>>>>> +	wait_event_timeout(thread->wait_task_done,
>>>>> +			   task->task_state != TASK_STATE_BUSY,
>>>>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
>>>>> +
>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>> +	if (task->task_state != TASK_STATE_DONE)
>>>>> +		cmdq_task_handle_error_result(task);
>>>>> +	if (list_empty(&thread->task_busy_list))
>>>>> +		cmdq_thread_disable(cmdq, thread);
>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>> +
>>>>> +	/* release regardless of success or not */
>>>>> +	clk_disable_unprepare(cmdq->clock);
>>>>> +	cmdq_task_release(task);
>>>>> +}
>>>>> +
>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
>>>>> +{
>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>> +
>>>>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
>>>>> +	queue_work(cmdq->task_release_wq, &task->release_work);
>>>>> +}
>>>>> +
>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
>>>>> +{
>>>>> +	void *new_buf;
>>>>> +
>>>>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
>>>>> +	if (!new_buf)
>>>>> +		return -ENOMEM;
>>>>> +	rec->buf = new_buf;
>>>>> +	rec->buf_size = size;
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
>>>>> +{
>>>>> +	struct cmdq_base *cmdq_base;
>>>>> +	struct resource res;
>>>>> +	int subsys;
>>>>> +	u32 base;
>>>>> +
>>>>> +	if (of_address_to_resource(dev->of_node, 0, &res))
>>>>> +		return NULL;
>>>>> +	base = (u32)res.start;
>>>>> +
>>>>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
>>>>> +	if (subsys < 0)
>>>>> +		return NULL;
>>>>> +
>>>>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
>>>>> +	if (!cmdq_base)
>>>>> +		return NULL;
>>>>> +	cmdq_base->subsys = subsys;
>>>>> +	cmdq_base->base = base;
>>>>> +
>>>>> +	return cmdq_base;
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_register_device);
>>>>> +
>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>> +		    struct cmdq_rec **rec_ptr)
>>>>> +{
>>>>> +	struct cmdq_rec *rec;
>>>>> +	int err;
>>>>> +
>>>>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
>>>>> +	if (!rec)
>>>>> +		return -ENOMEM;
>>>>> +	rec->cmdq = dev_get_drvdata(dev);
>>>>> +	rec->engine_flag = engine_flag;
>>>>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
>>>>
>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
>>>
>>> Will do.
>>>
>>>>> +	if (err < 0) {
>>>>> +		kfree(rec);
>>>>> +		return err;
>>>>> +	}
>>>>> +	*rec_ptr = rec;
>>>>> +	return 0;
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
>>>>> +
>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
>>>>> +				   u32 arg_a, u32 arg_b)
>>>>> +{
>>>>> +	u64 *cmd_ptr;
>>>>> +	int err;
>>>>> +
>>>>> +	if (WARN_ON(rec->finalized))
>>>>> +		return -EBUSY;
>>>>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
>>>>> +		return -EINVAL;
>>>>
>>>> cmdq_rec_append_command is just called from inside this driver and code
>>>> is a enum. We can expect it to be correct, no need for this check.
>>>
>>> Will drop this check.
>>>
>>>>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
>>>>
>>>> command_size is the offset into the buffer to which a new command is
>>>> written, so this name is highly confusing. I wonder if this would be
>>>> easier to understand if we redefine command_size to something like the
>>>> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
>>>
>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
>>> dividing CMDQ_INST_SIZE.
>>> What do you think?
>>>
>>>>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
>>>>> +		if (err < 0)
>>>>> +			return err;
>>>>> +	}
>>>>> +	cmd_ptr = rec->buf + rec->command_size;
>>>>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
>>>>> +	rec->command_size += CMDQ_INST_SIZE;
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
>>>>> +		   u32 offset)
>>>>> +{
>>>>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
>>>>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
>>>>
>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
>>>
>>> Will drop it.
>>>
>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
>>>>> +
>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>> +			struct cmdq_base *base, u32 offset, u32 mask)
>>>>> +{
>>>>> +	u32 offset_mask = offset;
>>>>> +	int err;
>>>>> +
>>>>> +	if (mask != 0xffffffff) {
>>>>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
>>>>> +		if (err < 0)
>>>>> +			return err;
>>>>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
>>>>> +	}
>>>>> +	return cmdq_rec_write(rec, value, base, offset_mask);
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
>>>>> +
>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
>>>>> +{
>>>>> +	u32 arg_b;
>>>>> +
>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	/*
>>>>> +	 * bit 0-11: wait value
>>>>> +	 * bit 15: 1 - wait, 0 - no wait
>>>>> +	 * bit 16-27: update value
>>>>> +	 * bit 31: 1 - update, 0 - no update
>>>>> +	 */
>>>>
>>>> I don't understand this comments. What are they for?
>>>
>>> This is for WFE command. I will comment it.
>>>
>>>>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
>>>>> +
>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
>>>>> +{
>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
>>>>> +				       CMDQ_WFE_UPDATE);
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
>>>>> +
>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
>>>>> +{
>>>>> +	int err;
>>>>> +
>>>>> +	if (rec->finalized)
>>>>> +		return 0;
>>>>> +
>>>>> +	/* insert EOC and generate IRQ for each command iteration */
>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
>>>>> +	if (err < 0)
>>>>> +		return err;
>>>>> +
>>>>> +	/* JUMP to end */
>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
>>>>> +	if (err < 0)
>>>>> +		return err;
>>>>> +
>>>>
>>>> Does this need to be atomic?
>>>> What happens if after CODE_EOC and before CODE_JUMP some
>>>> write/read/event gets added?
>>>> What happens if more commands get added to the queue after CODE_JUMP,
>>>> but before finalized is set to true. Why don't you use atomic functions
>>>> to access finalized?
>>>
>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
>>> client uses cmdq_rec.
>>>
>>
>> Well I think that rec->finalized tries to implement this, but might
>> fail, if two kernel threads work on the same cmdq_rec.
>>
>>>>> +	rec->finalized = true;
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>> +			 void *data)
>>>>> +{
>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>> +	struct cmdq_task *task;
>>>>> +	struct cmdq_task_cb task_cb;
>>>>> +	struct cmdq_thread *thread;
>>>>> +	int err;
>>>>> +
>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>> +	if (cmdq->suspended) {
>>>>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>> +		return -EPERM;
>>>>> +	}
>>>>> +
>>>>> +	err = cmdq_rec_finalize(rec);
>>>>> +	if (err < 0) {
>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>> +		return err;
>>>>> +	}
>>>>> +
>>>>> +	task_cb.cb = cb;
>>>>> +	task_cb.data = data;
>>>>> +	task = cmdq_task_acquire(rec, task_cb);
>>>>> +	if (!task) {
>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>> +		return -EFAULT;
>>>>> +	}
>>>>> +
>>>>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
>>>>> +	cmdq_task_exec(task, thread);
>>>>> +	cmdq_task_wait_release_schedule(task);
>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>> +	return 0;
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
>>>>> +
>>>>> +struct cmdq_flush_completion {
>>>>> +	struct completion cmplt;
>>>>> +	bool err;
>>>>> +};
>>>>> +
>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
>>>>> +{
>>>>> +	struct cmdq_flush_completion *cmplt = data.data;
>>>>> +
>>>>> +	cmplt->err = data.err;
>>>>> +	complete(&cmplt->cmplt);
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
>>>>> +{
>>>>> +	struct cmdq_flush_completion cmplt;
>>>>> +	int err;
>>>>> +
>>>>> +	init_completion(&cmplt.cmplt);
>>>>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
>>>>> +	if (err < 0)
>>>>> +		return err;
>>>>> +	wait_for_completion(&cmplt.cmplt);
>>>>> +	return cmplt.err ? -EFAULT : 0;
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
>>>>> +
>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
>>>>> +{
>>>>> +	kfree(rec->buf);
>>>>> +	kfree(rec);
>>>>> +}
>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
>>>>> +
>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
>>>>> +{
>>>>> +	struct cmdq_thread *thread;
>>>>> +	int i;
>>>>> +
>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>> +		thread = &cmdq->thread[i];
>>>>> +		if (!list_empty(&thread->task_busy_list))
>>>>> +			return false;
>>>>> +	}
>>>>> +	return true;
>>>>> +}
>>>>> +
>>>>> +static int cmdq_suspend(struct device *dev)
>>>>> +{
>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>> +	u32 exec_threads;
>>>>> +
>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>> +	cmdq->suspended = true;
>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>> +
>>>>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
>>>>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
>>>>> +		dev_err(dev, "wait active tasks timeout.\n");
>>>>> +		flush_workqueue(cmdq->task_release_wq);
>>>>> +	}
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int cmdq_resume(struct device *dev)
>>>>> +{
>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>> +
>>>>> +	cmdq->suspended = false;
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int cmdq_remove(struct platform_device *pdev)
>>>>> +{
>>>>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
>>>>> +
>>>>> +	destroy_workqueue(cmdq->task_release_wq);
>>>>> +	cmdq->task_release_wq = NULL;
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int cmdq_probe(struct platform_device *pdev)
>>>>> +{
>>>>> +	struct device *dev = &pdev->dev;
>>>>> +	struct device_node *node = dev->of_node;
>>>>> +	struct resource *res;
>>>>> +	struct cmdq *cmdq;
>>>>> +	int err, i;
>>>>> +
>>>>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
>>>>> +	if (!cmdq)
>>>>> +		return -ENOMEM;
>>>>> +	cmdq->dev = dev;
>>>>> +
>>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>> +	cmdq->base = devm_ioremap_resource(dev, res);
>>>>> +	if (IS_ERR(cmdq->base)) {
>>>>> +		dev_err(dev, "failed to ioremap gce\n");
>>>>> +		return PTR_ERR(cmdq->base);
>>>>> +	}
>>>>> +
>>>>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
>>>>> +	if (!cmdq->irq) {
>>>>> +		dev_err(dev, "failed to get irq\n");
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
>>>>> +		dev, cmdq->base, cmdq->irq);
>>>>> +
>>>>> +	mutex_init(&cmdq->task_mutex);
>>>>> +	spin_lock_init(&cmdq->exec_lock);
>>>>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
>>>>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
>>>>> +			"cmdq_task_wait_release");
>>>>> +
>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
>>>>> +				CMDQ_THR_SHIFT * i;
>>>>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
>>>>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
>>>>> +	}
>>>>> +
>>>>> +	platform_set_drvdata(pdev, cmdq);
>>>>> +
>>>>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
>>>>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
>>>>> +	if (err < 0) {
>>>>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
>>>>> +		goto fail;
>>>>> +	}
>>>>> +
>>>>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
>>>>> +	if (IS_ERR(cmdq->clock)) {
>>>>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
>>>>> +		err = PTR_ERR(cmdq->clock);
>>>>> +		goto fail;
>>>>> +	}
>>>>> +	return 0;
>>>>> +
>>>>> +fail:
>>>>> +	cmdq_remove(pdev);
>>>>> +	return err;
>>>>> +}
>>>>> +
>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
>>>>> +	.suspend = cmdq_suspend,
>>>>> +	.resume = cmdq_resume,
>>>>> +};
>>>>> +
>>>>> +static const struct of_device_id cmdq_of_ids[] = {
>>>>> +	{.compatible = "mediatek,mt8173-gce",},
>>>>> +	{}
>>>>> +};
>>>>> +
>>>>> +static struct platform_driver cmdq_drv = {
>>>>> +	.probe = cmdq_probe,
>>>>> +	.remove = cmdq_remove,
>>>>> +	.driver = {
>>>>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
>>>>> +		.owner = THIS_MODULE,
>>>>> +		.pm = &cmdq_pm_ops,
>>>>> +		.of_match_table = cmdq_of_ids,
>>>>> +	}
>>>>> +};
>>>>> +
>>>>> +builtin_platform_driver(cmdq_drv);
>>>>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
>>>>> new file mode 100644
>>>>> index 0000000..60eef3d
>>>>> --- /dev/null
>>>>> +++ b/include/soc/mediatek/cmdq.h
>>>>> @@ -0,0 +1,197 @@
>>>>> +/*
>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>> + * published by the Free Software Foundation.
>>>>> + *
>>>>> + * This program is distributed in the hope that it will be useful,
>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>> + * GNU General Public License for more details.
>>>>> + */
>>>>> +
>>>>> +#ifndef __MTK_CMDQ_H__
>>>>> +#define __MTK_CMDQ_H__
>>>>> +
>>>>> +#include <linux/platform_device.h>
>>>>> +#include <linux/types.h>
>>>>> +
>>>>> +enum cmdq_eng {
>>>>> +	CMDQ_ENG_DISP_AAL,
>>>>> +	CMDQ_ENG_DISP_COLOR0,
>>>>> +	CMDQ_ENG_DISP_COLOR1,
>>>>> +	CMDQ_ENG_DISP_DPI0,
>>>>> +	CMDQ_ENG_DISP_DSI0,
>>>>> +	CMDQ_ENG_DISP_DSI1,
>>>>> +	CMDQ_ENG_DISP_GAMMA,
>>>>> +	CMDQ_ENG_DISP_OD,
>>>>> +	CMDQ_ENG_DISP_OVL0,
>>>>> +	CMDQ_ENG_DISP_OVL1,
>>>>> +	CMDQ_ENG_DISP_PWM0,
>>>>> +	CMDQ_ENG_DISP_PWM1,
>>>>> +	CMDQ_ENG_DISP_RDMA0,
>>>>> +	CMDQ_ENG_DISP_RDMA1,
>>>>> +	CMDQ_ENG_DISP_RDMA2,
>>>>> +	CMDQ_ENG_DISP_UFOE,
>>>>> +	CMDQ_ENG_DISP_WDMA0,
>>>>> +	CMDQ_ENG_DISP_WDMA1,
>>>>> +	CMDQ_ENG_MAX,
>>>>> +};
>>>>> +
>>>>> +/* events for CMDQ and display */
>>>>> +enum cmdq_event {
>>>>> +	/* Display start of frame(SOF) events */
>>>>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
>>>>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
>>>>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
>>>>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
>>>>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
>>>>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
>>>>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
>>>>> +	/* Display end of frame(EOF) events */
>>>>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
>>>>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
>>>>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
>>>>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
>>>>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
>>>>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
>>>>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
>>>>> +	/* Mutex end of frame(EOF) events */
>>>>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
>>>>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
>>>>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
>>>>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
>>>>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
>>>>> +	/* Display underrun events */
>>>>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
>>>>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
>>>>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
>>>>> +	/* Keep this at the end of HW events */
>>>>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
>>>>> +};
>>>>> +
>>>>> +struct cmdq_cb_data {
>>>>> +	bool	err;
>>>>> +	void	*data;
>>>>> +};
>>>>> +
>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
>>>>> +
>>>>> +struct cmdq_task;
>>>>> +struct cmdq;
>>>>> +
>>>>> +struct cmdq_rec {
>>>>> +	struct cmdq		*cmdq;
>>>>> +	u64			engine_flag;
>>>>> +	size_t			command_size;
>>>>> +	void			*buf;
>>>>> +	size_t			buf_size;
>>>>> +	bool			finalized;
>>>>> +};
>>
>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that
>> and this way make the driver less complex?
>
> There are two main reasons for cmdq_rec.
> 1. It is slow to access dma too frequently.
>     So, we append commands to cacheable memory, and then flush to dma.
> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
>     If we merge them, we need to take care of some synchronization
>     issues.
>
>>>>> +
>>>>> +struct cmdq_base {
>>>>> +	int	subsys;
>>>>> +	u32	base;
>>>>
>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
>>>> so we can get rid of the struct, right?
>>>
>>> Current subsys method is based on previous comment from Daniel Kurtz.
>>> Please take a look of our previous discussion.
>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>> Thanks.
>>>
>>
>> I have to look deeper into this, but from what I read, the proposal from
>> Daniel [1] seems good to me.
>>
>> [1] https://patchwork.kernel.org/patch/8068311/
>
> The initial proposal has some problem, so please see the follow-up
> discussions. Thanks.
> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>
>
>>>>> +};
>>>>> +
>>>>> +/**
>>>>> + * cmdq_register_device() - register device which needs CMDQ
>>>>> + * @dev:		device
>>>>> + *
>>>>> + * Return: cmdq_base pointer or NULL for failed
>>>>> + */
>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_create() - create command queue record
>>>>> + * @dev:		device
>>>>> + * @engine_flag:	command queue engine flag
>>>>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + */
>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>> +		    struct cmdq_rec **rec_ptr);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_write() - append write command to the command queue record
>>>>> + * @rec:	the command queue record
>>>>> + * @value:	the specified target register value
>>>>> + * @base:	the command queue base
>>>>> + * @offset:	register offset from module base
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + */
>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
>>>>> +		   struct cmdq_base *base, u32 offset);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_write_mask() - append write command with mask to the command
>>>>> + *			   queue record
>>>>> + * @rec:	the command queue record
>>>>> + * @value:	the specified target register value
>>>>> + * @base:	the command queue base
>>>>> + * @offset:	register offset from module base
>>>>> + * @mask:	the specified target register mask
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + */
>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>> +			struct cmdq_base *base, u32 offset, u32 mask);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
>>>>
>>>> reco rd -> record
>>>
>>> Will fix it.
>>>
>>>> Regards,
>>>> Matthias
>>>>
>>>>> + * @rec:	the command queue record
>>>>> + * @event:	the desired event type to "wait and CLEAR"
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + */
>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_clear_event() - append clear event command to the command queue
>>>>> + *			    record
>>>>> + * @rec:	the command queue record
>>>>> + * @event:	the desired event to be cleared
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + */
>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
>>>>> + * @rec:	the command queue record
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + *
>>>>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
>>>>> + * synchronous flush function. When the function returned, the recorded
>>>>> + * commands have been done.
>>>>> + */
>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
>>>>> + *			    commands and call back after ISR is finished
>>>>> + * @rec:	the command queue record
>>>>> + * @cb:		called in the end of CMDQ ISR
>>>>> + * @data:	this data will pass back to cb
>>>>> + *
>>>>> + * Return: 0 for success; else the error code is returned
>>>>> + *
>>>>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
>>>>> + * after ISR is finished. Note that this is an ASYNC function. When the function
>>>>> + * returned, it may or may not be finished. The ISR callback function is called
>>>>> + * in the end of ISR.
>>
>> "The callback is called from the ISR."
>>
>> Regards,
>> Matthias
>>
>>>>> + */
>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>> +			 void *data);
>>>>> +
>>>>> +/**
>>>>> + * cmdq_rec_destroy() - destroy command queue record
>>>>> + * @rec:	the command queue record
>>>>> + */
>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
>>>>> +
>>>>> +#endif	/* __MTK_CMDQ_H__ */
>>>>>
>>>
>>> Thanks,
>>> HS
>>>
>
> Thanks,
> HS
>


From jitao.shi at mediatek.com  Thu Jun  2 02:57:25 2016
From: jitao.shi at mediatek.com (Jitao Shi)
Date: Thu, 2 Jun 2016 17:57:25 +0800
Subject: [PATCH 2/2 v16] drm/bridge: Add I2C based driver for ps8640 bridge
In-Reply-To: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>
References: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>
Message-ID: <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>

This patch adds drm_bridge driver for parade DSI to eDP bridge chip.

Signed-off-by: Jitao Shi <jitao.shi at mediatek.com>
Reviewed-by: Daniel Kurtz <djkurtz at chromium.org>
---
Changes since v15:
 - Drop drm_connector_(un)register calls from parade ps8640.
   The main DRM driver mtk_drm_drv now calls
   drm_connector_register_all() after drm_dev_register() in the
   mtk_drm_bind() function. That function should iterate over all
   connectors and call drm_connector_register() for each of them.
   So, remove drm_connector_(un)register calls from parade ps8640.

Changes since v14:
 - update copyright info.
 - change bridge_to_ps8640 and connector_to_ps8640 to inline function.
 - fix some coding style.
 - use sizeof as array counter.
 - use drm_get_edid when read edid.
 - add mutex when firmware updating. 

Changes since v13:
 - add const on data, ps8640_write_bytes(struct i2c_client *client, const u8 *data, u16 data_len)
 - fix PAGE2_SW_REST tyro.
 - move the buf[3] init to entrance of the function.

Changes since v12:
 - fix hw_chip_id build warning

Changes since v11:
 - Remove depends on I2C, add DRM depends
 - Reuse ps8640_write_bytes() in ps8640_write_byte()
 - Use timer check for polling like the routines in <linux/iopoll.h>
 - Fix no drm_connector_unregister/drm_connector_cleanup when ps8640_bridge_attach fail
 - Check the ps8640 hardware id in ps8640_validate_firmware
 - Remove fw_version check
 - Move ps8640_validate_firmware before ps8640_enter_bl
 - Add ddc_i2c unregister when probe fail and ps8640_remove
---
 drivers/gpu/drm/bridge/Kconfig         |   12 +
 drivers/gpu/drm/bridge/Makefile        |    1 +
 drivers/gpu/drm/bridge/parade-ps8640.c | 1067 ++++++++++++++++++++++++++++++++
 3 files changed, 1080 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/parade-ps8640.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 8f7423f..02fea1a 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -50,6 +50,18 @@ config DRM_PARADE_PS8622
 	---help---
 	  Parade eDP-LVDS bridge chip driver.
 
+config DRM_PARADE_PS8640
+	tristate "Parade PS8640 MIPI DSI to eDP Converter"
+	depends on DRM
+	depends on OF
+	select DRM_KMS_HELPER
+	select DRM_MIPI_DSI
+	select DRM_PANEL
+	---help---
+	  Choose this option if you have PS8640 for display
+	  The PS8640 is a high-performance and low-power
+	  MIPI DSI to eDP converter
+
 source "drivers/gpu/drm/bridge/analogix/Kconfig"
 
 endmenu
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 96b13b3..6c00b2f 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -6,3 +6,4 @@ 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/
+obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
new file mode 100644
index 0000000..a73871e
--- /dev/null
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -0,0 +1,1067 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/regulator/consumer.h>
+#include <asm/unaligned.h>
+#include <drm/drm_panel.h>
+
+#include <drmP.h>
+#include <drm_atomic_helper.h>
+#include <drm_crtc_helper.h>
+#include <drm_crtc.h>
+#include <drm_edid.h>
+#include <drm_mipi_dsi.h>
+
+#define PAGE2_SPI_CFG3		0x82
+#define I2C_TO_SPI_RESET	0x20
+#define PAGE2_ROMADD_BYTE1	0x8e
+#define PAGE2_ROMADD_BYTE2	0x8f
+#define PAGE2_SWSPI_WDATA	0x90
+#define PAGE2_SWSPI_RDATA	0x91
+#define PAGE2_SWSPI_LEN		0x92
+#define PAGE2_SWSPI_CTL		0x93
+#define TRIGGER_NO_READBACK	0x05
+#define TRIGGER_READBACK	0x01
+#define PAGE2_SPI_STATUS	0x9e
+#define SPI_READY		0x0c
+#define PAGE2_GPIO_L		0xa6
+#define PAGE2_GPIO_H		0xa7
+#define PS_GPIO9		BIT(1)
+#define PAGE2_IROM_CTRL		0xb0
+#define IROM_ENABLE		0xc0
+#define IROM_DISABLE		0x80
+#define PAGE2_SW_RESET		0xbc
+#define SPI_SW_RESET		BIT(7)
+#define MPU_SW_RESET		BIT(6)
+#define PAGE2_ENCTLSPI_WR	0xda
+#define PAGE2_I2C_BYPASS	0xea
+#define I2C_BYPASS_EN		0xd0
+#define PAGE3_SET_ADD		0xfe
+#define PAGE3_SET_VAL		0xff
+#define VDO_CTL_ADD		0x13
+#define VDO_DIS			0x18
+#define VDO_EN			0x1c
+#define PAGE4_REV_L		0xf0
+#define PAGE4_REV_H		0xf1
+#define PAGE4_CHIP_L		0xf2
+#define PAGE4_CHIP_H		0xf3
+
+/* Firmware */
+#define SPI_MAX_RETRY_CNT	8
+#define PS_FW_NAME		"ps864x_fw.bin"
+
+#define FW_CHIP_ID_OFFSET	0
+#define FW_VERSION_OFFSET	2
+#define EDID_I2C_ADDR		0x50
+
+#define WRITE_STATUS_REG_CMD	0x01
+#define READ_STATUS_REG_CMD	0x05
+#define BUSY			BIT(0)
+#define CLEAR_ALL_PROTECT	0x00
+#define BLK_PROTECT_BITS	0x0c
+#define STATUS_REG_PROTECT	BIT(7)
+#define WRITE_ENABLE_CMD	0x06
+#define CHIP_ERASE_CMD		0xc7
+#define MAX_DEVS		0x8
+struct ps8640_info {
+	u8 family_id;
+	u8 variant_id;
+	u16 version;
+};
+
+struct ps8640 {
+	struct drm_connector connector;
+	struct drm_bridge bridge;
+	struct edid *edid;
+	struct mipi_dsi_device dsi;
+	struct i2c_client *page[MAX_DEVS];
+	struct i2c_client *ddc_i2c;
+	struct regulator_bulk_data supplies[2];
+	struct drm_panel *panel;
+	struct gpio_desc *gpio_rst_n;
+	struct gpio_desc *gpio_slp_n;
+	struct gpio_desc *gpio_mode_sel_n;
+	bool enabled;
+
+	/* firmware file info */
+	struct ps8640_info info;
+	bool in_fw_update;
+	/* for firmware update protect */
+	struct mutex fw_mutex;
+};
+
+static const u8 enc_ctrl_code[6] = { 0xaa, 0x55, 0x50, 0x41, 0x52, 0x44 };
+static const u8 hw_chip_id[4] = { 0x00, 0x0a, 0x00, 0x30 };
+
+static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
+{
+	return container_of(e, struct ps8640, bridge);
+}
+
+static inline struct ps8640 *connector_to_ps8640(struct drm_connector *e)
+{
+	return container_of(e, struct ps8640, connector);
+}
+
+static int ps8640_read(struct i2c_client *client, u8 reg, u8 *data,
+		       u16 data_len)
+{
+	int ret;
+	struct i2c_msg msgs[] = {
+		{
+		 .addr = client->addr,
+		 .flags = 0,
+		 .len = 1,
+		 .buf = &reg,
+		},
+		{
+		 .addr = client->addr,
+		 .flags = I2C_M_RD,
+		 .len = data_len,
+		 .buf = data,
+		}
+	};
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+
+	if (ret == 2)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static int ps8640_write_bytes(struct i2c_client *client, const u8 *data,
+			      u16 data_len)
+{
+	int ret;
+	struct i2c_msg msg;
+
+	msg.addr = client->addr;
+	msg.flags = 0;
+	msg.len = data_len;
+	msg.buf = (u8 *)data;
+
+	ret = i2c_transfer(client->adapter, &msg, 1);
+	if (ret == 1)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static int ps8640_write_byte(struct i2c_client *client, u8 reg,  u8 data)
+{
+	u8 buf[] = { reg, data };
+
+	return ps8640_write_bytes(client, buf, sizeof(buf));
+}
+
+static void ps8640_get_mcu_fw_version(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[5];
+	u8 fw_ver[2];
+
+	ps8640_read(client, 0x4, fw_ver, sizeof(fw_ver));
+	ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1];
+
+	DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]);
+}
+
+static int ps8640_bridge_unmute(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[3];
+	u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_EN };
+
+	return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
+}
+
+static int ps8640_bridge_mute(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[3];
+	u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_DIS };
+
+	return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
+}
+
+static void ps8640_pre_enable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	struct i2c_client *client = ps_bridge->page[2];
+	int err;
+	u8 set_vdo_done;
+	ktime_t timeout;
+
+	if (ps_bridge->in_fw_update)
+		return;
+
+	if (ps_bridge->enabled)
+		return;
+
+	err = drm_panel_prepare(ps_bridge->panel);
+	if (err < 0) {
+		DRM_ERROR("failed to prepare panel: %d\n", err);
+		return;
+	}
+
+	err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
+				    ps_bridge->supplies);
+	if (err < 0) {
+		DRM_ERROR("cannot enable regulators %d\n", err);
+		goto err_panel_unprepare;
+	}
+
+	gpiod_set_value(ps_bridge->gpio_slp_n, 1);
+	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
+	usleep_range(2000, 2500);
+	gpiod_set_value(ps_bridge->gpio_rst_n, 1);
+
+	/*
+	 * Wait for the ps8640 embed mcu ready
+	 * First wait 200ms and then check the mcu ready flag every 20ms
+	 */
+	msleep(200);
+
+	timeout = ktime_add_ms(ktime_get(), 200);
+	for (;;) {
+		err = ps8640_read(client, PAGE2_GPIO_H, &set_vdo_done, 1);
+		if (err < 0) {
+			DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err);
+			goto err_regulators_disable;
+		}
+		if ((set_vdo_done & PS_GPIO9) == PS_GPIO9)
+			break;
+		if (ktime_compare(ktime_get(), timeout) > 0)
+			break;
+		msleep(20);
+	}
+
+	if (ps_bridge->info.version == 0)
+		ps8640_get_mcu_fw_version(ps_bridge);
+
+	err = ps8640_bridge_unmute(ps_bridge);
+	if (err)
+		DRM_ERROR("failed to enable unmutevideo: %d\n", err);
+	/* Switch access edp panel's edid through i2c */
+	ps8640_write_byte(client, PAGE2_I2C_BYPASS, I2C_BYPASS_EN);
+	ps_bridge->enabled = true;
+
+	return;
+
+err_regulators_disable:
+	regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
+			       ps_bridge->supplies);
+err_panel_unprepare:
+	drm_panel_unprepare(ps_bridge->panel);
+}
+
+static void ps8640_enable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	int err;
+
+	err = drm_panel_enable(ps_bridge->panel);
+	if (err < 0)
+		DRM_ERROR("failed to enable panel: %d\n", err);
+}
+
+static void ps8640_disable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	int err;
+
+	err = drm_panel_disable(ps_bridge->panel);
+	if (err < 0)
+		DRM_ERROR("failed to disable panel: %d\n", err);
+}
+
+static void ps8640_post_disable(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	int err;
+
+	if (ps_bridge->in_fw_update)
+		return;
+
+	if (!ps_bridge->enabled)
+		return;
+
+	ps_bridge->enabled = false;
+
+	err = ps8640_bridge_mute(ps_bridge);
+	if (err < 0)
+		DRM_ERROR("failed to unmutevideo: %d\n", err);
+
+	gpiod_set_value(ps_bridge->gpio_rst_n, 0);
+	gpiod_set_value(ps_bridge->gpio_slp_n, 0);
+	err = regulator_bulk_disable(ARRAY_SIZE(ps_bridge->supplies),
+				     ps_bridge->supplies);
+	if (err < 0)
+		DRM_ERROR("cannot disable regulators %d\n", err);
+
+	err = drm_panel_unprepare(ps_bridge->panel);
+	if (err)
+		DRM_ERROR("failed to unprepare panel: %d\n", err);
+}
+
+static int ps8640_get_modes(struct drm_connector *connector)
+{
+	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
+	struct device *dev = &ps_bridge->page[0]->dev;
+	struct edid *edid;
+	int num_modes = 0;
+	bool power_off;
+
+	if (ps_bridge->edid)
+		return drm_add_edid_modes(connector, ps_bridge->edid);
+
+	power_off = !ps_bridge->enabled;
+	ps8640_pre_enable(&ps_bridge->bridge);
+
+	edid = devm_kmalloc(dev, sizeof(edid), GFP_KERNEL);
+	if (!edid) {
+		DRM_ERROR("Failed to allocate EDID\n");
+		return 0;
+	}
+
+	edid = drm_get_edid(connector, ps_bridge->ddc_i2c->adapter);
+	if (!edid)
+		goto out;
+
+	ps_bridge->edid = edid;
+	drm_mode_connector_update_edid_property(connector, ps_bridge->edid);
+	num_modes = drm_add_edid_modes(connector, ps_bridge->edid);
+
+out:
+	if (power_off)
+		ps8640_post_disable(&ps_bridge->bridge);
+
+	return num_modes;
+}
+
+static struct drm_encoder *ps8640_best_encoder(struct drm_connector *connector)
+{
+	struct ps8640 *ps_bridge = connector_to_ps8640(connector);
+
+	return ps_bridge->bridge.encoder;
+}
+
+static const struct drm_connector_helper_funcs ps8640_connector_helper_funcs = {
+	.get_modes = ps8640_get_modes,
+	.best_encoder = ps8640_best_encoder,
+};
+
+static enum drm_connector_status ps8640_detect(struct drm_connector *connector,
+					       bool force)
+{
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs ps8640_connector_funcs = {
+	.dpms = drm_atomic_helper_connector_dpms,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = ps8640_detect,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+int ps8640_bridge_attach(struct drm_bridge *bridge)
+{
+	struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
+	struct device *dev = &ps_bridge->page[0]->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *port, *in_ep;
+	struct device_node *dsi_node = NULL;
+	struct mipi_dsi_host *host = ps_bridge->dsi.host;
+	int ret;
+
+	ret = drm_connector_init(bridge->dev, &ps_bridge->connector,
+				 &ps8640_connector_funcs,
+				 DRM_MODE_CONNECTOR_eDP);
+
+	if (ret) {
+		DRM_ERROR("Failed to initialize connector with drm: %d\n", ret);
+		return ret;
+	}
+
+	drm_connector_helper_add(&ps_bridge->connector,
+				 &ps8640_connector_helper_funcs);
+
+	ps_bridge->connector.dpms = DRM_MODE_DPMS_ON;
+	drm_mode_connector_attach_encoder(&ps_bridge->connector,
+					  bridge->encoder);
+
+	if (ps_bridge->panel)
+		drm_panel_attach(ps_bridge->panel, &ps_bridge->connector);
+
+	/* port at 0 is ps8640 dsi input port */
+	port = of_graph_get_port_by_id(np, 0);
+	if (port) {
+		in_ep = of_get_child_by_name(port, "endpoint");
+		of_node_put(port);
+		if (in_ep) {
+			dsi_node = of_graph_get_remote_port_parent(in_ep);
+			of_node_put(in_ep);
+		}
+	}
+	if (dsi_node) {
+		host = of_find_mipi_dsi_host_by_node(dsi_node);
+		of_node_put(dsi_node);
+		if (!host) {
+			ret = -ENODEV;
+			goto err;
+		}
+	}
+
+	ps_bridge->dsi.host = host;
+	ps_bridge->dsi.mode_flags = MIPI_DSI_MODE_VIDEO |
+				     MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+	ps_bridge->dsi.format = MIPI_DSI_FMT_RGB888;
+	ps_bridge->dsi.lanes = 4;
+	ret = mipi_dsi_attach(&ps_bridge->dsi);
+	if (ret)
+		goto err;
+
+	return 0;
+err:
+	if (ps_bridge->panel)
+		drm_panel_detach(ps_bridge->panel);
+	drm_connector_cleanup(&ps_bridge->connector);
+	return ret;
+}
+
+static const struct drm_bridge_funcs ps8640_bridge_funcs = {
+	.attach = ps8640_bridge_attach,
+	.disable = ps8640_disable,
+	.post_disable = ps8640_post_disable,
+	.pre_enable = ps8640_pre_enable,
+	.enable = ps8640_enable,
+};
+
+/* Firmware Version is returned as Major.Minor */
+static ssize_t ps8640_fw_version_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct ps8640 *ps_bridge = dev_get_drvdata(dev);
+	struct ps8640_info *info = &ps_bridge->info;
+
+	return scnprintf(buf, PAGE_SIZE, "%u.%u\n", info->version >> 8,
+			 info->version & 0xff);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t ps8640_hw_version_show(struct device *dev,
+				      struct device_attribute *attr, char *buf)
+{
+	struct ps8640 *ps_bridge = dev_get_drvdata(dev);
+	struct ps8640_info *info = &ps_bridge->info;
+
+	return scnprintf(buf, PAGE_SIZE, "ps%u.%u\n", info->family_id,
+			 info->variant_id);
+}
+
+static int ps8640_spi_send_cmd(struct ps8640 *ps_bridge, u8 *cmd, u8 cmd_len)
+{
+	struct i2c_client *client = ps_bridge->page[2];
+	u8 i, buf[3] = { PAGE2_SWSPI_LEN, cmd_len - 1, TRIGGER_NO_READBACK };
+	int ret;
+
+	ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
+	if (ret)
+		goto err;
+
+	/* write command in write port */
+	for (i = 0; i < cmd_len; i++) {
+		ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA, cmd[i]);
+		if (ret)
+			goto err_irom_disable;
+	}
+
+	ret = ps8640_write_bytes(client, buf, sizeof(buf));
+	if (ret)
+		goto err_irom_disable;
+
+	ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
+	if (ret)
+		goto err;
+
+	return 0;
+err_irom_disable:
+	ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
+err:
+	dev_err(&client->dev, "send command err: %d\n", ret);
+	return ret;
+}
+
+static int ps8640_wait_spi_ready(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[2];
+	u8 spi_rdy_st;
+	ktime_t timeout;
+
+	timeout = ktime_add_ms(ktime_get(), 200);
+	for (;;) {
+		ps8640_read(client, PAGE2_SPI_STATUS, &spi_rdy_st, 1);
+		if ((spi_rdy_st & SPI_READY) != SPI_READY)
+			break;
+
+		if (ktime_compare(ktime_get(), timeout) > 0) {
+			dev_err(&client->dev, "wait spi ready timeout\n");
+			return -EBUSY;
+		}
+
+		msleep(20);
+	}
+
+	return 0;
+}
+
+static int ps8640_wait_spi_nobusy(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[2];
+	u8 spi_status, buf[3] = { PAGE2_SWSPI_LEN, 0, TRIGGER_READBACK };
+	int ret;
+	ktime_t timeout;
+
+	timeout = ktime_add_ms(ktime_get(), 500);
+	for (;;) {
+		/* 0x05 RDSR; Read-Status-Register */
+		ret = ps8640_write_byte(client, PAGE2_SWSPI_WDATA,
+					READ_STATUS_REG_CMD);
+		if (ret)
+			goto err_send_cmd_exit;
+
+		ret = ps8640_write_bytes(client, buf, 3);
+		if (ret)
+			goto err_send_cmd_exit;
+
+		/* delay for cmd send */
+		usleep_range(300, 500);
+		/* wait for SPI ROM until not busy */
+		ret = ps8640_read(client, PAGE2_SWSPI_RDATA, &spi_status, 1);
+		if (ret)
+			goto err_send_cmd_exit;
+
+		if (!(spi_status & BUSY))
+			break;
+
+		if (ktime_compare(ktime_get(), timeout) > 0) {
+			dev_err(&client->dev, "wait spi no busy timeout: %d\n",
+				ret);
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+
+err_send_cmd_exit:
+	dev_err(&client->dev, "send command err: %d\n", ret);
+	return ret;
+}
+
+static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[0];
+	int ret;
+
+	ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
+	if (ret)
+		goto exit;
+
+	ret = ps8640_wait_spi_ready(ps_bridge);
+	if (ret)
+		goto err_spi;
+
+	ret = ps8640_wait_spi_nobusy(ps_bridge);
+	if (ret)
+		goto err_spi;
+
+	ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
+	if (ret)
+		goto exit;
+
+	return 0;
+
+err_spi:
+	ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
+exit:
+	if (ret)
+		dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret);
+
+	return ret;
+}
+
+static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[2];
+	int ret;
+
+	/* switch ps8640 mode to spi dl mode */
+	if (ps_bridge->gpio_mode_sel_n)
+		gpiod_set_value(ps_bridge->gpio_mode_sel_n, 0);
+
+	/* reset spi interface */
+	ret = ps8640_write_byte(client, PAGE2_SW_RESET,
+				SPI_SW_RESET | MPU_SW_RESET);
+	if (ret)
+		goto exit;
+
+	ret = ps8640_write_byte(client, PAGE2_SW_RESET, MPU_SW_RESET);
+	if (ret)
+		goto exit;
+
+exit:
+	if (ret)
+		dev_err(&client->dev, "fail reset spi interface: %d\n", ret);
+
+	return ret;
+}
+
+static int ps8640_rom_prepare(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[2];
+	struct device *dev = &client->dev;
+	u8 i, cmd[2];
+	int ret;
+
+	cmd[0] = WRITE_ENABLE_CMD;
+	ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
+	if (ret) {
+		dev_err(dev, "failed enable-write-status-register: %d\n", ret);
+		return ret;
+	}
+
+	cmd[0] = WRITE_STATUS_REG_CMD;
+	cmd[1] = CLEAR_ALL_PROTECT;
+	ret = ps8640_spi_send_cmd(ps_bridge, cmd, 2);
+	if (ret) {
+		dev_err(dev, "fail disable all protection: %d\n", ret);
+		return ret;
+	}
+
+	/* wait for SPI module ready */
+	ret = ps8640_wait_rom_idle(ps_bridge);
+	if (ret) {
+		dev_err(dev, "fail wait rom idle: %d\n", ret);
+		return ret;
+	}
+
+	ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
+	for (i = 0; i < 6; i++)
+		ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]);
+	ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
+
+	/* Enable-Write-Status-Register */
+	cmd[0] = WRITE_ENABLE_CMD;
+	ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
+	if (ret) {
+		dev_err(dev, "fail enable-write-status-register: %d\n", ret);
+		return ret;
+	}
+
+	/* chip erase command */
+	cmd[0] = CHIP_ERASE_CMD;
+	ret = ps8640_spi_send_cmd(ps_bridge, cmd, 1);
+	if (ret) {
+		dev_err(dev, "fail disable all protection: %d\n", ret);
+		return ret;
+	}
+
+	ret = ps8640_wait_rom_idle(ps_bridge);
+	if (ret) {
+		dev_err(dev, "fail wait rom idle: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ps8640_check_chip_id(struct ps8640 *ps_bridge)
+{
+	struct i2c_client *client = ps_bridge->page[4];
+	u8 buf[4];
+
+	ps8640_read(client, PAGE4_REV_L, buf, 4);
+	return memcmp(buf, hw_chip_id, sizeof(buf));
+}
+
+static int ps8640_validate_firmware(struct ps8640 *ps_bridge,
+				    const struct firmware *fw)
+{
+	struct i2c_client *client = ps_bridge->page[0];
+	u16 fw_chip_id;
+
+	/*
+	 * Get the chip_id from the firmware. Make sure that it is the
+	 * right controller to do the firmware and config update.
+	 */
+	fw_chip_id = get_unaligned_le16(fw->data + FW_CHIP_ID_OFFSET);
+
+	if (fw_chip_id != 0x8640 && ps8640_check_chip_id(ps_bridge) == 0) {
+		dev_err(&client->dev,
+			"chip id mismatch: fw 0x%x vs. chip 0x8640\n",
+			fw_chip_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw)
+{
+	struct i2c_client *client = ps_bridge->page[0];
+	struct device *dev = &client->dev;
+	struct i2c_client *client2 = ps_bridge->page[2];
+	struct i2c_client *client7 = ps_bridge->page[7];
+	size_t pos;
+	u8 buf[257], rom_page_id_buf[3];
+	int ret;
+	u16 cpy_len;
+
+	ps8640_write_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET);
+	msleep(100);
+	ps8640_write_byte(client2, PAGE2_SPI_CFG3, 0x00);
+
+	for (pos = 0; pos < fw->size; pos += cpy_len) {
+		rom_page_id_buf[0] = PAGE2_ROMADD_BYTE1;
+		rom_page_id_buf[1] = pos >> 8;
+		rom_page_id_buf[2] = pos >> 16;
+		ret = ps8640_write_bytes(client2, rom_page_id_buf, 3);
+		if (ret)
+			goto error;
+		cpy_len = fw->size >= 256 + pos ? 256 : fw->size - pos;
+		buf[0] = 0;
+		memcpy(buf + 1, fw->data + pos, cpy_len);
+		ret = ps8640_write_bytes(client7, buf, cpy_len + 1);
+		if (ret)
+			goto error;
+
+		dev_dbg(dev, "fw update completed %zu / %zu bytes\n", pos,
+			fw->size);
+	}
+	return 0;
+
+error:
+	dev_err(dev, "failed write external flash, %d\n", ret);
+	return ret;
+}
+
+static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge)
+{
+	u8 cmd[2];
+	struct i2c_client *client = ps_bridge->page[2];
+
+	/* Enable-Write-Status-Register */
+	cmd[0] = WRITE_ENABLE_CMD;
+	ps8640_spi_send_cmd(ps_bridge, cmd, 1);
+
+	/* protect BPL/BP0/BP1 */
+	cmd[0] = WRITE_STATUS_REG_CMD;
+	cmd[1] = BLK_PROTECT_BITS | STATUS_REG_PROTECT;
+	ps8640_spi_send_cmd(ps_bridge, cmd, 2);
+
+	/* wait for SPI rom ready */
+	ps8640_wait_rom_idle(ps_bridge);
+
+	/* disable PS8640 mapping function */
+	ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, 0x00);
+
+	if (ps_bridge->gpio_mode_sel_n)
+		gpiod_set_value(ps_bridge->gpio_mode_sel_n, 1);
+	return 0;
+}
+
+static int ps8640_enter_bl(struct ps8640 *ps_bridge)
+{
+	ps_bridge->in_fw_update = true;
+	return ps8640_spi_dl_mode(ps_bridge);
+}
+
+static void ps8640_exit_bl(struct ps8640 *ps_bridge, const struct firmware *fw)
+{
+	ps8640_spi_normal_mode(ps_bridge);
+	ps_bridge->in_fw_update = false;
+}
+
+static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw)
+{
+	struct i2c_client *client = ps_bridge->page[0];
+	struct device *dev = &client->dev;
+	int ret;
+	bool ps8640_status_backup = ps_bridge->enabled;
+
+	ret = ps8640_validate_firmware(ps_bridge, fw);
+	if (ret)
+		return ret;
+
+	mutex_lock(&ps_bridge->fw_mutex);
+	if (!ps_bridge->in_fw_update) {
+		if (!ps8640_status_backup)
+			ps8640_pre_enable(&ps_bridge->bridge);
+
+		ret = ps8640_enter_bl(ps_bridge);
+		if (ret)
+			goto exit;
+	}
+
+	ret = ps8640_rom_prepare(ps_bridge);
+	if (ret)
+		goto exit;
+
+	ret = ps8640_write_rom(ps_bridge, fw);
+
+exit:
+	if (ret)
+		dev_err(dev, "Failed to load firmware, %d\n", ret);
+
+	ps8640_exit_bl(ps_bridge, fw);
+	if (!ps8640_status_backup)
+		ps8640_post_disable(&ps_bridge->bridge);
+	mutex_unlock(&ps_bridge->fw_mutex);
+	return ret;
+}
+
+static ssize_t ps8640_update_fw_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
+	const struct firmware *fw;
+	int error;
+
+	error = request_firmware(&fw, PS_FW_NAME, dev);
+	if (error) {
+		dev_err(dev, "Unable to open firmware %s: %d\n",
+			PS_FW_NAME, error);
+		return error;
+	}
+
+	error = ps8640_load_fw(ps_bridge, fw);
+	if (error)
+		dev_err(dev, "The firmware update failed(%d)\n", error);
+	else
+		dev_info(dev, "The firmware update succeeded\n");
+
+	release_firmware(fw);
+	return error ? error : count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, ps8640_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, ps8640_hw_version_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, ps8640_update_fw_store);
+
+static struct attribute *ps8640_attrs[] = {
+	&dev_attr_fw_version.attr,
+	&dev_attr_hw_version.attr,
+	&dev_attr_update_fw.attr,
+	NULL
+};
+
+static const struct attribute_group ps8640_attr_group = {
+	.attrs = ps8640_attrs,
+};
+
+static void ps8640_remove_sysfs_group(void *data)
+{
+	struct ps8640 *ps_bridge = data;
+
+	sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
+}
+
+static int ps8640_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct ps8640 *ps_bridge;
+	struct device_node *np = dev->of_node;
+	struct device_node *port, *out_ep;
+	struct device_node *panel_node = NULL;
+	int ret;
+	u32 i;
+
+	ps_bridge = devm_kzalloc(dev, sizeof(*ps_bridge), GFP_KERNEL);
+	if (!ps_bridge)
+		return -ENOMEM;
+
+	/* port at 1 is ps8640 output port */
+	port = of_graph_get_port_by_id(np, 1);
+	if (port) {
+		out_ep = of_get_child_by_name(port, "endpoint");
+		of_node_put(port);
+		if (out_ep) {
+			panel_node = of_graph_get_remote_port_parent(out_ep);
+			of_node_put(out_ep);
+		}
+	}
+	if (panel_node) {
+		ps_bridge->panel = of_drm_find_panel(panel_node);
+		of_node_put(panel_node);
+		if (!ps_bridge->panel)
+			return -EPROBE_DEFER;
+	}
+
+	mutex_init(&ps_bridge->fw_mutex);
+	ps_bridge->supplies[0].supply = "vdd33";
+	ps_bridge->supplies[1].supply = "vdd12";
+	ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ps_bridge->supplies),
+				      ps_bridge->supplies);
+	if (ret) {
+		dev_info(dev, "failed to get regulators: %d\n", ret);
+		return ret;
+	}
+
+	ps_bridge->gpio_mode_sel_n = devm_gpiod_get_optional(&client->dev,
+							     "mode-sel",
+							     GPIOD_OUT_HIGH);
+	if (IS_ERR(ps_bridge->gpio_mode_sel_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_mode_sel_n);
+		dev_err(dev, "cannot get mode-sel %d\n", ret);
+		return ret;
+	}
+
+	ps_bridge->gpio_slp_n = devm_gpiod_get(&client->dev, "sleep",
+					       GPIOD_OUT_LOW);
+	if (IS_ERR(ps_bridge->gpio_slp_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_slp_n);
+		dev_err(dev, "cannot get sleep: %d\n", ret);
+		return ret;
+	}
+
+	/*
+	 * Request the reset pin low to avoid the bridge being
+	 * initialized prematurely
+	 */
+	ps_bridge->gpio_rst_n = devm_gpiod_get(&client->dev, "reset",
+					       GPIOD_OUT_LOW);
+	if (IS_ERR(ps_bridge->gpio_rst_n)) {
+		ret = PTR_ERR(ps_bridge->gpio_rst_n);
+		dev_err(dev, "cannot get reset: %d\n", ret);
+		return ret;
+	}
+
+	ps_bridge->bridge.funcs = &ps8640_bridge_funcs;
+	ps_bridge->bridge.of_node = dev->of_node;
+
+	ps_bridge->page[0] = client;
+	ps_bridge->ddc_i2c = i2c_new_dummy(client->adapter, EDID_I2C_ADDR);
+	if (!ps_bridge->ddc_i2c) {
+		dev_err(dev, "failed ddc_i2c dummy device, address%02x\n",
+			EDID_I2C_ADDR);
+		return -EBUSY;
+	}
+	/*
+	 * ps8640 uses multiple addresses, use dummy devices for them
+	 * page[0]: for DP control
+	 * page[1]: for VIDEO Bridge
+	 * page[2]: for control top
+	 * page[3]: for DSI Link Control1
+	 * page[4]: for MIPI Phy
+	 * page[5]: for VPLL
+	 * page[6]: for DSI Link Control2
+	 * page[7]: for spi rom mapping
+	 */
+	for (i = 1; i < MAX_DEVS; i++) {
+		ps_bridge->page[i] = i2c_new_dummy(client->adapter,
+						   client->addr + i);
+		if (!ps_bridge->page[i]) {
+			dev_err(dev, "failed i2c dummy device, address%02x\n",
+				client->addr + i);
+			ret = -EBUSY;
+			goto exit_dummy;
+		}
+	}
+	i2c_set_clientdata(client, ps_bridge);
+
+	ret = sysfs_create_group(&client->dev.kobj, &ps8640_attr_group);
+	if (ret) {
+		dev_err(dev, "failed to create sysfs entries: %d\n", ret);
+		goto exit_dummy;
+	}
+
+	ret = devm_add_action(dev, ps8640_remove_sysfs_group, ps_bridge);
+	if (ret) {
+		dev_err(dev, "failed to add sysfs cleanup action: %d\n", ret);
+		goto exit_remove_sysfs;
+	}
+
+	ret = drm_bridge_add(&ps_bridge->bridge);
+	if (ret) {
+		dev_err(dev, "Failed to add bridge: %d\n", ret);
+		goto exit_remove_sysfs;
+	}
+	return 0;
+
+exit_remove_sysfs:
+	sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
+exit_dummy:
+	while (--i)
+		i2c_unregister_device(ps_bridge->page[i]);
+	i2c_unregister_device(ps_bridge->ddc_i2c);
+	return ret;
+}
+
+static int ps8640_remove(struct i2c_client *client)
+{
+	struct ps8640 *ps_bridge = i2c_get_clientdata(client);
+	int i = MAX_DEVS;
+
+	drm_bridge_remove(&ps_bridge->bridge);
+	sysfs_remove_group(&ps_bridge->page[0]->dev.kobj, &ps8640_attr_group);
+	while (--i)
+		i2c_unregister_device(ps_bridge->page[i]);
+
+	i2c_unregister_device(ps_bridge->ddc_i2c);
+	return 0;
+}
+
+static const struct i2c_device_id ps8640_i2c_table[] = {
+	{ "parade,ps8640", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(i2c, ps8640_i2c_table);
+
+static const struct of_device_id ps8640_match[] = {
+	{ .compatible = "parade,ps8640" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ps8640_match);
+
+static struct i2c_driver ps8640_driver = {
+	.id_table = ps8640_i2c_table,
+	.probe = ps8640_probe,
+	.remove = ps8640_remove,
+	.driver = {
+		.name = "parade,ps8640",
+		.of_match_table = ps8640_match,
+	},
+};
+module_i2c_driver(ps8640_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi at mediatek.com>");
+MODULE_AUTHOR("CK Hu <ck.hu at mediatek.com>");
+MODULE_DESCRIPTION("PARADE ps8640 DSI-eDP converter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.9.5



From jitao.shi at mediatek.com  Thu Jun  2 02:57:24 2016
From: jitao.shi at mediatek.com (Jitao Shi)
Date: Thu, 2 Jun 2016 17:57:24 +0800
Subject: [PATCH 1/2 v16] Documentation: bridge: Add documentation for ps8640
 DT properties
Message-ID: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>

Add documentation for DT properties supported by
ps8640 DSI-eDP converter.

Signed-off-by: Jitao Shi <jitao.shi at mediatek.com>
Acked-by: Rob Herring <robh at kernel.org>
Reviewed-by: Philipp Zabel <p.zabel at pengutronix.de>
---
Changes since v15:
 - No change.

Changes since v14:
 - change mode-sel-gpios as optional.
---
 .../devicetree/bindings/display/bridge/ps8640.txt  |   44 ++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/bridge/ps8640.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/ps8640.txt b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
new file mode 100644
index 0000000..7b13f92
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/ps8640.txt
@@ -0,0 +1,44 @@
+ps8640-bridge bindings
+
+Required properties:
+	- compatible: "parade,ps8640"
+	- reg: first page address of the bridge.
+	- sleep-gpios: OF device-tree gpio specification for PD pin.
+	- reset-gpios: OF device-tree gpio specification for reset pin.
+	- vdd12-supply: OF device-tree regulator specification for 1.2V power.
+	- vdd33-supply: OF device-tree regulator specification for 3.3V power.
+	- ports: The device node can contain video interface port nodes per
+		 the video-interfaces bind[1]. For port at 0,set the reg = <0> as
+		 ps8640 dsi in and port at 1,set the reg = <1> as ps8640 eDP out.
+
+Optional properties:
+	- mode-sel-gpios: OF device-tree gpio specification for mode-sel pin.
+[1]: Documentation/devicetree/bindings/media/video-interfaces.txt
+
+Example:
+	edp-bridge at 18 {
+		compatible = "parade,ps8640";
+		reg = <0x18>;
+		sleep-gpios = <&pio 116 GPIO_ACTIVE_LOW>;
+		reset-gpios = <&pio 115 GPIO_ACTIVE_LOW>;
+		mode-sel-gpios = <&pio 92 GPIO_ACTIVE_HIGH>;
+		vdd12-supply = <&ps8640_fixed_1v2>;
+		vdd33-supply = <&mt6397_vgp2_reg>;
+
+		ports {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			port at 0 {
+				reg = <0>;
+				ps8640_in: endpoint {
+					remote-endpoint = <&dsi0_out>;
+				};
+			};
+			port at 1 {
+				reg = <1>;
+				ps8640_out: endpoint {
+					remote-endpoint = <&panel_in>;
+				};
+			};
+		};
+	};
-- 
1.7.9.5



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:30 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:30 +0200
Subject: [PATCH 03/20] drm: atmel-hlcdc: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-4-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index 39802c0..473a475 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -113,21 +113,9 @@ static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
 	return atmel_hlcdc_dc_mode_valid(rgb->dc, mode);
 }
 
-
-
-static struct drm_encoder *
-atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	return &rgb->encoder;
-}
-
 static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = {
 	.get_modes = atmel_hlcdc_panel_get_modes,
 	.mode_valid = atmel_hlcdc_rgb_mode_valid,
-	.best_encoder = atmel_hlcdc_rgb_best_encoder,
 };
 
 static enum drm_connector_status
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:28 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:28 +0200
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>

Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
that DRM drivers can leave this hook unassigned if they know they want
to use drm_atomic_helper_best_encoder().

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
 drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index f6a3350..849d029 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state *state,
 	if (funcs->atomic_best_encoder)
 		new_encoder = funcs->atomic_best_encoder(connector,
 							 connector_state);
-	else
+	else if (funcs->best_encoder)
 		new_encoder = funcs->best_encoder(connector);
+	else
+		new_encoder = drm_atomic_helper_best_encoder(connector);
 
 	if (!new_encoder) {
 		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 7c2eb75..d44389a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 		my_score++;
 
 	connector_funcs = connector->helper_private;
-	encoder = connector_funcs->best_encoder(connector);
+
+	/*
+	 * If the DRM device implements atomic hooks and ->best_encoder() is
+	 * NULL we fallback to the default drm_atomic_helper_best_encoder()
+	 * helper.
+	 */
+	if (fb_helper->dev->mode_config.funcs->atomic_commit &&
+	    !connector_funcs->best_encoder)
+		encoder = drm_atomic_helper_best_encoder(connector);
+	else
+		encoder = connector_funcs->best_encoder(connector);
+
 	if (!encoder)
 		goto out;
 
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:29 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:29 +0200
Subject: [PATCH 02/20] drm: arc: Rely on the default ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-3-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder(), and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/arc/arcpgu_hdmi.c | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
index 08b6bae..b7a8b2a 100644
--- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
+++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
@@ -46,23 +46,6 @@ static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
 	return sfuncs->get_modes(&slave->base, connector);
 }
 
-struct drm_encoder *
-arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
-{
-	struct drm_encoder_slave *slave;
-	struct arcpgu_drm_connector *con =
-		container_of(connector, struct arcpgu_drm_connector, connector);
-
-	slave = con->encoder_slave;
-	if (slave == NULL) {
-		dev_err(connector->dev->dev,
-			"connector_best_encoder: cannot find slave encoder for connector\n");
-		return NULL;
-	}
-
-	return &slave->base;
-}
-
 static enum drm_connector_status
 arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -97,7 +80,6 @@ static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
 static const struct drm_connector_helper_funcs
 arcpgu_drm_connector_helper_funcs = {
 	.get_modes = arcpgu_drm_connector_get_modes,
-	.best_encoder = arcpgu_drm_connector_best_encoder,
 };
 
 static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:31 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:31 +0200
Subject: [PATCH 04/20] drm: exynos: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-5-git-send-email-boris.brezillon@free-electrons.com>

We have 1:1 relationship between connectors and encoders and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder()
implementations and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dpi.c  | 9 ---------
 drivers/gpu/drm/exynos/exynos_drm_dsi.c  | 9 ---------
 drivers/gpu/drm/exynos/exynos_drm_vidi.c | 8 --------
 drivers/gpu/drm/exynos/exynos_hdmi.c     | 8 --------
 4 files changed, 34 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 5e38e74..ad6b73c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -93,17 +93,8 @@ static int exynos_dpi_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-exynos_dpi_best_encoder(struct drm_connector *connector)
-{
-	struct exynos_dpi *ctx = connector_to_dpi(connector);
-
-	return &ctx->encoder;
-}
-
 static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
 	.get_modes = exynos_dpi_get_modes,
-	.best_encoder = exynos_dpi_best_encoder,
 };
 
 static int exynos_dpi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 601ecf8..e07cb1f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1566,17 +1566,8 @@ static int exynos_dsi_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-exynos_dsi_best_encoder(struct drm_connector *connector)
-{
-	struct exynos_dsi *dsi = connector_to_dsi(connector);
-
-	return &dsi->encoder;
-}
-
 static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
 	.get_modes = exynos_dsi_get_modes,
-	.best_encoder = exynos_dsi_best_encoder,
 };
 
 static int exynos_dsi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 608b0af..e8f6c92 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -378,16 +378,8 @@ static int vidi_get_modes(struct drm_connector *connector)
 	return drm_add_edid_modes(connector, edid);
 }
 
-static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
-{
-	struct vidi_context *ctx = ctx_from_connector(connector);
-
-	return &ctx->encoder;
-}
-
 static const struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
 	.get_modes = vidi_get_modes,
-	.best_encoder = vidi_best_encoder,
 };
 
 static int vidi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 58de5a4..1625d7c 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -937,17 +937,9 @@ static int hdmi_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
-{
-	struct hdmi_context *hdata = connector_to_hdmi(connector);
-
-	return &hdata->encoder;
-}
-
 static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
 	.get_modes = hdmi_get_modes,
 	.mode_valid = hdmi_mode_valid,
-	.best_encoder = hdmi_best_encoder,
 };
 
 static int hdmi_create_connector(struct drm_encoder *encoder)
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:27 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:27 +0200
Subject: [PATCH 00/20] drm/atomic: Provide default ->best_encoder() behavior
Message-ID: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>

Hello,

This patch series aims at replacing all dummy ->best_encoder()
implementations where we have a 1:1 relationship between encoders
and connectors.
The core already provides the drm_atomic_helper_best_encoder()
function which is taking the first encoder attached to the
connector (after making sure only one encoder was attached to the
connector), but it's not automatically used, and drivers wanting
to rely on this default behavior have to explicitly assign their
->best_encoder() hook to drm_atomic_helper_best_encoder().

The first patch fixes remaining places where
drm_atomic_helper_best_encoder() should be called when ->best_encoder()
is NULL, so that drivers using the atomic helpers can get rid of the
explicit ->best_encoder assignment if they need to rely on the default
drm_atomic_helper_best_encoder() implementation.

The following patches are killing all open coded ->best_encoder()
implementations that could be replaced by
drm_atomic_helper_best_encoder().

All modifications have been compile tested except for the changed on
the intel driver.
I've also tested on an atmel board, but I recommend waiting for DRM
driver maintainers feedback before applying the associated changes.

Note that once patch 1 is applied, the other patches can be applied
independently.

Best Regards,

Boris 

Boris Brezillon (20):
  drm/atomic: Fix remaining places where !funcs->best_encoder is valid
  drm: arc: Rely on the default ->best_encoder() behavior
  drm: atmel-hlcdc: Rely on the default ->best_encoder() behavior
  drm: exynos: Rely on the default ->best_encoder() behavior
  drm: fsl-dcu: Rely on the default ->best_encoder() behavior
  drm: i915: Rely on the default ->best_encoder() behavior where
    appropriate
  drm: mediatek: Rely on the default ->best_encoder() behavior
  drm: msm: Rely on the default ->best_encoder() behavior where
    appropriate
  drm: rcar-du: Rely on the default ->best_encoder() behavior
  drm: rockchip: Rely on the default ->best_encoder() behavior
  drm: sti: Rely on the default ->best_encoder() behavior
  drm: sun4i: Rely on the default ->best_encoder() behavior
  drm: tegra: Rely on the default ->best_encoder() behavior
  drm: vc4: Rely on the default ->best_encoder() behavior
  drm: virtgpu: Rely on the default ->best_encoder() behavior
  drm: omap: Rely on the default ->best_encoder() behavior
  drm/bridge: anx78xx: Rely on the default ->best_encoder() behavior
  drm/bridge: ptn3460: Rely on the default ->best_encoder() behavior
  drm/bridge: ps8622: Rely on the default ->best_encoder() behavior
  drm/bridge: dw-hdmi: Use drm_atomic_helper_best_encoder()

 drivers/gpu/drm/arc/arcpgu_hdmi.c                  | 18 ------------------
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 12 ------------
 drivers/gpu/drm/bridge/analogix-anx78xx.c          |  8 --------
 drivers/gpu/drm/bridge/dw-hdmi.c                   | 11 +----------
 drivers/gpu/drm/bridge/nxp-ptn3460.c               |  8 --------
 drivers/gpu/drm/bridge/parade-ps8622.c             | 10 ----------
 drivers/gpu/drm/drm_atomic_helper.c                |  4 +++-
 drivers/gpu/drm/drm_fb_helper.c                    | 13 ++++++++++++-
 drivers/gpu/drm/exynos/exynos_drm_dpi.c            |  9 ---------
 drivers/gpu/drm/exynos/exynos_drm_dsi.c            |  9 ---------
 drivers/gpu/drm/exynos/exynos_drm_vidi.c           |  8 --------
 drivers/gpu/drm/exynos/exynos_hdmi.c               |  8 --------
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c          |  9 ---------
 drivers/gpu/drm/i915/intel_crt.c                   |  1 -
 drivers/gpu/drm/i915/intel_display.c               |  8 --------
 drivers/gpu/drm/i915/intel_dp.c                    |  1 -
 drivers/gpu/drm/i915/intel_drv.h                   |  1 -
 drivers/gpu/drm/i915/intel_dsi.c                   |  1 -
 drivers/gpu/drm/i915/intel_dvo.c                   |  1 -
 drivers/gpu/drm/i915/intel_hdmi.c                  |  1 -
 drivers/gpu/drm/i915/intel_lvds.c                  |  1 -
 drivers/gpu/drm/i915/intel_sdvo.c                  |  1 -
 drivers/gpu/drm/i915/intel_tv.c                    |  1 -
 drivers/gpu/drm/mediatek/mtk_dsi.c                 |  9 ---------
 drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
 drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
 drivers/gpu/drm/omapdrm/omap_connector.c           |  8 --------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c          | 12 ------------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h          |  3 ---
 drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c          |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c          |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c           |  1 -
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c             |  9 ---------
 drivers/gpu/drm/rockchip/inno_hdmi.c               |  9 ---------
 drivers/gpu/drm/sti/sti_dvo.c                      | 10 ----------
 drivers/gpu/drm/sti/sti_hda.c                      | 10 ----------
 drivers/gpu/drm/sti/sti_hdmi.c                     | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_rgb.c                  | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_tv.c                   |  9 ---------
 drivers/gpu/drm/tegra/drm.h                        |  2 --
 drivers/gpu/drm/tegra/dsi.c                        |  1 -
 drivers/gpu/drm/tegra/hdmi.c                       |  1 -
 drivers/gpu/drm/tegra/output.c                     |  8 --------
 drivers/gpu/drm/tegra/rgb.c                        |  1 -
 drivers/gpu/drm/tegra/sor.c                        |  1 -
 drivers/gpu/drm/vc4/vc4_dpi.c                      |  9 ---------
 drivers/gpu/drm/vc4/vc4_hdmi.c                     |  9 ---------
 drivers/gpu/drm/virtio/virtgpu_display.c           | 10 ----------
 49 files changed, 16 insertions(+), 299 deletions(-)

-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:34 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:34 +0200
Subject: [PATCH 07/20] drm: mediatek: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-8-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/mediatek/mtk_dsi.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 2d808e5..7343ffc 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -575,14 +575,6 @@ static int mtk_dsi_connector_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(dsi->panel);
 }
 
-static struct drm_encoder *mtk_dsi_connector_best_encoder(
-		struct drm_connector *connector)
-{
-	struct mtk_dsi *dsi = connector_to_dsi(connector);
-
-	return &dsi->encoder;
-}
-
 static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = {
 	.mode_fixup = mtk_dsi_encoder_mode_fixup,
 	.mode_set = mtk_dsi_encoder_mode_set,
@@ -603,7 +595,6 @@ static const struct drm_connector_funcs mtk_dsi_connector_funcs = {
 static const struct drm_connector_helper_funcs
 	mtk_dsi_connector_helper_funcs = {
 	.get_modes = mtk_dsi_connector_get_modes,
-	.best_encoder = mtk_dsi_connector_best_encoder,
 };
 
 static int mtk_drm_attach_bridge(struct drm_bridge *bridge,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:41 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:41 +0200
Subject: [PATCH 14/20] drm: vc4: Rely on the default ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-15-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders and
the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/vc4/vc4_dpi.c  | 9 ---------
 drivers/gpu/drm/vc4/vc4_hdmi.c | 9 ---------
 2 files changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 9817dbf..dba1114 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -208,14 +208,6 @@ static int vc4_dpi_connector_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-vc4_dpi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct vc4_dpi_connector *dpi_connector =
-		to_vc4_dpi_connector(connector);
-	return dpi_connector->encoder;
-}
-
 static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = vc4_dpi_connector_detect,
@@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
 
 static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = {
 	.get_modes = vc4_dpi_connector_get_modes,
-	.best_encoder = vc4_dpi_connector_best_encoder,
 };
 
 static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index fd2644d..68df91c 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -208,14 +208,6 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
 	return ret;
 }
 
-static struct drm_encoder *
-vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct vc4_hdmi_connector *hdmi_connector =
-		to_vc4_hdmi_connector(connector);
-	return hdmi_connector->encoder;
-}
-
 static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = vc4_hdmi_connector_detect,
@@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 
 static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
 	.get_modes = vc4_hdmi_connector_get_modes,
-	.best_encoder = vc4_hdmi_connector_best_encoder,
 };
 
 static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:32 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:32 +0200
Subject: [PATCH 05/20] drm: fsl-dcu: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-6-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 98c998d..0b0989e 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -102,14 +102,6 @@ static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = {
 	.reset = drm_atomic_helper_connector_reset,
 };
 
-static struct drm_encoder *
-fsl_dcu_drm_connector_best_encoder(struct drm_connector *connector)
-{
-	struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
-
-	return fsl_con->encoder;
-}
-
 static int fsl_dcu_drm_connector_get_modes(struct drm_connector *connector)
 {
 	struct fsl_dcu_drm_connector *fsl_connector;
@@ -136,7 +128,6 @@ static int fsl_dcu_drm_connector_mode_valid(struct drm_connector *connector,
 }
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.best_encoder = fsl_dcu_drm_connector_best_encoder,
 	.get_modes = fsl_dcu_drm_connector_get_modes,
 	.mode_valid = fsl_dcu_drm_connector_mode_valid,
 };
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:35 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:35 +0200
Subject: [PATCH 08/20] drm: msm: Rely on the default ->best_encoder() behavior
 where appropriate
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-9-git-send-email-boris.brezillon@free-electrons.com>

For all outputs except DSI we have a 1:1 relationship between connectors
and encoders and the driver is relying on the atomic helpers: we can
drop the custom ->best_encoder() and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
 drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
 3 files changed, 27 deletions(-)

diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c
index 72360cd..5960628 100644
--- a/drivers/gpu/drm/msm/edp/edp_connector.c
+++ b/drivers/gpu/drm/msm/edp/edp_connector.c
@@ -91,15 +91,6 @@ static int edp_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-edp_connector_best_encoder(struct drm_connector *connector)
-{
-	struct edp_connector *edp_connector = to_edp_connector(connector);
-
-	DBG("");
-	return edp_connector->edp->encoder;
-}
-
 static const struct drm_connector_funcs edp_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = edp_connector_detect,
@@ -113,7 +104,6 @@ static const struct drm_connector_funcs edp_connector_funcs = {
 static const struct drm_connector_helper_funcs edp_connector_helper_funcs = {
 	.get_modes = edp_connector_get_modes,
 	.mode_valid = edp_connector_mode_valid,
-	.best_encoder = edp_connector_best_encoder,
 };
 
 /* initialize connector */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
index b15d726..a2515b4 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -406,13 +406,6 @@ static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return 0;
 }
 
-static struct drm_encoder *
-msm_hdmi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
-	return hdmi_connector->hdmi->encoder;
-}
-
 static const struct drm_connector_funcs hdmi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = hdmi_connector_detect,
@@ -426,7 +419,6 @@ static const struct drm_connector_funcs hdmi_connector_funcs = {
 static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
 	.get_modes = msm_hdmi_connector_get_modes,
 	.mode_valid = msm_hdmi_connector_mode_valid,
-	.best_encoder = msm_hdmi_connector_best_encoder,
 };
 
 /* initialize connector */
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
index 2648cd7..353429b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
@@ -90,14 +90,6 @@ static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
-{
-	struct mdp4_lvds_connector *mdp4_lvds_connector =
-			to_mdp4_lvds_connector(connector);
-	return mdp4_lvds_connector->encoder;
-}
-
 static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = mdp4_lvds_connector_detect,
@@ -111,7 +103,6 @@ static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
 static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
 	.get_modes = mdp4_lvds_connector_get_modes,
 	.mode_valid = mdp4_lvds_connector_mode_valid,
-	.best_encoder = mdp4_lvds_connector_best_encoder,
 };
 
 /* initialize connector */
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:36 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:36 +0200
Subject: [PATCH 09/20] drm: rcar-du: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-10-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders,
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ------------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h |  3 ---
 drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c  |  1 -
 5 files changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 4e939e4..55149e9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -27,18 +27,6 @@
 #include "rcar_du_vgacon.h"
 
 /* -----------------------------------------------------------------------------
- * Common connector functions
- */
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector)
-{
-	struct rcar_du_connector *rcon = to_rcar_connector(connector);
-
-	return rcar_encoder_to_drm_encoder(rcon->encoder);
-}
-
-/* -----------------------------------------------------------------------------
  * Encoder
  */
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 719b6f2a..a8669c3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -49,9 +49,6 @@ struct rcar_du_connector {
 #define to_rcar_connector(c) \
 	container_of(c, struct rcar_du_connector, connector)
 
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector);
-
 int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 			 enum rcar_du_encoder_type type,
 			 enum rcar_du_output output,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
index 6c92714..612b4d5 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
@@ -52,7 +52,6 @@ static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_hdmi_connector_get_modes,
 	.mode_valid = rcar_du_hdmi_connector_mode_valid,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index e905f5d..6afd0af 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -59,7 +59,6 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_lvds_connector_get_modes,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 9d7e5c9..68f7ffa 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -28,7 +28,6 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_vga_connector_get_modes,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:38 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:38 +0200
Subject: [PATCH 11/20] drm: sti: Rely on the default ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-12-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/sti/sti_dvo.c  | 10 ----------
 drivers/gpu/drm/sti/sti_hda.c  | 10 ----------
 drivers/gpu/drm/sti/sti_hdmi.c | 10 ----------
 3 files changed, 30 deletions(-)

diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index 25f7663..d5627d1 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -384,20 +384,10 @@ static int sti_dvo_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_dvo_best_encoder(struct drm_connector *connector)
-{
-	struct sti_dvo_connector *dvo_connector
-		= to_sti_dvo_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return dvo_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = {
 	.get_modes = sti_dvo_connector_get_modes,
 	.mode_valid = sti_dvo_connector_mode_valid,
-	.best_encoder = sti_dvo_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index f7d3464..c4649f5 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -676,20 +676,10 @@ static int sti_hda_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector)
-{
-	struct sti_hda_connector *hda_connector
-		= to_sti_hda_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return hda_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = {
 	.get_modes = sti_hda_connector_get_modes,
 	.mode_valid = sti_hda_connector_mode_valid,
-	.best_encoder = sti_hda_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index 6ef0715..dc9ab6e 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -897,20 +897,10 @@ static int sti_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector)
-{
-	struct sti_hdmi_connector *hdmi_connector
-		= to_sti_hdmi_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return hdmi_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = {
 	.get_modes = sti_hdmi_connector_get_modes,
 	.mode_valid = sti_hdmi_connector_mode_valid,
-	.best_encoder = sti_hdmi_best_encoder,
 };
 
 /* get detection status of display device */
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:40 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:40 +0200
Subject: [PATCH 13/20] drm: tegra: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-14-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/tegra/drm.h    | 2 --
 drivers/gpu/drm/tegra/dsi.c    | 1 -
 drivers/gpu/drm/tegra/hdmi.c   | 1 -
 drivers/gpu/drm/tegra/output.c | 8 --------
 drivers/gpu/drm/tegra/rgb.c    | 1 -
 drivers/gpu/drm/tegra/sor.c    | 1 -
 6 files changed, 14 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index f52d6cb2..0ddcce1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -239,8 +239,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 void tegra_output_exit(struct tegra_output *output);
 
 int tegra_output_connector_get_modes(struct drm_connector *connector);
-struct drm_encoder *
-tegra_output_connector_best_encoder(struct drm_connector *connector);
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force);
 void tegra_output_connector_destroy(struct drm_connector *connector);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index d1239eb..099cccb 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -794,7 +794,6 @@ tegra_dsi_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_dsi_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index b7ef492..2fdb879 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -806,7 +806,6 @@ static const struct drm_connector_helper_funcs
 tegra_hdmi_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_hdmi_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 46664b6..1480f6a 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -42,14 +42,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 	return err;
 }
 
-struct drm_encoder *
-tegra_output_connector_best_encoder(struct drm_connector *connector)
-{
-	struct tegra_output *output = connector_to_output(connector);
-
-	return &output->encoder;
-}
-
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index e246334..a131b44 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -112,7 +112,6 @@ tegra_rgb_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_rgb_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 757c6e8..34958d7 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1087,7 +1087,6 @@ tegra_sor_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
 	.get_modes = tegra_sor_connector_get_modes,
 	.mode_valid = tegra_sor_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:45 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:45 +0200
Subject: [PATCH 18/20] drm/bridge: ptn3460: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-19-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/nxp-ptn3460.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
index 7ecd59f..93f3dac 100644
--- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
+++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
@@ -235,16 +235,8 @@ out:
 	return num_modes;
 }
 
-static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
-{
-	struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
-
-	return ptn_bridge->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
 	.get_modes = ptn3460_get_modes,
-	.best_encoder = ptn3460_best_encoder,
 };
 
 static enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:37 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:37 +0200
Subject: [PATCH 10/20] drm: rockchip: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-11-git-send-email-boris.brezillon@free-electrons.com>

All outputss have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations  and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 9 ---------
 drivers/gpu/drm/rockchip/inno_hdmi.c   | 9 ---------
 2 files changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index dedc65b..ca22e5e 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -964,18 +964,9 @@ static enum drm_mode_status dw_mipi_dsi_mode_valid(
 	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
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index f8b4feb..006260d 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -579,14 +579,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
 	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)
@@ -613,7 +605,6 @@ static struct drm_connector_funcs inno_hdmi_connector_funcs = {
 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)
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:42 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:42 +0200
Subject: [PATCH 15/20] drm: virtgpu: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-16-git-send-email-boris.brezillon@free-electrons.com>

The virtgpu output exposes a 1:1 relationship between connectors and
encoders and the driver is relying on the atomic helpers: we can drop
the custom ->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/virtio/virtgpu_display.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index d4305da..4d41dcb 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -341,15 +341,6 @@ static int virtio_gpu_conn_mode_valid(struct drm_connector *connector,
 	return MODE_BAD;
 }
 
-static struct drm_encoder*
-virtio_gpu_best_encoder(struct drm_connector *connector)
-{
-	struct virtio_gpu_output *virtio_gpu_output =
-		drm_connector_to_virtio_gpu_output(connector);
-
-	return &virtio_gpu_output->enc;
-}
-
 static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
 	.mode_set   = virtio_gpu_enc_mode_set,
 	.enable     = virtio_gpu_enc_enable,
@@ -359,7 +350,6 @@ static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
 static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
 	.get_modes    = virtio_gpu_conn_get_modes,
 	.mode_valid   = virtio_gpu_conn_mode_valid,
-	.best_encoder = virtio_gpu_best_encoder,
 };
 
 static enum drm_connector_status virtio_gpu_conn_detect(
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:43 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:43 +0200
Subject: [PATCH 16/20] drm: omap: Rely on the default ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-17-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/omapdrm/omap_connector.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index ce2d67b..80af5e1 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -256,13 +256,6 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
 	return ret;
 }
 
-struct drm_encoder *omap_connector_attached_encoder(
-		struct drm_connector *connector)
-{
-	struct omap_connector *omap_connector = to_omap_connector(connector);
-	return omap_connector->encoder;
-}
-
 static const struct drm_connector_funcs omap_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.reset = drm_atomic_helper_connector_reset,
@@ -276,7 +269,6 @@ static const struct drm_connector_funcs omap_connector_funcs = {
 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
 	.get_modes = omap_connector_get_modes,
 	.mode_valid = omap_connector_mode_valid,
-	.best_encoder = omap_connector_attached_encoder,
 };
 
 /* initialize connector */
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:39 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:39 +0200
Subject: [PATCH 12/20] drm: sun4i: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-13-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/sun4i/sun4i_rgb.c | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_tv.c  |  9 ---------
 2 files changed, 19 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index ab64948..442cfe2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -90,19 +90,9 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-sun4i_rgb_best_encoder(struct drm_connector *connector)
-{
-	struct sun4i_rgb *rgb =
-		drm_connector_to_sun4i_rgb(connector);
-
-	return &rgb->encoder;
-}
-
 static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
 	.get_modes	= sun4i_rgb_get_modes,
 	.mode_valid	= sun4i_rgb_mode_valid,
-	.best_encoder	= sun4i_rgb_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index bc047f9..b841478 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -526,18 +526,9 @@ static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-sun4i_tv_comp_best_encoder(struct drm_connector *connector)
-{
-	struct sun4i_tv *tv = drm_connector_to_sun4i_tv(connector);
-
-	return &tv->encoder;
-}
-
 static struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
 	.get_modes	= sun4i_tv_comp_get_modes,
 	.mode_valid	= sun4i_tv_comp_mode_valid,
-	.best_encoder	= sun4i_tv_comp_best_encoder,
 };
 
 static enum drm_connector_status
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:33 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:33 +0200
Subject: [PATCH 06/20] drm: i915: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-7-git-send-email-boris.brezillon@free-electrons.com>

For all outputs except dp_mst, we have a 1:1 relationship between
connectors and encoders and the driver is relying on the atomic helpers:
we can drop the custom ->best_encoder() implementation and let the core
call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/i915/intel_crt.c     | 1 -
 drivers/gpu/drm/i915/intel_display.c | 8 --------
 drivers/gpu/drm/i915/intel_dp.c      | 1 -
 drivers/gpu/drm/i915/intel_drv.h     | 1 -
 drivers/gpu/drm/i915/intel_dsi.c     | 1 -
 drivers/gpu/drm/i915/intel_dvo.c     | 1 -
 drivers/gpu/drm/i915/intel_hdmi.c    | 1 -
 drivers/gpu/drm/i915/intel_lvds.c    | 1 -
 drivers/gpu/drm/i915/intel_sdvo.c    | 1 -
 drivers/gpu/drm/i915/intel_tv.c      | 1 -
 10 files changed, 17 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 3fbb6fc..bd0cd68 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -753,7 +753,6 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
 	.mode_valid = intel_crt_mode_valid,
 	.get_modes = intel_crt_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_crt_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2113f40..77026ce 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -16113,14 +16113,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
 	intel_teardown_gmbus(dev);
 }
 
-/*
- * Return which encoder is currently attached for connector.
- */
-struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
-{
-	return &intel_attached_encoder(connector)->base;
-}
-
 void intel_connector_attach_encoder(struct intel_connector *connector,
 				    struct intel_encoder *encoder)
 {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index f192f58..21b2833 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4935,7 +4935,6 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
 	.get_modes = intel_dp_get_modes,
 	.mode_valid = intel_dp_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_dp_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a28b4aa..79a4d6b 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1128,7 +1128,6 @@ struct intel_connector *intel_connector_alloc(void);
 bool intel_connector_get_hw_state(struct intel_connector *connector);
 void intel_connector_attach_encoder(struct intel_connector *connector,
 				    struct intel_encoder *encoder);
-struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
 struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
 					     struct drm_crtc *crtc);
 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 366ad6c..ec51952 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -1378,7 +1378,6 @@ static const struct drm_encoder_funcs intel_dsi_funcs = {
 static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = {
 	.get_modes = intel_dsi_get_modes,
 	.mode_valid = intel_dsi_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_connector_funcs intel_dsi_connector_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 286baec..34b7e3f 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -351,7 +351,6 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
 	.mode_valid = intel_dvo_mode_valid,
 	.get_modes = intel_dvo_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 2c3bd9c..aef4bc8 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2114,7 +2114,6 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
 	.get_modes = intel_hdmi_get_modes,
 	.mode_valid = intel_hdmi_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index bc53c0d..d4faa5a 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -547,7 +547,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
 	.get_modes = intel_lvds_get_modes,
 	.mode_valid = intel_lvds_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_connector_funcs intel_lvds_connector_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 2128fae..5cd48ff 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2191,7 +2191,6 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
 	.get_modes = intel_sdvo_get_modes,
 	.mode_valid = intel_sdvo_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 223129d..47fe241 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1512,7 +1512,6 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
 	.mode_valid = intel_tv_mode_valid,
 	.get_modes = intel_tv_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_tv_enc_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:44 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:44 +0200
Subject: [PATCH 17/20] drm/bridge: anx78xx: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-18-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/analogix-anx78xx.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
index d087b05..f9f03bc 100644
--- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
@@ -986,16 +986,8 @@ unlock:
 	return num_modes;
 }
 
-static struct drm_encoder *anx78xx_best_encoder(struct drm_connector *connector)
-{
-	struct anx78xx *anx78xx = connector_to_anx78xx(connector);
-
-	return anx78xx->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs anx78xx_connector_helper_funcs = {
 	.get_modes = anx78xx_get_modes,
-	.best_encoder = anx78xx_best_encoder,
 };
 
 static enum drm_connector_status anx78xx_detect(struct drm_connector *connector,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:46 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:46 +0200
Subject: [PATCH 19/20] drm/bridge: ps8622: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-20-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/parade-ps8622.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
index be881e9..5cd8dd7 100644
--- a/drivers/gpu/drm/bridge/parade-ps8622.c
+++ b/drivers/gpu/drm/bridge/parade-ps8622.c
@@ -474,18 +474,8 @@ static int ps8622_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(ps8622->panel);
 }
 
-static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
-{
-	struct ps8622_bridge *ps8622;
-
-	ps8622 = connector_to_ps8622(connector);
-
-	return ps8622->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
 	.get_modes = ps8622_get_modes,
-	.best_encoder = ps8622_best_encoder,
 };
 
 static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Thu Jun  2 07:31:47 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Thu,  2 Jun 2016 16:31:47 +0200
Subject: [PATCH 20/20] drm/bridge: dw-hdmi: Use
 drm_atomic_helper_best_encoder()
In-Reply-To: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1464877907-27723-21-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, which means
we can rely on the drm_atomic_helper_best_encoder() behavior.

We still have to explicitly assign ->best_encoder() to
drm_atomic_helper_best_encoder(), because the automated fallback to
drm_atomic_helper_best_encoder() when ->best_encoder() is NULL is only
available when the DRM device is using the atomic helpers, and this bridge
is compatible with non-atomic and atomic devices.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/dw-hdmi.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index c9d9412..70b1f7d 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1476,15 +1476,6 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return mode_status;
 }
 
-static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector
-							   *connector)
-{
-	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-					     connector);
-
-	return hdmi->encoder;
-}
-
 static void dw_hdmi_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
@@ -1525,7 +1516,7 @@ static const struct drm_connector_funcs dw_hdmi_atomic_connector_funcs = {
 static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
 	.get_modes = dw_hdmi_connector_get_modes,
 	.mode_valid = dw_hdmi_connector_mode_valid,
-	.best_encoder = dw_hdmi_connector_best_encoder,
+	.best_encoder = drm_atomic_helper_best_encoder,
 };
 
 static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
-- 
2.7.4



From k.kozlowski at samsung.com  Thu Jun  2 08:39:02 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:02 +0200
Subject: [RFC v3 00/45] dma-mapping: Use unsigned long for dma_attrs
Message-ID: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>

Hi,


This is third approach (complete this time) for replacing struct
dma_attrs with unsigned long.

The main patch (2/45) doing the change is split into many subpatches
for easier review (3-43).  They should be squashed together when
applying.


*Important:* Patchset is *only* build tested on allyesconfigs: ARM,
ARM64, i386, x86_64 and powerpc. Please provide reviewes and tests
for other platforms.

Rebased on next-20160602.

For easier testing the patchset is available here:
repo:   https://github.com/krzk/linux
branch: for-next/dma-attrs-const-v3


I didn't CC all the maintainers... the list is too big.

Changes since v2
================
1. Follow Christoph Hellwig's comments (don't use BIT add
   documentation, remove dma_get_attr).


Rationale
=========
The dma-mapping core and the implementations do not change the
DMA attributes passed by pointer.  Thus the pointer can point to const
data.  However the attributes do not have to be a bitfield. Instead
unsigned long will do fine:

1. This is just simpler.  Both in terms of reading the code and setting
   attributes.  Instead of initializing local attributes on the stack
   and passing pointer to it to dma_set_attr(), just set the bits.

2. It brings safeness and checking for const correctness because the
   attributes are passed by value.


Best regards,
Krzysztof


Krzysztof Kozlowski (45):
  powerpc: dma-mapping: Don't hard-code the value of
    DMA_ATTR_WEAK_ORDERING
  dma-mapping: Use unsigned long for dma_attrs
  alpha: dma-mapping: Use unsigned long for dma_attrs
  arc: dma-mapping: Use unsigned long for dma_attrs
  ARM: dma-mapping: Use unsigned long for dma_attrs
  arm64: dma-mapping: Use unsigned long for dma_attrs
  avr32: dma-mapping: Use unsigned long for dma_attrs
  blackfin: dma-mapping: Use unsigned long for dma_attrs
  c6x: dma-mapping: Use unsigned long for dma_attrs
  cris: dma-mapping: Use unsigned long for dma_attrs
  frv: dma-mapping: Use unsigned long for dma_attrs
  drm/exynos: dma-mapping: Use unsigned long for dma_attrs
  drm/mediatek: dma-mapping: Use unsigned long for dma_attrs
  drm/msm: dma-mapping: Use unsigned long for dma_attrs
  drm/nouveau: dma-mapping: Use unsigned long for dma_attrs
  drm/rockship: dma-mapping: Use unsigned long for dma_attrs
  infiniband: dma-mapping: Use unsigned long for dma_attrs
  iommu: dma-mapping: Use unsigned long for dma_attrs
  [media] dma-mapping: Use unsigned long for dma_attrs
  xen: dma-mapping: Use unsigned long for dma_attrs
  swiotlb: dma-mapping: Use unsigned long for dma_attrs
  powerpc: dma-mapping: Use unsigned long for dma_attrs
  video: dma-mapping: Use unsigned long for dma_attrs
  x86: dma-mapping: Use unsigned long for dma_attrs
  iommu: intel: dma-mapping: Use unsigned long for dma_attrs
  h8300: dma-mapping: Use unsigned long for dma_attrs
  hexagon: dma-mapping: Use unsigned long for dma_attrs
  ia64: dma-mapping: Use unsigned long for dma_attrs
  m68k: dma-mapping: Use unsigned long for dma_attrs
  metag: dma-mapping: Use unsigned long for dma_attrs
  microblaze: dma-mapping: Use unsigned long for dma_attrs
  mips: dma-mapping: Use unsigned long for dma_attrs
  mn10300: dma-mapping: Use unsigned long for dma_attrs
  nios2: dma-mapping: Use unsigned long for dma_attrs
  openrisc: dma-mapping: Use unsigned long for dma_attrs
  parisc: dma-mapping: Use unsigned long for dma_attrs
  misc: mic: dma-mapping: Use unsigned long for dma_attrs
  s390: dma-mapping: Use unsigned long for dma_attrs
  sh: dma-mapping: Use unsigned long for dma_attrs
  sparc: dma-mapping: Use unsigned long for dma_attrs
  tile: dma-mapping: Use unsigned long for dma_attrs
  unicore32: dma-mapping: Use unsigned long for dma_attrs
  xtensa: dma-mapping: Use unsigned long for dma_attrs
  dma-mapping: Remove dma_get_attr
  dma-mapping: Document the DMA attributes right in declaration

 Documentation/DMA-API.txt                          |  33 +++---
 Documentation/DMA-attributes.txt                   |   2 +-
 arch/alpha/include/asm/dma-mapping.h               |   2 -
 arch/alpha/kernel/pci-noop.c                       |   2 +-
 arch/alpha/kernel/pci_iommu.c                      |  12 +-
 arch/arc/mm/dma.c                                  |  12 +-
 arch/arm/common/dmabounce.c                        |   4 +-
 arch/arm/include/asm/dma-mapping.h                 |  13 +--
 arch/arm/include/asm/xen/page-coherent.h           |  16 +--
 arch/arm/mm/dma-mapping.c                          | 117 +++++++++----------
 arch/arm/xen/mm.c                                  |   8 +-
 arch/arm64/mm/dma-mapping.c                        |  67 +++++------
 arch/avr32/mm/dma-coherent.c                       |  12 +-
 arch/blackfin/kernel/dma-mapping.c                 |   8 +-
 arch/c6x/include/asm/dma-mapping.h                 |   4 +-
 arch/c6x/kernel/dma.c                              |   9 +-
 arch/c6x/mm/dma-coherent.c                         |   4 +-
 arch/cris/arch-v32/drivers/pci/dma.c               |   9 +-
 arch/frv/mb93090-mb00/pci-dma-nommu.c              |   8 +-
 arch/frv/mb93090-mb00/pci-dma.c                    |   9 +-
 arch/h8300/kernel/dma.c                            |   8 +-
 arch/hexagon/include/asm/dma-mapping.h             |   1 -
 arch/hexagon/kernel/dma.c                          |   8 +-
 arch/ia64/hp/common/sba_iommu.c                    |  22 ++--
 arch/ia64/include/asm/machvec.h                    |   1 -
 arch/ia64/kernel/pci-swiotlb.c                     |   4 +-
 arch/ia64/sn/pci/pci_dma.c                         |  22 ++--
 arch/m68k/kernel/dma.c                             |  12 +-
 arch/metag/kernel/dma.c                            |  16 +--
 arch/microblaze/include/asm/dma-mapping.h          |   1 -
 arch/microblaze/kernel/dma.c                       |  12 +-
 arch/mips/cavium-octeon/dma-octeon.c               |   8 +-
 arch/mips/loongson64/common/dma-swiotlb.c          |  10 +-
 arch/mips/mm/dma-default.c                         |  20 ++--
 arch/mips/netlogic/common/nlm-dma.c                |   4 +-
 arch/mn10300/mm/dma-alloc.c                        |   8 +-
 arch/nios2/mm/dma-mapping.c                        |  12 +-
 arch/openrisc/kernel/dma.c                         |  21 ++--
 arch/parisc/kernel/pci-dma.c                       |  18 +--
 arch/powerpc/include/asm/dma-mapping.h             |   7 +-
 arch/powerpc/include/asm/iommu.h                   |  10 +-
 arch/powerpc/kernel/dma-iommu.c                    |  12 +-
 arch/powerpc/kernel/dma.c                          |  18 +--
 arch/powerpc/kernel/ibmebus.c                      |  12 +-
 arch/powerpc/kernel/iommu.c                        |  12 +-
 arch/powerpc/kernel/vio.c                          |  12 +-
 arch/powerpc/platforms/cell/iommu.c                |  26 ++---
 arch/powerpc/platforms/pasemi/iommu.c              |   2 +-
 arch/powerpc/platforms/powernv/npu-dma.c           |   8 +-
 arch/powerpc/platforms/powernv/pci-ioda.c          |   4 +-
 arch/powerpc/platforms/powernv/pci.c               |   2 +-
 arch/powerpc/platforms/powernv/pci.h               |   2 +-
 arch/powerpc/platforms/ps3/system-bus.c            |  18 +--
 arch/powerpc/platforms/pseries/iommu.c             |   6 +-
 arch/powerpc/sysdev/dart_iommu.c                   |   2 +-
 arch/s390/include/asm/dma-mapping.h                |   1 -
 arch/s390/pci/pci_dma.c                            |  23 ++--
 arch/sh/include/asm/dma-mapping.h                  |   4 +-
 arch/sh/kernel/dma-nommu.c                         |   4 +-
 arch/sh/mm/consistent.c                            |   4 +-
 arch/sparc/kernel/iommu.c                          |  12 +-
 arch/sparc/kernel/ioport.c                         |  24 ++--
 arch/sparc/kernel/pci_sun4v.c                      |  12 +-
 arch/tile/kernel/pci-dma.c                         |  28 ++---
 arch/unicore32/mm/dma-swiotlb.c                    |   4 +-
 arch/x86/include/asm/dma-mapping.h                 |   5 +-
 arch/x86/include/asm/swiotlb.h                     |   4 +-
 arch/x86/include/asm/xen/page-coherent.h           |   9 +-
 arch/x86/kernel/amd_gart_64.c                      |  20 ++--
 arch/x86/kernel/pci-calgary_64.c                   |  14 +--
 arch/x86/kernel/pci-dma.c                          |   4 +-
 arch/x86/kernel/pci-nommu.c                        |   4 +-
 arch/x86/kernel/pci-swiotlb.c                      |   4 +-
 arch/x86/pci/sta2x11-fixup.c                       |   2 +-
 arch/x86/pci/vmd.c                                 |  16 +--
 arch/xtensa/kernel/pci-dma.c                       |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c          |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_gem.c            |  20 ++--
 drivers/gpu/drm/exynos/exynos_drm_gem.h            |   2 +-
 drivers/gpu/drm/mediatek/mtk_drm_gem.c             |  13 +--
 drivers/gpu/drm/mediatek/mtk_drm_gem.h             |   2 +-
 drivers/gpu/drm/msm/msm_drv.c                      |  13 +--
 .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c    |  13 +--
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c        |  17 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h        |   2 +-
 drivers/infiniband/core/umem.c                     |   7 +-
 drivers/iommu/amd_iommu.c                          |  12 +-
 drivers/iommu/dma-iommu.c                          |   8 +-
 drivers/iommu/intel-iommu.c                        |  12 +-
 drivers/media/platform/sti/bdisp/bdisp-hw.c        |  26 ++---
 drivers/media/v4l2-core/videobuf2-dma-contig.c     |  30 ++---
 drivers/media/v4l2-core/videobuf2-dma-sg.c         |  19 +--
 drivers/misc/mic/host/mic_boot.c                   |  20 ++--
 drivers/parisc/ccio-dma.c                          |  16 +--
 drivers/parisc/sba_iommu.c                         |  16 +--
 drivers/video/fbdev/omap2/omapfb/omapfb-main.c     |  12 +-
 drivers/video/fbdev/omap2/omapfb/omapfb.h          |   3 +-
 drivers/xen/swiotlb-xen.c                          |  14 +--
 include/linux/dma-attrs.h                          |  71 ------------
 include/linux/dma-iommu.h                          |   6 +-
 include/linux/dma-mapping.h                        | 128 ++++++++++++++-------
 include/linux/swiotlb.h                            |  10 +-
 include/media/videobuf2-dma-contig.h               |   7 +-
 include/rdma/ib_verbs.h                            |   8 +-
 include/xen/swiotlb-xen.h                          |  12 +-
 lib/dma-noop.c                                     |   9 +-
 lib/swiotlb.c                                      |  13 ++-
 108 files changed, 689 insertions(+), 788 deletions(-)
 delete mode 100644 include/linux/dma-attrs.h

-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:03 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:03 +0200
Subject: [RFC v3 01/45] powerpc: dma-mapping: Don't hard-code the value of
 DMA_ATTR_WEAK_ORDERING
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-2-git-send-email-k.kozlowski@samsung.com>

Hard-coded value of DMA_ATTR_WEAK_ORDERING is then compared with the
symbol.  This will stop matching if the value of symbol is changed (when
switching DMA attributes to unsigned long).

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/powerpc/platforms/cell/iommu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 14a582b21274..0c2794d2b6c0 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -1162,7 +1162,7 @@ static int __init setup_iommu_fixed(char *str)
 	pciep = of_find_node_by_type(NULL, "pcie-endpoint");
 
 	if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0))
-		iommu_fixed_is_weak = 1;
+		iommu_fixed_is_weak = DMA_ATTR_WEAK_ORDERING;
 
 	of_node_put(pciep);
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:06 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:06 +0200
Subject: [RFC v3 04/45] arc: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-5-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/arc/mm/dma.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 73d7e4c75b7d..3d1f467d1792 100644
--- a/arch/arc/mm/dma.c
+++ b/arch/arc/mm/dma.c
@@ -22,7 +22,7 @@
 
 
 static void *arc_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	unsigned long order = get_order(size);
 	struct page *page;
@@ -90,7 +90,7 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
 }
 
 static void arc_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct page *page = virt_to_page(dma_handle);
 	int is_non_coh = 1;
@@ -129,7 +129,7 @@ static void _dma_cache_sync(phys_addr_t paddr, size_t size,
 
 static dma_addr_t arc_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	phys_addr_t paddr = page_to_phys(page) + offset;
 	_dma_cache_sync(paddr, size, dir);
@@ -137,7 +137,7 @@ static dma_addr_t arc_dma_map_page(struct device *dev, struct page *page,
 }
 
 static int arc_dma_map_sg(struct device *dev, struct scatterlist *sg,
-	   int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+	   int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:04 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:04 +0200
Subject: [RFC v3 02/45] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-3-git-send-email-k.kozlowski@samsung.com>

The dma-mapping core and the implementations do not change the
DMA attributes passed by pointer.  Thus the pointer can point to const
data.  However the attributes do not have to be a bitfield. Instead
unsigned long will do fine:

1. This is just simpler.  Both in terms of reading the code and setting
   attributes.  Instead of initializing local attributes on the stack
   and passing pointer to it to dma_set_attr(), just set the bits.

2. It brings safeness and checking for const correctness because the
   attributes are passed by value.

Semantic patches for this change (at least most of them):
===
virtual patch
virtual context

@r@
identifier f, attrs;

@@
f(...,
- struct dma_attrs *attrs
+ unsigned long attrs
, ...)
{
...
}

@@
identifier r.f;
@@
f(...,
- NULL
+ 0
 )
===
// Options: --all-includes
virtual patch
virtual context

@r@
identifier f, attrs;
type t;

@@
t f(..., struct dma_attrs *attrs);

@@
identifier r.f;
@@
f(...,
- NULL
+ 0
 )
===

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 Documentation/DMA-API.txt        |  29 +++++------
 Documentation/DMA-attributes.txt |   2 +-
 include/linux/dma-attrs.h        |  71 -------------------------
 include/linux/dma-mapping.h      | 108 +++++++++++++++++++++++----------------
 lib/dma-noop.c                   |   9 ++--
 5 files changed, 83 insertions(+), 136 deletions(-)
 delete mode 100644 include/linux/dma-attrs.h

diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt
index 45ef3f279c3b..24f9688bb98a 100644
--- a/Documentation/DMA-API.txt
+++ b/Documentation/DMA-API.txt
@@ -369,35 +369,32 @@ See also dma_map_single().
 dma_addr_t
 dma_map_single_attrs(struct device *dev, void *cpu_addr, size_t size,
 		     enum dma_data_direction dir,
-		     struct dma_attrs *attrs)
+		     unsigned long attrs)
 
 void
 dma_unmap_single_attrs(struct device *dev, dma_addr_t dma_addr,
 		       size_t size, enum dma_data_direction dir,
-		       struct dma_attrs *attrs)
+		       unsigned long attrs)
 
 int
 dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 		 int nents, enum dma_data_direction dir,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 
 void
 dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
 		   int nents, enum dma_data_direction dir,
-		   struct dma_attrs *attrs)
+		   unsigned long attrs)
 
 The four functions above are just like the counterpart functions
 without the _attrs suffixes, except that they pass an optional
-struct dma_attrs*.
-
-struct dma_attrs encapsulates a set of "DMA attributes". For the
-definition of struct dma_attrs see linux/dma-attrs.h.
+dma_attrs.
 
 The interpretation of DMA attributes is architecture-specific, and
 each attribute should be documented in Documentation/DMA-attributes.txt.
 
-If struct dma_attrs* is NULL, the semantics of each of these
-functions is identical to those of the corresponding function
+If dma_attrs are 0, the semantics of each of these functions
+is identical to those of the corresponding function
 without the _attrs suffix. As a result dma_map_single_attrs()
 can generally replace dma_map_single(), etc.
 
@@ -405,15 +402,15 @@ As an example of the use of the *_attrs functions, here's how
 you could pass an attribute DMA_ATTR_FOO when mapping memory
 for DMA:
 
-#include <linux/dma-attrs.h>
-/* DMA_ATTR_FOO should be defined in linux/dma-attrs.h and
+#include <linux/dma-mapping.h>
+/* DMA_ATTR_FOO should be defined in linux/dma-mapping.h and
  * documented in Documentation/DMA-attributes.txt */
 ...
 
-	DEFINE_DMA_ATTRS(attrs);
-	dma_set_attr(DMA_ATTR_FOO, &attrs);
+	unsigned long attr;
+	attr |= DMA_ATTR_FOO;
 	....
-	n = dma_map_sg_attrs(dev, sg, nents, DMA_TO_DEVICE, &attr);
+	n = dma_map_sg_attrs(dev, sg, nents, DMA_TO_DEVICE, attr);
 	....
 
 Architectures that care about DMA_ATTR_FOO would check for its
@@ -422,7 +419,7 @@ routines, e.g.:
 
 void whizco_dma_map_sg_attrs(struct device *dev, dma_addr_t dma_addr,
 			     size_t size, enum dma_data_direction dir,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	....
 	int foo =  dma_get_attr(DMA_ATTR_FOO, attrs);
diff --git a/Documentation/DMA-attributes.txt b/Documentation/DMA-attributes.txt
index e8cf9cf873b3..2d455a5cf671 100644
--- a/Documentation/DMA-attributes.txt
+++ b/Documentation/DMA-attributes.txt
@@ -2,7 +2,7 @@
 			==============
 
 This document describes the semantics of the DMA attributes that are
-defined in linux/dma-attrs.h.
+defined in linux/dma-mapping.h.
 
 DMA_ATTR_WRITE_BARRIER
 ----------------------
diff --git a/include/linux/dma-attrs.h b/include/linux/dma-attrs.h
deleted file mode 100644
index 5246239a4953..000000000000
--- a/include/linux/dma-attrs.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef _DMA_ATTR_H
-#define _DMA_ATTR_H
-
-#include <linux/bitmap.h>
-#include <linux/bitops.h>
-#include <linux/bug.h>
-
-/**
- * an enum dma_attr represents an attribute associated with a DMA
- * mapping. The semantics of each attribute should be defined in
- * Documentation/DMA-attributes.txt.
- */
-enum dma_attr {
-	DMA_ATTR_WRITE_BARRIER,
-	DMA_ATTR_WEAK_ORDERING,
-	DMA_ATTR_WRITE_COMBINE,
-	DMA_ATTR_NON_CONSISTENT,
-	DMA_ATTR_NO_KERNEL_MAPPING,
-	DMA_ATTR_SKIP_CPU_SYNC,
-	DMA_ATTR_FORCE_CONTIGUOUS,
-	DMA_ATTR_ALLOC_SINGLE_PAGES,
-	DMA_ATTR_MAX,
-};
-
-#define __DMA_ATTRS_LONGS BITS_TO_LONGS(DMA_ATTR_MAX)
-
-/**
- * struct dma_attrs - an opaque container for DMA attributes
- * @flags - bitmask representing a collection of enum dma_attr
- */
-struct dma_attrs {
-	unsigned long flags[__DMA_ATTRS_LONGS];
-};
-
-#define DEFINE_DMA_ATTRS(x) 					\
-	struct dma_attrs x = {					\
-		.flags = { [0 ... __DMA_ATTRS_LONGS-1] = 0 },	\
-	}
-
-static inline void init_dma_attrs(struct dma_attrs *attrs)
-{
-	bitmap_zero(attrs->flags, __DMA_ATTRS_LONGS);
-}
-
-/**
- * dma_set_attr - set a specific attribute
- * @attr: attribute to set
- * @attrs: struct dma_attrs (may be NULL)
- */
-static inline void dma_set_attr(enum dma_attr attr, struct dma_attrs *attrs)
-{
-	if (attrs == NULL)
-		return;
-	BUG_ON(attr >= DMA_ATTR_MAX);
-	__set_bit(attr, attrs->flags);
-}
-
-/**
- * dma_get_attr - check for a specific attribute
- * @attr: attribute to set
- * @attrs: struct dma_attrs (may be NULL)
- */
-static inline int dma_get_attr(enum dma_attr attr, struct dma_attrs *attrs)
-{
-	if (attrs == NULL)
-		return 0;
-	BUG_ON(attr >= DMA_ATTR_MAX);
-	return test_bit(attr, attrs->flags);
-}
-
-#endif /* _DMA_ATTR_H */
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 71c1b215ef66..b752b9ccec78 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -5,13 +5,25 @@
 #include <linux/string.h>
 #include <linux/device.h>
 #include <linux/err.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-debug.h>
 #include <linux/dma-direction.h>
 #include <linux/scatterlist.h>
 #include <linux/kmemcheck.h>
 #include <linux/bug.h>
 
+/**
+ * List of possible attributes associated with a DMA mapping. The semantics
+ * of each attribute should be defined in Documentation/DMA-attributes.txt.
+ */
+#define DMA_ATTR_WRITE_BARRIER		(1UL << 1)
+#define DMA_ATTR_WEAK_ORDERING		(1UL << 2)
+#define DMA_ATTR_WRITE_COMBINE		(1UL << 3)
+#define DMA_ATTR_NON_CONSISTENT		(1UL << 4)
+#define DMA_ATTR_NO_KERNEL_MAPPING	(1UL << 5)
+#define DMA_ATTR_SKIP_CPU_SYNC		(1UL << 6)
+#define DMA_ATTR_FORCE_CONTIGUOUS	(1UL << 7)
+#define DMA_ATTR_ALLOC_SINGLE_PAGES	(1UL << 8)
+
 /*
  * A dma_addr_t can hold any valid DMA or bus address for the platform.
  * It can be given to a device to use as a DMA source or target.  A CPU cannot
@@ -21,34 +33,35 @@
 struct dma_map_ops {
 	void* (*alloc)(struct device *dev, size_t size,
 				dma_addr_t *dma_handle, gfp_t gfp,
-				struct dma_attrs *attrs);
+				unsigned long attrs);
 	void (*free)(struct device *dev, size_t size,
 			      void *vaddr, dma_addr_t dma_handle,
-			      struct dma_attrs *attrs);
+			      unsigned long attrs);
 	int (*mmap)(struct device *, struct vm_area_struct *,
-			  void *, dma_addr_t, size_t, struct dma_attrs *attrs);
+			  void *, dma_addr_t, size_t,
+			  unsigned long attrs);
 
 	int (*get_sgtable)(struct device *dev, struct sg_table *sgt, void *,
-			   dma_addr_t, size_t, struct dma_attrs *attrs);
+			   dma_addr_t, size_t, unsigned long attrs);
 
 	dma_addr_t (*map_page)(struct device *dev, struct page *page,
 			       unsigned long offset, size_t size,
 			       enum dma_data_direction dir,
-			       struct dma_attrs *attrs);
+			       unsigned long attrs);
 	void (*unmap_page)(struct device *dev, dma_addr_t dma_handle,
 			   size_t size, enum dma_data_direction dir,
-			   struct dma_attrs *attrs);
+			   unsigned long attrs);
 	/*
 	 * map_sg returns 0 on error and a value > 0 on success.
 	 * It should never return a value < 0.
 	 */
 	int (*map_sg)(struct device *dev, struct scatterlist *sg,
 		      int nents, enum dma_data_direction dir,
-		      struct dma_attrs *attrs);
+		      unsigned long attrs);
 	void (*unmap_sg)(struct device *dev,
 			 struct scatterlist *sg, int nents,
 			 enum dma_data_direction dir,
-			 struct dma_attrs *attrs);
+			 unsigned long attrs);
 	void (*sync_single_for_cpu)(struct device *dev,
 				    dma_addr_t dma_handle, size_t size,
 				    enum dma_data_direction dir);
@@ -88,6 +101,19 @@ static inline int is_device_dma_capable(struct device *dev)
 	return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE;
 }
 
+/**
+ * dma_get_attr - check for a specific attribute
+ * @attr: attribute to look for
+ * @attrs: attributes to check within
+ *
+ * Unlike all other dma-mapping functions, this one gets pointer to
+ * attributes (for old-code compatiblity reasons).
+ */
+static inline bool dma_get_attr(unsigned long attr, unsigned long attrs)
+{
+	return !!(attr & attrs);
+}
+
 #ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT
 /*
  * These three functions are only for dma allocator.
@@ -123,7 +149,7 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
 					      size_t size,
 					      enum dma_data_direction dir,
-					      struct dma_attrs *attrs)
+					      unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	dma_addr_t addr;
@@ -142,7 +168,7 @@ static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
 static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr,
 					  size_t size,
 					  enum dma_data_direction dir,
-					  struct dma_attrs *attrs)
+					  unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 
@@ -158,7 +184,7 @@ static inline void dma_unmap_single_attrs(struct device *dev, dma_addr_t addr,
  */
 static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
 				   int nents, enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	int i, ents;
@@ -176,7 +202,7 @@ static inline int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg,
 
 static inline void dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
 				      int nents, enum dma_data_direction dir,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 
@@ -195,7 +221,7 @@ static inline dma_addr_t dma_map_page(struct device *dev, struct page *page,
 
 	kmemcheck_mark_initialized(page_address(page) + offset, size);
 	BUG_ON(!valid_dma_direction(dir));
-	addr = ops->map_page(dev, page, offset, size, dir, NULL);
+	addr = ops->map_page(dev, page, offset, size, dir, 0);
 	debug_dma_map_page(dev, page, offset, size, dir, addr, false);
 
 	return addr;
@@ -208,7 +234,7 @@ static inline void dma_unmap_page(struct device *dev, dma_addr_t addr,
 
 	BUG_ON(!valid_dma_direction(dir));
 	if (ops->unmap_page)
-		ops->unmap_page(dev, addr, size, dir, NULL);
+		ops->unmap_page(dev, addr, size, dir, 0);
 	debug_dma_unmap_page(dev, addr, size, dir, false);
 }
 
@@ -289,10 +315,10 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
 
 }
 
-#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, NULL)
-#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, NULL)
-#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, NULL)
-#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, NULL)
+#define dma_map_single(d, a, s, r) dma_map_single_attrs(d, a, s, r, 0)
+#define dma_unmap_single(d, a, s, r) dma_unmap_single_attrs(d, a, s, r, 0)
+#define dma_map_sg(d, s, n, r) dma_map_sg_attrs(d, s, n, r, 0)
+#define dma_unmap_sg(d, s, n, r) dma_unmap_sg_attrs(d, s, n, r, 0)
 
 extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
 			   void *cpu_addr, dma_addr_t dma_addr, size_t size);
@@ -321,7 +347,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags);
  */
 static inline int
 dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void *cpu_addr,
-	       dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs)
+	       dma_addr_t dma_addr, size_t size, unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	BUG_ON(!ops);
@@ -330,7 +356,7 @@ dma_mmap_attrs(struct device *dev, struct vm_area_struct *vma, void *cpu_addr,
 	return dma_common_mmap(dev, vma, cpu_addr, dma_addr, size);
 }
 
-#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, NULL)
+#define dma_mmap_coherent(d, v, c, h, s) dma_mmap_attrs(d, v, c, h, s, 0)
 
 int
 dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
@@ -338,7 +364,8 @@ dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
 
 static inline int
 dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, void *cpu_addr,
-		      dma_addr_t dma_addr, size_t size, struct dma_attrs *attrs)
+		      dma_addr_t dma_addr, size_t size,
+		      unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	BUG_ON(!ops);
@@ -348,7 +375,7 @@ dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, void *cpu_addr,
 	return dma_common_get_sgtable(dev, sgt, cpu_addr, dma_addr, size);
 }
 
-#define dma_get_sgtable(d, t, v, h, s) dma_get_sgtable_attrs(d, t, v, h, s, NULL)
+#define dma_get_sgtable(d, t, v, h, s) dma_get_sgtable_attrs(d, t, v, h, s, 0)
 
 #ifndef arch_dma_alloc_attrs
 #define arch_dma_alloc_attrs(dev, flag)	(true)
@@ -356,7 +383,7 @@ dma_get_sgtable_attrs(struct device *dev, struct sg_table *sgt, void *cpu_addr,
 
 static inline void *dma_alloc_attrs(struct device *dev, size_t size,
 				       dma_addr_t *dma_handle, gfp_t flag,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	void *cpu_addr;
@@ -378,7 +405,7 @@ static inline void *dma_alloc_attrs(struct device *dev, size_t size,
 
 static inline void dma_free_attrs(struct device *dev, size_t size,
 				     void *cpu_addr, dma_addr_t dma_handle,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 
@@ -398,31 +425,27 @@ static inline void dma_free_attrs(struct device *dev, size_t size,
 static inline void *dma_alloc_coherent(struct device *dev, size_t size,
 		dma_addr_t *dma_handle, gfp_t flag)
 {
-	return dma_alloc_attrs(dev, size, dma_handle, flag, NULL);
+	return dma_alloc_attrs(dev, size, dma_handle, flag, 0);
 }
 
 static inline void dma_free_coherent(struct device *dev, size_t size,
 		void *cpu_addr, dma_addr_t dma_handle)
 {
-	return dma_free_attrs(dev, size, cpu_addr, dma_handle, NULL);
+	return dma_free_attrs(dev, size, cpu_addr, dma_handle, 0);
 }
 
 static inline void *dma_alloc_noncoherent(struct device *dev, size_t size,
 		dma_addr_t *dma_handle, gfp_t gfp)
 {
-	DEFINE_DMA_ATTRS(attrs);
-
-	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
-	return dma_alloc_attrs(dev, size, dma_handle, gfp, &attrs);
+	return dma_alloc_attrs(dev, size, dma_handle, gfp,
+			       DMA_ATTR_NON_CONSISTENT);
 }
 
 static inline void dma_free_noncoherent(struct device *dev, size_t size,
 		void *cpu_addr, dma_addr_t dma_handle)
 {
-	DEFINE_DMA_ATTRS(attrs);
-
-	dma_set_attr(DMA_ATTR_NON_CONSISTENT, &attrs);
-	dma_free_attrs(dev, size, cpu_addr, dma_handle, &attrs);
+	dma_free_attrs(dev, size, cpu_addr, dma_handle,
+		       DMA_ATTR_NON_CONSISTENT);
 }
 
 static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
@@ -646,9 +669,8 @@ static inline void dmam_release_declared_memory(struct device *dev)
 static inline void *dma_alloc_wc(struct device *dev, size_t size,
 				 dma_addr_t *dma_addr, gfp_t gfp)
 {
-	DEFINE_DMA_ATTRS(attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-	return dma_alloc_attrs(dev, size, dma_addr, gfp, &attrs);
+	return dma_alloc_attrs(dev, size, dma_addr, gfp,
+			       DMA_ATTR_WRITE_COMBINE);
 }
 #ifndef dma_alloc_writecombine
 #define dma_alloc_writecombine dma_alloc_wc
@@ -657,9 +679,8 @@ static inline void *dma_alloc_wc(struct device *dev, size_t size,
 static inline void dma_free_wc(struct device *dev, size_t size,
 			       void *cpu_addr, dma_addr_t dma_addr)
 {
-	DEFINE_DMA_ATTRS(attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-	return dma_free_attrs(dev, size, cpu_addr, dma_addr, &attrs);
+	return dma_free_attrs(dev, size, cpu_addr, dma_addr,
+			      DMA_ATTR_WRITE_COMBINE);
 }
 #ifndef dma_free_writecombine
 #define dma_free_writecombine dma_free_wc
@@ -670,9 +691,8 @@ static inline int dma_mmap_wc(struct device *dev,
 			      void *cpu_addr, dma_addr_t dma_addr,
 			      size_t size)
 {
-	DEFINE_DMA_ATTRS(attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-	return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size, &attrs);
+	return dma_mmap_attrs(dev, vma, cpu_addr, dma_addr, size,
+			      DMA_ATTR_WRITE_COMBINE);
 }
 #ifndef dma_mmap_writecombine
 #define dma_mmap_writecombine dma_mmap_wc
diff --git a/lib/dma-noop.c b/lib/dma-noop.c
index 72145646857e..3d766e78fbe2 100644
--- a/lib/dma-noop.c
+++ b/lib/dma-noop.c
@@ -10,7 +10,7 @@
 
 static void *dma_noop_alloc(struct device *dev, size_t size,
 			    dma_addr_t *dma_handle, gfp_t gfp,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	void *ret;
 
@@ -22,7 +22,7 @@ static void *dma_noop_alloc(struct device *dev, size_t size,
 
 static void dma_noop_free(struct device *dev, size_t size,
 			  void *cpu_addr, dma_addr_t dma_addr,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	free_pages((unsigned long)cpu_addr, get_order(size));
 }
@@ -30,13 +30,14 @@ static void dma_noop_free(struct device *dev, size_t size,
 static dma_addr_t dma_noop_map_page(struct device *dev, struct page *page,
 				      unsigned long offset, size_t size,
 				      enum dma_data_direction dir,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	return page_to_phys(page) + offset;
 }
 
 static int dma_noop_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
-			     enum dma_data_direction dir, struct dma_attrs *attrs)
+			     enum dma_data_direction dir,
+			     unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:07 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:07 +0200
Subject: [RFC v3 05/45] ARM: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-6-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/arm/common/dmabounce.c              |  4 +-
 arch/arm/include/asm/dma-mapping.h       | 13 +++--
 arch/arm/include/asm/xen/page-coherent.h | 16 +++----
 arch/arm/mm/dma-mapping.c                | 81 ++++++++++++++++----------------
 arch/arm/xen/mm.c                        |  4 +-
 5 files changed, 56 insertions(+), 62 deletions(-)

diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
index 1143c4d5c567..301281645d08 100644
--- a/arch/arm/common/dmabounce.c
+++ b/arch/arm/common/dmabounce.c
@@ -310,7 +310,7 @@ static inline void unmap_single(struct device *dev, struct safe_buffer *buf,
  */
 static dma_addr_t dmabounce_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	dma_addr_t dma_addr;
 	int ret;
@@ -344,7 +344,7 @@ static dma_addr_t dmabounce_map_page(struct device *dev, struct page *page,
  * should be)
  */
 static void dmabounce_unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
-		enum dma_data_direction dir, struct dma_attrs *attrs)
+		enum dma_data_direction dir, unsigned long attrs)
 {
 	struct safe_buffer *buf;
 
diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
index a83570f10124..d009f7911ffc 100644
--- a/arch/arm/include/asm/dma-mapping.h
+++ b/arch/arm/include/asm/dma-mapping.h
@@ -5,7 +5,6 @@
 
 #include <linux/mm_types.h>
 #include <linux/scatterlist.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-debug.h>
 
 #include <asm/memory.h>
@@ -174,7 +173,7 @@ static inline void dma_mark_clean(void *addr, size_t size) { }
  * to be the device-viewed address.
  */
 extern void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-			   gfp_t gfp, struct dma_attrs *attrs);
+			   gfp_t gfp, unsigned long attrs);
 
 /**
  * arm_dma_free - free memory allocated by arm_dma_alloc
@@ -191,7 +190,7 @@ extern void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
  * during and after this call executing are illegal.
  */
 extern void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
-			 dma_addr_t handle, struct dma_attrs *attrs);
+			 dma_addr_t handle, unsigned long attrs);
 
 /**
  * arm_dma_mmap - map a coherent DMA allocation into user space
@@ -208,7 +207,7 @@ extern void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
  */
 extern int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 			void *cpu_addr, dma_addr_t dma_addr, size_t size,
-			struct dma_attrs *attrs);
+			unsigned long attrs);
 
 /*
  * This can be called during early boot to increase the size of the atomic
@@ -262,16 +261,16 @@ extern void dmabounce_unregister_dev(struct device *);
  * The scatter list versions of the above methods.
  */
 extern int arm_dma_map_sg(struct device *, struct scatterlist *, int,
-		enum dma_data_direction, struct dma_attrs *attrs);
+		enum dma_data_direction, unsigned long attrs);
 extern void arm_dma_unmap_sg(struct device *, struct scatterlist *, int,
-		enum dma_data_direction, struct dma_attrs *attrs);
+		enum dma_data_direction, unsigned long attrs);
 extern void arm_dma_sync_sg_for_cpu(struct device *, struct scatterlist *, int,
 		enum dma_data_direction);
 extern void arm_dma_sync_sg_for_device(struct device *, struct scatterlist *, int,
 		enum dma_data_direction);
 extern int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
 		void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		struct dma_attrs *attrs);
+		unsigned long attrs);
 
 #endif /* __KERNEL__ */
 #endif
diff --git a/arch/arm/include/asm/xen/page-coherent.h b/arch/arm/include/asm/xen/page-coherent.h
index 9408a994cc91..95ce6ac3a971 100644
--- a/arch/arm/include/asm/xen/page-coherent.h
+++ b/arch/arm/include/asm/xen/page-coherent.h
@@ -2,15 +2,14 @@
 #define _ASM_ARM_XEN_PAGE_COHERENT_H
 
 #include <asm/page.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-mapping.h>
 
 void __xen_dma_map_page(struct device *hwdev, struct page *page,
 	     dma_addr_t dev_addr, unsigned long offset, size_t size,
-	     enum dma_data_direction dir, struct dma_attrs *attrs);
+	     enum dma_data_direction dir, unsigned long attrs);
 void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs);
+		unsigned long attrs);
 void __xen_dma_sync_single_for_cpu(struct device *hwdev,
 		dma_addr_t handle, size_t size, enum dma_data_direction dir);
 
@@ -18,22 +17,20 @@ void __xen_dma_sync_single_for_device(struct device *hwdev,
 		dma_addr_t handle, size_t size, enum dma_data_direction dir);
 
 static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
-		dma_addr_t *dma_handle, gfp_t flags,
-		struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs)
 {
 	return __generic_dma_ops(hwdev)->alloc(hwdev, size, dma_handle, flags, attrs);
 }
 
 static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
-		void *cpu_addr, dma_addr_t dma_handle,
-		struct dma_attrs *attrs)
+		void *cpu_addr, dma_addr_t dma_handle, unsigned long attrs)
 {
 	__generic_dma_ops(hwdev)->free(hwdev, size, cpu_addr, dma_handle, attrs);
 }
 
 static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
 	     dma_addr_t dev_addr, unsigned long offset, size_t size,
-	     enum dma_data_direction dir, struct dma_attrs *attrs)
+	     enum dma_data_direction dir, unsigned long attrs)
 {
 	unsigned long page_pfn = page_to_xen_pfn(page);
 	unsigned long dev_pfn = XEN_PFN_DOWN(dev_addr);
@@ -58,8 +55,7 @@ static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
 }
 
 static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
-		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	unsigned long pfn = PFN_DOWN(handle);
 	/*
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ff7ed5697d3e..ebb3fde99043 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -124,7 +124,7 @@ static void __dma_page_dev_to_cpu(struct page *, unsigned long,
  */
 static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
-	     struct dma_attrs *attrs)
+	     unsigned long attrs)
 {
 	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
 		__dma_page_cpu_to_dev(page, offset, size, dir);
@@ -133,7 +133,7 @@ static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
 
 static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
-	     struct dma_attrs *attrs)
+	     unsigned long attrs)
 {
 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
 }
@@ -153,8 +153,7 @@ static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *pag
  * whatever the device wrote there.
  */
 static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle,
-		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
 		__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
@@ -194,12 +193,12 @@ struct dma_map_ops arm_dma_ops = {
 EXPORT_SYMBOL(arm_dma_ops);
 
 static void *arm_coherent_dma_alloc(struct device *dev, size_t size,
-	dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs);
+	dma_addr_t *handle, gfp_t gfp, unsigned long attrs);
 static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr,
-				  dma_addr_t handle, struct dma_attrs *attrs);
+				  dma_addr_t handle, unsigned long attrs);
 static int arm_coherent_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 		 void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		 struct dma_attrs *attrs);
+		 unsigned long attrs);
 
 struct dma_map_ops arm_coherent_dma_ops = {
 	.alloc			= arm_coherent_dma_alloc,
@@ -621,7 +620,7 @@ static void __free_from_contiguous(struct device *dev, struct page *page,
 	dma_release_from_contiguous(dev, page, size >> PAGE_SHIFT);
 }
 
-static inline pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot)
+static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot)
 {
 	prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ?
 			    pgprot_writecombine(prot) :
@@ -732,7 +731,7 @@ static struct arm_dma_allocator remap_allocator = {
 
 static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 			 gfp_t gfp, pgprot_t prot, bool is_coherent,
-			 struct dma_attrs *attrs, const void *caller)
+			 unsigned long attrs, const void *caller)
 {
 	u64 mask = get_coherent_dma_mask(dev);
 	struct page *page = NULL;
@@ -814,7 +813,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
  * virtual and bus address for that space.
  */
 void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-		    gfp_t gfp, struct dma_attrs *attrs)
+		    gfp_t gfp, unsigned long attrs)
 {
 	pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
 
@@ -823,7 +822,7 @@ void *arm_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 }
 
 static void *arm_coherent_dma_alloc(struct device *dev, size_t size,
-	dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
+	dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
 {
 	return __dma_alloc(dev, size, handle, gfp, PAGE_KERNEL, true,
 			   attrs, __builtin_return_address(0));
@@ -831,7 +830,7 @@ static void *arm_coherent_dma_alloc(struct device *dev, size_t size,
 
 static int __arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 		 void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 {
 	int ret = -ENXIO;
 #ifdef CONFIG_MMU
@@ -859,14 +858,14 @@ static int __arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
  */
 static int arm_coherent_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 		 void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 {
 	return __arm_dma_mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
 }
 
 int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 		 void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 {
 #ifdef CONFIG_MMU
 	vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
@@ -878,7 +877,7 @@ int arm_dma_mmap(struct device *dev, struct vm_area_struct *vma,
  * Free a buffer as defined by the above mapping.
  */
 static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
-			   dma_addr_t handle, struct dma_attrs *attrs,
+			   dma_addr_t handle, unsigned long attrs,
 			   bool is_coherent)
 {
 	struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
@@ -900,20 +899,20 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 }
 
 void arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
-		  dma_addr_t handle, struct dma_attrs *attrs)
+		  dma_addr_t handle, unsigned long attrs)
 {
 	__arm_dma_free(dev, size, cpu_addr, handle, attrs, false);
 }
 
 static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_addr,
-				  dma_addr_t handle, struct dma_attrs *attrs)
+				  dma_addr_t handle, unsigned long attrs)
 {
 	__arm_dma_free(dev, size, cpu_addr, handle, attrs, true);
 }
 
 int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
 		 void *cpu_addr, dma_addr_t handle, size_t size,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 {
 	struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
 	int ret;
@@ -1046,7 +1045,7 @@ static void __dma_page_dev_to_cpu(struct page *page, unsigned long off,
  * here.
  */
 int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
-		enum dma_data_direction dir, struct dma_attrs *attrs)
+		enum dma_data_direction dir, unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	struct scatterlist *s;
@@ -1080,7 +1079,7 @@ int arm_dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
  * rules concerning calls here are the same as for dma_unmap_single().
  */
 void arm_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-		enum dma_data_direction dir, struct dma_attrs *attrs)
+		enum dma_data_direction dir, unsigned long attrs)
 {
 	struct dma_map_ops *ops = get_dma_ops(dev);
 	struct scatterlist *s;
@@ -1253,7 +1252,7 @@ static inline void __free_iova(struct dma_iommu_mapping *mapping,
 static const int iommu_order_array[] = { 9, 8, 4, 0 };
 
 static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
-					  gfp_t gfp, struct dma_attrs *attrs)
+					  gfp_t gfp, unsigned long attrs)
 {
 	struct page **pages;
 	int count = size >> PAGE_SHIFT;
@@ -1342,7 +1341,7 @@ error:
 }
 
 static int __iommu_free_buffer(struct device *dev, struct page **pages,
-			       size_t size, struct dma_attrs *attrs)
+			       size_t size, unsigned long attrs)
 {
 	int count = size >> PAGE_SHIFT;
 	int i;
@@ -1439,7 +1438,7 @@ static struct page **__atomic_get_pages(void *addr)
 	return (struct page **)page;
 }
 
-static struct page **__iommu_get_pages(void *cpu_addr, struct dma_attrs *attrs)
+static struct page **__iommu_get_pages(void *cpu_addr, unsigned long attrs)
 {
 	struct vm_struct *area;
 
@@ -1484,7 +1483,7 @@ static void __iommu_free_atomic(struct device *dev, void *cpu_addr,
 }
 
 static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
-	    dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
+	    dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
 {
 	pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
 	struct page **pages;
@@ -1532,7 +1531,7 @@ err_buffer:
 
 static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
 		    void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		    struct dma_attrs *attrs)
+		    unsigned long attrs)
 {
 	unsigned long uaddr = vma->vm_start;
 	unsigned long usize = vma->vm_end - vma->vm_start;
@@ -1568,7 +1567,7 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
  * Must not be called with IRQs disabled.
  */
 void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
-			  dma_addr_t handle, struct dma_attrs *attrs)
+			  dma_addr_t handle, unsigned long attrs)
 {
 	struct page **pages;
 	size = PAGE_ALIGN(size);
@@ -1595,7 +1594,7 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
 
 static int arm_iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
 				 void *cpu_addr, dma_addr_t dma_addr,
-				 size_t size, struct dma_attrs *attrs)
+				 size_t size, unsigned long attrs)
 {
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 	struct page **pages = __iommu_get_pages(cpu_addr, attrs);
@@ -1633,7 +1632,7 @@ static int __dma_direction_to_prot(enum dma_data_direction dir)
  */
 static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
 			  size_t size, dma_addr_t *handle,
-			  enum dma_data_direction dir, struct dma_attrs *attrs,
+			  enum dma_data_direction dir, unsigned long attrs,
 			  bool is_coherent)
 {
 	struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
@@ -1676,7 +1675,7 @@ fail:
 }
 
 static int __iommu_map_sg(struct device *dev, struct scatterlist *sg, int nents,
-		     enum dma_data_direction dir, struct dma_attrs *attrs,
+		     enum dma_data_direction dir, unsigned long attrs,
 		     bool is_coherent)
 {
 	struct scatterlist *s = sg, *dma = sg, *start = sg;
@@ -1734,7 +1733,7 @@ bad_mapping:
  * obtained via sg_dma_{address,length}.
  */
 int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+		int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	return __iommu_map_sg(dev, sg, nents, dir, attrs, true);
 }
@@ -1752,14 +1751,14 @@ int arm_coherent_iommu_map_sg(struct device *dev, struct scatterlist *sg,
  * sg_dma_{address,length}.
  */
 int arm_iommu_map_sg(struct device *dev, struct scatterlist *sg,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+		int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	return __iommu_map_sg(dev, sg, nents, dir, attrs, false);
 }
 
 static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs,
-		bool is_coherent)
+		int nents, enum dma_data_direction dir,
+		unsigned long attrs, bool is_coherent)
 {
 	struct scatterlist *s;
 	int i;
@@ -1786,7 +1785,8 @@ static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
  * rules concerning calls here are the same as for dma_unmap_single().
  */
 void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+		int nents, enum dma_data_direction dir,
+		unsigned long attrs)
 {
 	__iommu_unmap_sg(dev, sg, nents, dir, attrs, true);
 }
@@ -1802,7 +1802,8 @@ void arm_coherent_iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
  * rules concerning calls here are the same as for dma_unmap_single().
  */
 void arm_iommu_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-			enum dma_data_direction dir, struct dma_attrs *attrs)
+			enum dma_data_direction dir,
+			unsigned long attrs)
 {
 	__iommu_unmap_sg(dev, sg, nents, dir, attrs, false);
 }
@@ -1855,7 +1856,7 @@ void arm_iommu_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
  */
 static dma_addr_t arm_coherent_iommu_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
-	     struct dma_attrs *attrs)
+	     unsigned long attrs)
 {
 	struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
 	dma_addr_t dma_addr;
@@ -1889,7 +1890,7 @@ fail:
  */
 static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
-	     struct dma_attrs *attrs)
+	     unsigned long attrs)
 {
 	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
 		__dma_page_cpu_to_dev(page, offset, size, dir);
@@ -1907,8 +1908,7 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
  * Coherent IOMMU aware version of arm_dma_unmap_page()
  */
 static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle,
-		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
 	dma_addr_t iova = handle & PAGE_MASK;
@@ -1932,8 +1932,7 @@ static void arm_coherent_iommu_unmap_page(struct device *dev, dma_addr_t handle,
  * IOMMU aware version of arm_dma_unmap_page()
  */
 static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
-		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
 	dma_addr_t iova = handle & PAGE_MASK;
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
index c5f9a9e3d1f3..fc67ed236a10 100644
--- a/arch/arm/xen/mm.c
+++ b/arch/arm/xen/mm.c
@@ -98,7 +98,7 @@ static void __xen_dma_page_cpu_to_dev(struct device *hwdev, dma_addr_t handle,
 
 void __xen_dma_map_page(struct device *hwdev, struct page *page,
 	     dma_addr_t dev_addr, unsigned long offset, size_t size,
-	     enum dma_data_direction dir, struct dma_attrs *attrs)
+	     enum dma_data_direction dir, unsigned long attrs)
 {
 	if (is_device_dma_coherent(hwdev))
 		return;
@@ -110,7 +110,7 @@ void __xen_dma_map_page(struct device *hwdev, struct page *page,
 
 void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 
 {
 	if (is_device_dma_coherent(hwdev))
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:10 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:10 +0200
Subject: [RFC v3 08/45] blackfin: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-9-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/blackfin/kernel/dma-mapping.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/blackfin/kernel/dma-mapping.c b/arch/blackfin/kernel/dma-mapping.c
index 771afe6e4264..53fbbb61aa86 100644
--- a/arch/blackfin/kernel/dma-mapping.c
+++ b/arch/blackfin/kernel/dma-mapping.c
@@ -79,7 +79,7 @@ static void __free_dma_pages(unsigned long addr, unsigned int pages)
 }
 
 static void *bfin_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -94,7 +94,7 @@ static void *bfin_dma_alloc(struct device *dev, size_t size,
 }
 
 static void bfin_dma_free(struct device *dev, size_t size, void *vaddr,
-		  dma_addr_t dma_handle, struct dma_attrs *attrs)
+		  dma_addr_t dma_handle, unsigned long attrs)
 {
 	__free_dma_pages((unsigned long)vaddr, get_pages(size));
 }
@@ -111,7 +111,7 @@ EXPORT_SYMBOL(__dma_sync);
 
 static int bfin_dma_map_sg(struct device *dev, struct scatterlist *sg_list,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -139,7 +139,7 @@ static void bfin_dma_sync_sg_for_device(struct device *dev,
 
 static dma_addr_t bfin_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	dma_addr_t handle = (dma_addr_t)(page_address(page) + offset);
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:09 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:09 +0200
Subject: [RFC v3 07/45] avr32: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-8-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/avr32/mm/dma-coherent.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
index 92cf1fb2b3e6..fc51f4421933 100644
--- a/arch/avr32/mm/dma-coherent.c
+++ b/arch/avr32/mm/dma-coherent.c
@@ -99,7 +99,7 @@ static void __dma_free(struct device *dev, size_t size,
 }
 
 static void *avr32_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
 {
 	struct page *page;
 	dma_addr_t phys;
@@ -119,7 +119,7 @@ static void *avr32_dma_alloc(struct device *dev, size_t size,
 }
 
 static void avr32_dma_free(struct device *dev, size_t size,
-		void *cpu_addr, dma_addr_t handle, struct dma_attrs *attrs)
+		void *cpu_addr, dma_addr_t handle, unsigned long attrs)
 {
 	struct page *page;
 
@@ -142,7 +142,7 @@ static void avr32_dma_free(struct device *dev, size_t size,
 
 static dma_addr_t avr32_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	void *cpu_addr = page_address(page) + offset;
 
@@ -152,7 +152,7 @@ static dma_addr_t avr32_dma_map_page(struct device *dev, struct page *page,
 
 static int avr32_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:05 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:05 +0200
Subject: [RFC v3 03/45] alpha: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-4-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/alpha/include/asm/dma-mapping.h |  2 --
 arch/alpha/kernel/pci-noop.c         |  2 +-
 arch/alpha/kernel/pci_iommu.c        | 12 ++++++------
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/arch/alpha/include/asm/dma-mapping.h b/arch/alpha/include/asm/dma-mapping.h
index 3c3451f58ff4..c63b6ac19ee5 100644
--- a/arch/alpha/include/asm/dma-mapping.h
+++ b/arch/alpha/include/asm/dma-mapping.h
@@ -1,8 +1,6 @@
 #ifndef _ALPHA_DMA_MAPPING_H
 #define _ALPHA_DMA_MAPPING_H
 
-#include <linux/dma-attrs.h>
-
 extern struct dma_map_ops *dma_ops;
 
 static inline struct dma_map_ops *get_dma_ops(struct device *dev)
diff --git a/arch/alpha/kernel/pci-noop.c b/arch/alpha/kernel/pci-noop.c
index 8e735b5e56bd..bb152e21e5ae 100644
--- a/arch/alpha/kernel/pci-noop.c
+++ b/arch/alpha/kernel/pci-noop.c
@@ -109,7 +109,7 @@ sys_pciconfig_write(unsigned long bus, unsigned long dfn,
 
 static void *alpha_noop_alloc_coherent(struct device *dev, size_t size,
 				       dma_addr_t *dma_handle, gfp_t gfp,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	void *ret;
 
diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c
index 8969bf2dfe3a..451fc9cdd323 100644
--- a/arch/alpha/kernel/pci_iommu.c
+++ b/arch/alpha/kernel/pci_iommu.c
@@ -349,7 +349,7 @@ static struct pci_dev *alpha_gendev_to_pci(struct device *dev)
 static dma_addr_t alpha_pci_map_page(struct device *dev, struct page *page,
 				     unsigned long offset, size_t size,
 				     enum dma_data_direction dir,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
 	int dac_allowed;
@@ -369,7 +369,7 @@ static dma_addr_t alpha_pci_map_page(struct device *dev, struct page *page,
 
 static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr,
 				 size_t size, enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	unsigned long flags;
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
@@ -433,7 +433,7 @@ static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr,
 
 static void *alpha_pci_alloc_coherent(struct device *dev, size_t size,
 				      dma_addr_t *dma_addrp, gfp_t gfp,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
 	void *cpu_addr;
@@ -478,7 +478,7 @@ try_again:
 
 static void alpha_pci_free_coherent(struct device *dev, size_t size,
 				    void *cpu_addr, dma_addr_t dma_addr,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
 	pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
@@ -651,7 +651,7 @@ sg_fill(struct device *dev, struct scatterlist *leader, struct scatterlist *end,
 
 static int alpha_pci_map_sg(struct device *dev, struct scatterlist *sg,
 			    int nents, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
 	struct scatterlist *start, *end, *out;
@@ -729,7 +729,7 @@ static int alpha_pci_map_sg(struct device *dev, struct scatterlist *sg,
 
 static void alpha_pci_unmap_sg(struct device *dev, struct scatterlist *sg,
 			       int nents, enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	struct pci_dev *pdev = alpha_gendev_to_pci(dev);
 	unsigned long flags;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:13 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:13 +0200
Subject: [RFC v3 11/45] frv: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-12-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/frv/mb93090-mb00/pci-dma-nommu.c | 8 ++++----
 arch/frv/mb93090-mb00/pci-dma.c       | 9 ++++-----
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/arch/frv/mb93090-mb00/pci-dma-nommu.c b/arch/frv/mb93090-mb00/pci-dma-nommu.c
index 082be49b5df0..90f2e4cb33d6 100644
--- a/arch/frv/mb93090-mb00/pci-dma-nommu.c
+++ b/arch/frv/mb93090-mb00/pci-dma-nommu.c
@@ -35,7 +35,7 @@ static DEFINE_SPINLOCK(dma_alloc_lock);
 static LIST_HEAD(dma_alloc_list);
 
 static void *frv_dma_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_handle,
-		gfp_t gfp, struct dma_attrs *attrs)
+		gfp_t gfp, unsigned long attrs)
 {
 	struct dma_alloc_record *new;
 	struct list_head *this = &dma_alloc_list;
@@ -86,7 +86,7 @@ static void *frv_dma_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_ha
 }
 
 static void frv_dma_free(struct device *hwdev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct dma_alloc_record *rec;
 	unsigned long flags;
@@ -107,7 +107,7 @@ static void frv_dma_free(struct device *hwdev, size_t size, void *vaddr,
 
 static int frv_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
@@ -124,7 +124,7 @@ static int frv_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static dma_addr_t frv_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	BUG_ON(direction == DMA_NONE);
 	flush_dcache_page(page);
diff --git a/arch/frv/mb93090-mb00/pci-dma.c b/arch/frv/mb93090-mb00/pci-dma.c
index 316b7b65348d..f585745b1abc 100644
--- a/arch/frv/mb93090-mb00/pci-dma.c
+++ b/arch/frv/mb93090-mb00/pci-dma.c
@@ -19,8 +19,7 @@
 #include <asm/io.h>
 
 static void *frv_dma_alloc(struct device *hwdev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp,
-		struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -32,14 +31,14 @@ static void *frv_dma_alloc(struct device *hwdev, size_t size,
 }
 
 static void frv_dma_free(struct device *hwdev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	consistent_free(vaddr);
 }
 
 static int frv_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	unsigned long dampr2;
 	void *vaddr;
@@ -69,7 +68,7 @@ static int frv_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static dma_addr_t frv_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	flush_dcache_page(page);
 	return (dma_addr_t) page_to_phys(page) + offset;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:11 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:11 +0200
Subject: [RFC v3 09/45] c6x: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-10-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/c6x/include/asm/dma-mapping.h | 4 ++--
 arch/c6x/kernel/dma.c              | 9 ++++-----
 arch/c6x/mm/dma-coherent.c         | 4 ++--
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/arch/c6x/include/asm/dma-mapping.h b/arch/c6x/include/asm/dma-mapping.h
index 6b5cd7b0cf32..5717b1e52d96 100644
--- a/arch/c6x/include/asm/dma-mapping.h
+++ b/arch/c6x/include/asm/dma-mapping.h
@@ -26,8 +26,8 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev)
 
 extern void coherent_mem_init(u32 start, u32 size);
 void *c6x_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-		gfp_t gfp, struct dma_attrs *attrs);
+		gfp_t gfp, unsigned long attrs);
 void c6x_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs);
+		dma_addr_t dma_handle, unsigned long attrs);
 
 #endif	/* _ASM_C6X_DMA_MAPPING_H */
diff --git a/arch/c6x/kernel/dma.c b/arch/c6x/kernel/dma.c
index 8a80f3a250c0..db4a6a301f5e 100644
--- a/arch/c6x/kernel/dma.c
+++ b/arch/c6x/kernel/dma.c
@@ -38,7 +38,7 @@ static void c6x_dma_sync(dma_addr_t handle, size_t size,
 
 static dma_addr_t c6x_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	dma_addr_t handle = virt_to_phys(page_address(page) + offset);
 
@@ -47,13 +47,13 @@ static dma_addr_t c6x_dma_map_page(struct device *dev, struct page *page,
 }
 
 static void c6x_dma_unmap_page(struct device *dev, dma_addr_t handle,
-		size_t size, enum dma_data_direction dir, struct dma_attrs *attrs)
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	c6x_dma_sync(handle, size, dir);
 }
 
 static int c6x_dma_map_sg(struct device *dev, struct scatterlist *sglist,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+		int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -67,8 +67,7 @@ static int c6x_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 }
 
 static void c6x_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
-		  int nents, enum dma_data_direction dir,
-		  struct dma_attrs *attrs)
+		  int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
diff --git a/arch/c6x/mm/dma-coherent.c b/arch/c6x/mm/dma-coherent.c
index f7ee63af2541..95e38ad27c69 100644
--- a/arch/c6x/mm/dma-coherent.c
+++ b/arch/c6x/mm/dma-coherent.c
@@ -74,7 +74,7 @@ static void __free_dma_pages(u32 addr, int order)
  * virtual and DMA address for that space.
  */
 void *c6x_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-		gfp_t gfp, struct dma_attrs *attrs)
+		gfp_t gfp, unsigned long attrs)
 {
 	u32 paddr;
 	int order;
@@ -99,7 +99,7 @@ void *c6x_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
  * Free DMA coherent memory as defined by the above mapping.
  */
 void c6x_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	int order;
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:12 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:12 +0200
Subject: [RFC v3 10/45] cris: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-11-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/cris/arch-v32/drivers/pci/dma.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/arch/cris/arch-v32/drivers/pci/dma.c b/arch/cris/arch-v32/drivers/pci/dma.c
index 8d5efa58cce1..1f0636793f0c 100644
--- a/arch/cris/arch-v32/drivers/pci/dma.c
+++ b/arch/cris/arch-v32/drivers/pci/dma.c
@@ -17,7 +17,7 @@
 #include <asm/io.h>
 
 static void *v32_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp,  struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -37,22 +37,21 @@ static void *v32_dma_alloc(struct device *dev, size_t size,
 }
 
 static void v32_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	free_pages((unsigned long)vaddr, get_order(size));
 }
 
 static inline dma_addr_t v32_dma_map_page(struct device *dev,
 		struct page *page, unsigned long offset, size_t size,
-		enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	return page_to_phys(page) + offset;
 }
 
 static inline int v32_dma_map_sg(struct device *dev, struct scatterlist *sg,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	printk("Map sg\n");
 	return nents;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:08 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:08 +0200
Subject: [RFC v3 06/45] arm64: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-7-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/arm64/mm/dma-mapping.c | 57 +++++++++++++++++++++++----------------------
 1 file changed, 29 insertions(+), 28 deletions(-)

diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index c566ec83719f..a7686028dfeb 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -29,7 +29,7 @@
 
 #include <asm/cacheflush.h>
 
-static pgprot_t __get_dma_pgprot(struct dma_attrs *attrs, pgprot_t prot,
+static pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot,
 				 bool coherent)
 {
 	if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
@@ -88,7 +88,7 @@ static int __free_from_pool(void *start, size_t size)
 
 static void *__dma_alloc_coherent(struct device *dev, size_t size,
 				  dma_addr_t *dma_handle, gfp_t flags,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	if (dev == NULL) {
 		WARN_ONCE(1, "Use an actual device structure for DMA allocation\n");
@@ -118,7 +118,7 @@ static void *__dma_alloc_coherent(struct device *dev, size_t size,
 
 static void __dma_free_coherent(struct device *dev, size_t size,
 				void *vaddr, dma_addr_t dma_handle,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	bool freed;
 	phys_addr_t paddr = dma_to_phys(dev, dma_handle);
@@ -137,7 +137,7 @@ static void __dma_free_coherent(struct device *dev, size_t size,
 
 static void *__dma_alloc(struct device *dev, size_t size,
 			 dma_addr_t *dma_handle, gfp_t flags,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	struct page *page;
 	void *ptr, *coherent_ptr;
@@ -185,7 +185,7 @@ no_mem:
 
 static void __dma_free(struct device *dev, size_t size,
 		       void *vaddr, dma_addr_t dma_handle,
-		       struct dma_attrs *attrs)
+		       unsigned long attrs)
 {
 	void *swiotlb_addr = phys_to_virt(dma_to_phys(dev, dma_handle));
 
@@ -202,7 +202,7 @@ static void __dma_free(struct device *dev, size_t size,
 static dma_addr_t __swiotlb_map_page(struct device *dev, struct page *page,
 				     unsigned long offset, size_t size,
 				     enum dma_data_direction dir,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	dma_addr_t dev_addr;
 
@@ -216,7 +216,7 @@ static dma_addr_t __swiotlb_map_page(struct device *dev, struct page *page,
 
 static void __swiotlb_unmap_page(struct device *dev, dma_addr_t dev_addr,
 				 size_t size, enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	if (!is_device_dma_coherent(dev))
 		__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);
@@ -225,7 +225,7 @@ static void __swiotlb_unmap_page(struct device *dev, dma_addr_t dev_addr,
 
 static int __swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 				  int nelems, enum dma_data_direction dir,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i, ret;
@@ -242,7 +242,7 @@ static int __swiotlb_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 static void __swiotlb_unmap_sg_attrs(struct device *dev,
 				     struct scatterlist *sgl, int nelems,
 				     enum dma_data_direction dir,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -303,7 +303,7 @@ static void __swiotlb_sync_sg_for_device(struct device *dev,
 static int __swiotlb_mmap(struct device *dev,
 			  struct vm_area_struct *vma,
 			  void *cpu_addr, dma_addr_t dma_addr, size_t size,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	int ret = -ENXIO;
 	unsigned long nr_vma_pages = (vma->vm_end - vma->vm_start) >>
@@ -330,7 +330,7 @@ static int __swiotlb_mmap(struct device *dev,
 
 static int __swiotlb_get_sgtable(struct device *dev, struct sg_table *sgt,
 				 void *cpu_addr, dma_addr_t handle, size_t size,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	int ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
 
@@ -425,21 +425,21 @@ out:
 
 static void *__dummy_alloc(struct device *dev, size_t size,
 			   dma_addr_t *dma_handle, gfp_t flags,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	return NULL;
 }
 
 static void __dummy_free(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 }
 
 static int __dummy_mmap(struct device *dev,
 			struct vm_area_struct *vma,
 			void *cpu_addr, dma_addr_t dma_addr, size_t size,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	return -ENXIO;
 }
@@ -447,20 +447,20 @@ static int __dummy_mmap(struct device *dev,
 static dma_addr_t __dummy_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	return DMA_ERROR_CODE;
 }
 
 static void __dummy_unmap_page(struct device *dev, dma_addr_t dev_addr,
 			       size_t size, enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 }
 
 static int __dummy_map_sg(struct device *dev, struct scatterlist *sgl,
 			  int nelems, enum dma_data_direction dir,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	return 0;
 }
@@ -468,7 +468,7 @@ static int __dummy_map_sg(struct device *dev, struct scatterlist *sgl,
 static void __dummy_unmap_sg(struct device *dev,
 			     struct scatterlist *sgl, int nelems,
 			     enum dma_data_direction dir,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 }
 
@@ -540,7 +540,7 @@ static void flush_page(struct device *dev, const void *virt, phys_addr_t phys)
 
 static void *__iommu_alloc_attrs(struct device *dev, size_t size,
 				 dma_addr_t *handle, gfp_t gfp,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	bool coherent = is_device_dma_coherent(dev);
 	int ioprot = dma_direction_to_prot(DMA_BIDIRECTIONAL, coherent);
@@ -600,7 +600,8 @@ static void *__iommu_alloc_attrs(struct device *dev, size_t size,
 }
 
 static void __iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
-			       dma_addr_t handle, struct dma_attrs *attrs)
+			       dma_addr_t handle,
+			       unsigned long attrs)
 {
 	size_t iosize = size;
 
@@ -616,7 +617,7 @@ static void __iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
 	 * Hence how dodgy the below logic looks...
 	 */
 	if (__in_atomic_pool(cpu_addr, size)) {
-		iommu_dma_unmap_page(dev, handle, iosize, 0, NULL);
+		iommu_dma_unmap_page(dev, handle, iosize, 0, 0);
 		__free_from_pool(cpu_addr, size);
 	} else if (is_vmalloc_addr(cpu_addr)){
 		struct vm_struct *area = find_vm_area(cpu_addr);
@@ -626,14 +627,14 @@ static void __iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
 		iommu_dma_free(dev, area->pages, iosize, &handle);
 		dma_common_free_remap(cpu_addr, size, VM_USERMAP);
 	} else {
-		iommu_dma_unmap_page(dev, handle, iosize, 0, NULL);
+		iommu_dma_unmap_page(dev, handle, iosize, 0, 0);
 		__free_pages(virt_to_page(cpu_addr), get_order(size));
 	}
 }
 
 static int __iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
 			      void *cpu_addr, dma_addr_t dma_addr, size_t size,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct vm_struct *area;
 	int ret;
@@ -653,7 +654,7 @@ static int __iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
 
 static int __iommu_get_sgtable(struct device *dev, struct sg_table *sgt,
 			       void *cpu_addr, dma_addr_t dma_addr,
-			       size_t size, struct dma_attrs *attrs)
+			       size_t size, unsigned long attrs)
 {
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 	struct vm_struct *area = find_vm_area(cpu_addr);
@@ -694,7 +695,7 @@ static void __iommu_sync_single_for_device(struct device *dev,
 static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	bool coherent = is_device_dma_coherent(dev);
 	int prot = dma_direction_to_prot(dir, coherent);
@@ -709,7 +710,7 @@ static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
 
 static void __iommu_unmap_page(struct device *dev, dma_addr_t dev_addr,
 			       size_t size, enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
 		__iommu_sync_single_for_cpu(dev, dev_addr, size, dir);
@@ -747,7 +748,7 @@ static void __iommu_sync_sg_for_device(struct device *dev,
 
 static int __iommu_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 				int nelems, enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	bool coherent = is_device_dma_coherent(dev);
 
@@ -761,7 +762,7 @@ static int __iommu_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 static void __iommu_unmap_sg_attrs(struct device *dev,
 				   struct scatterlist *sgl, int nelems,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
 		__iommu_sync_sg_for_cpu(dev, sgl, nelems, dir);
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:15 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:15 +0200
Subject: [RFC v3 13/45] drm/mediatek: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-14-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_gem.c | 13 ++++++-------
 drivers/gpu/drm/mediatek/mtk_drm_gem.h |  2 +-
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
index fa2ec0cd00e8..7abc550ebc00 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -54,15 +54,14 @@ struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
 
 	obj = &mtk_gem->base;
 
-	init_dma_attrs(&mtk_gem->dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
+	mtk_gem->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (!alloc_kmap)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
+		mtk_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
 					  &mtk_gem->dma_addr, GFP_KERNEL,
-					  &mtk_gem->dma_attrs);
+					  mtk_gem->dma_attrs);
 	if (!mtk_gem->cookie) {
 		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
 		ret = -ENOMEM;
@@ -93,7 +92,7 @@ void mtk_drm_gem_free_object(struct drm_gem_object *obj)
 		drm_prime_gem_destroy(obj, mtk_gem->sg);
 	else
 		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
-			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
+			       mtk_gem->dma_addr, mtk_gem->dma_attrs);
 
 	/* release file pointer to gem object. */
 	drm_gem_object_release(obj);
@@ -173,7 +172,7 @@ static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
-			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
+			     mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs);
 	if (ret)
 		drm_gem_vm_close(vma);
 
@@ -224,7 +223,7 @@ struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
 
 	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
 				    mtk_gem->dma_addr, obj->size,
-				    &mtk_gem->dma_attrs);
+				    mtk_gem->dma_attrs);
 	if (ret) {
 		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 		kfree(sgt);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
index 3a2a5624a1cb..2752718fa5b2 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -35,7 +35,7 @@ struct mtk_drm_gem_obj {
 	void			*cookie;
 	void			*kvaddr;
 	dma_addr_t		dma_addr;
-	struct dma_attrs	dma_attrs;
+	unsigned long		dma_attrs;
 	struct sg_table		*sg;
 };
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:14 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:14 +0200
Subject: [RFC v3 12/45] drm/exynos: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-13-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c |  2 +-
 drivers/gpu/drm/exynos/exynos_drm_g2d.c   | 12 +++++-------
 drivers/gpu/drm/exynos/exynos_drm_gem.c   | 20 ++++++++++----------
 drivers/gpu/drm/exynos/exynos_drm_gem.h   |  2 +-
 4 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 67dcd6831291..dd091175fc2d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -52,7 +52,7 @@ static int exynos_drm_fb_mmap(struct fb_info *info,
 
 	ret = dma_mmap_attrs(to_dma_dev(helper->dev), vma, exynos_gem->cookie,
 			     exynos_gem->dma_addr, exynos_gem->size,
-			     &exynos_gem->dma_attrs);
+			     exynos_gem->dma_attrs);
 	if (ret < 0) {
 		DRM_ERROR("failed to mmap.\n");
 		return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
index 493552368295..15539aea8415 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c
@@ -17,7 +17,6 @@
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/dma-mapping.h>
-#include <linux/dma-attrs.h>
 #include <linux/of.h>
 
 #include <drm/drmP.h>
@@ -235,7 +234,7 @@ struct g2d_data {
 	struct mutex			cmdlist_mutex;
 	dma_addr_t			cmdlist_pool;
 	void				*cmdlist_pool_virt;
-	struct dma_attrs		cmdlist_dma_attrs;
+	unsigned long			cmdlist_dma_attrs;
 
 	/* runqueue*/
 	struct g2d_runqueue_node	*runqueue_node;
@@ -256,13 +255,12 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)
 	int ret;
 	struct g2d_buf_info *buf_info;
 
-	init_dma_attrs(&g2d->cmdlist_dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs);
+	g2d->cmdlist_dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	g2d->cmdlist_pool_virt = dma_alloc_attrs(to_dma_dev(subdrv->drm_dev),
 						G2D_CMDLIST_POOL_SIZE,
 						&g2d->cmdlist_pool, GFP_KERNEL,
-						&g2d->cmdlist_dma_attrs);
+						g2d->cmdlist_dma_attrs);
 	if (!g2d->cmdlist_pool_virt) {
 		dev_err(dev, "failed to allocate dma memory\n");
 		return -ENOMEM;
@@ -295,7 +293,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)
 err:
 	dma_free_attrs(to_dma_dev(subdrv->drm_dev), G2D_CMDLIST_POOL_SIZE,
 			g2d->cmdlist_pool_virt,
-			g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+			g2d->cmdlist_pool, g2d->cmdlist_dma_attrs);
 	return ret;
 }
 
@@ -309,7 +307,7 @@ static void g2d_fini_cmdlist(struct g2d_data *g2d)
 		dma_free_attrs(to_dma_dev(subdrv->drm_dev),
 				G2D_CMDLIST_POOL_SIZE,
 				g2d->cmdlist_pool_virt,
-				g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
+				g2d->cmdlist_pool, g2d->cmdlist_dma_attrs);
 	}
 }
 
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index cdf9f1af4347..f2ae72ba7d5a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -24,7 +24,7 @@
 static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 {
 	struct drm_device *dev = exynos_gem->base.dev;
-	enum dma_attr attr;
+	unsigned long attr;
 	unsigned int nr_pages;
 	struct sg_table sgt;
 	int ret = -ENOMEM;
@@ -34,7 +34,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 		return 0;
 	}
 
-	init_dma_attrs(&exynos_gem->dma_attrs);
+	exynos_gem->dma_attrs = 0;
 
 	/*
 	 * if EXYNOS_BO_CONTIG, fully physically contiguous memory
@@ -42,7 +42,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 	 * as possible.
 	 */
 	if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG))
-		dma_set_attr(DMA_ATTR_FORCE_CONTIGUOUS, &exynos_gem->dma_attrs);
+		exynos_gem->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
 
 	/*
 	 * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping
@@ -54,8 +54,8 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 	else
 		attr = DMA_ATTR_NON_CONSISTENT;
 
-	dma_set_attr(attr, &exynos_gem->dma_attrs);
-	dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &exynos_gem->dma_attrs);
+	exynos_gem->dma_attrs |= attr;
+	exynos_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	nr_pages = exynos_gem->size >> PAGE_SHIFT;
 
@@ -67,7 +67,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 
 	exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size,
 					     &exynos_gem->dma_addr, GFP_KERNEL,
-					     &exynos_gem->dma_attrs);
+					     exynos_gem->dma_attrs);
 	if (!exynos_gem->cookie) {
 		DRM_ERROR("failed to allocate buffer.\n");
 		goto err_free;
@@ -75,7 +75,7 @@ static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem)
 
 	ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt, exynos_gem->cookie,
 				    exynos_gem->dma_addr, exynos_gem->size,
-				    &exynos_gem->dma_attrs);
+				    exynos_gem->dma_attrs);
 	if (ret < 0) {
 		DRM_ERROR("failed to get sgtable.\n");
 		goto err_dma_free;
@@ -99,7 +99,7 @@ err_sgt_free:
 	sg_free_table(&sgt);
 err_dma_free:
 	dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie,
-		       exynos_gem->dma_addr, &exynos_gem->dma_attrs);
+		       exynos_gem->dma_addr, exynos_gem->dma_attrs);
 err_free:
 	drm_free_large(exynos_gem->pages);
 
@@ -120,7 +120,7 @@ static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem)
 
 	dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie,
 			(dma_addr_t)exynos_gem->dma_addr,
-			&exynos_gem->dma_attrs);
+			exynos_gem->dma_attrs);
 
 	drm_free_large(exynos_gem->pages);
 }
@@ -346,7 +346,7 @@ static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem,
 
 	ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie,
 			     exynos_gem->dma_addr, exynos_gem->size,
-			     &exynos_gem->dma_attrs);
+			     exynos_gem->dma_attrs);
 	if (ret < 0) {
 		DRM_ERROR("failed to mmap.\n");
 		return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 78100742281d..df7c543d6558 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -50,7 +50,7 @@ struct exynos_drm_gem {
 	void			*cookie;
 	void __iomem		*kvaddr;
 	dma_addr_t		dma_addr;
-	struct dma_attrs	dma_attrs;
+	unsigned long		dma_attrs;
 	struct page		**pages;
 	struct sg_table		*sgt;
 };
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:16 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:16 +0200
Subject: [RFC v3 14/45] drm/msm: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-15-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/msm/msm_drv.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 1f7de47d817e..9b806e576d35 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -230,11 +230,10 @@ static int msm_drm_uninit(struct device *dev)
 	}
 
 	if (priv->vram.paddr) {
-		DEFINE_DMA_ATTRS(attrs);
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+		unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING;
 		drm_mm_takedown(&priv->vram.mm);
 		dma_free_attrs(dev, priv->vram.size, NULL,
-			       priv->vram.paddr, &attrs);
+			       priv->vram.paddr, attrs);
 	}
 
 	component_unbind_all(dev, ddev);
@@ -299,21 +298,21 @@ static int msm_init_vram(struct drm_device *dev)
 	}
 
 	if (size) {
-		DEFINE_DMA_ATTRS(attrs);
+		unsigned long attrs = 0;
 		void *p;
 
 		priv->vram.size = size;
 
 		drm_mm_init(&priv->vram.mm, 0, (size >> PAGE_SHIFT) - 1);
 
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
-		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+		attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+		attrs |= DMA_ATTR_WRITE_COMBINE;
 
 		/* note that for no-kernel-mapping, the vaddr returned
 		 * is bogus, but non-null if allocation succeeded:
 		 */
 		p = dma_alloc_attrs(dev->dev, size,
-				&priv->vram.paddr, GFP_KERNEL, &attrs);
+				&priv->vram.paddr, GFP_KERNEL, attrs);
 		if (!p) {
 			dev_err(dev->dev, "failed to allocate VRAM\n");
 			priv->vram.paddr = 0;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:17 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:17 +0200
Subject: [RFC v3 15/45] drm/nouveau: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-16-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c
index 6b8f2a19b2d9..a6a7fa0d7679 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c
@@ -109,7 +109,7 @@ struct gk20a_instmem {
 	u16 iommu_bit;
 
 	/* Only used by DMA API */
-	struct dma_attrs attrs;
+	unsigned long attrs;
 };
 #define gk20a_instmem(p) container_of((p), struct gk20a_instmem, base)
 
@@ -293,7 +293,7 @@ gk20a_instobj_dtor_dma(struct nvkm_memory *memory)
 		goto out;
 
 	dma_free_attrs(dev, node->base.mem.size << PAGE_SHIFT, node->base.vaddr,
-		       node->handle, &imem->attrs);
+		       node->handle, imem->attrs);
 
 out:
 	return node;
@@ -386,7 +386,7 @@ gk20a_instobj_ctor_dma(struct gk20a_instmem *imem, u32 npages, u32 align,
 
 	node->base.vaddr = dma_alloc_attrs(dev, npages << PAGE_SHIFT,
 					   &node->handle, GFP_KERNEL,
-					   &imem->attrs);
+					   imem->attrs);
 	if (!node->base.vaddr) {
 		nvkm_error(subdev, "cannot allocate DMA memory\n");
 		return -ENOMEM;
@@ -597,10 +597,9 @@ gk20a_instmem_new(struct nvkm_device *device, int index,
 
 		nvkm_info(&imem->base.subdev, "using IOMMU\n");
 	} else {
-		init_dma_attrs(&imem->attrs);
-		dma_set_attr(DMA_ATTR_NON_CONSISTENT, &imem->attrs);
-		dma_set_attr(DMA_ATTR_WEAK_ORDERING, &imem->attrs);
-		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &imem->attrs);
+		imem->attrs = DMA_ATTR_NON_CONSISTENT |
+			      DMA_ATTR_WEAK_ORDERING |
+			      DMA_ATTR_WRITE_COMBINE;
 
 		nvkm_info(&imem->base.subdev, "using DMA API\n");
 	}
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:18 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:18 +0200
Subject: [RFC v3 16/45] drm/rockship: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-17-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c | 17 +++++++----------
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h |  2 +-
 2 files changed, 8 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 9c2d8a894093..7b1788e2a808 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -17,8 +17,6 @@
 #include <drm/drm_gem.h>
 #include <drm/drm_vma_manager.h>
 
-#include <linux/dma-attrs.h>
-
 #include "rockchip_drm_drv.h"
 #include "rockchip_drm_gem.h"
 
@@ -28,15 +26,14 @@ static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
 	struct drm_gem_object *obj = &rk_obj->base;
 	struct drm_device *drm = obj->dev;
 
-	init_dma_attrs(&rk_obj->dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs);
+	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (!alloc_kmap)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs);
+		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
 					 &rk_obj->dma_addr, GFP_KERNEL,
-					 &rk_obj->dma_attrs);
+					 rk_obj->dma_attrs);
 	if (!rk_obj->kvaddr) {
 		DRM_ERROR("failed to allocate %#x byte dma buffer", obj->size);
 		return -ENOMEM;
@@ -51,7 +48,7 @@ static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
 	struct drm_device *drm = obj->dev;
 
 	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
-		       &rk_obj->dma_attrs);
+		       rk_obj->dma_attrs);
 }
 
 static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
@@ -70,7 +67,7 @@ static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
-			     obj->size, &rk_obj->dma_attrs);
+			     obj->size, rk_obj->dma_attrs);
 	if (ret)
 		drm_gem_vm_close(vma);
 
@@ -262,7 +259,7 @@ struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
 
 	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
 				    rk_obj->dma_addr, obj->size,
-				    &rk_obj->dma_attrs);
+				    rk_obj->dma_attrs);
 	if (ret) {
 		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 		kfree(sgt);
@@ -276,7 +273,7 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
 {
 	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs))
+	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, rk_obj->dma_attrs))
 		return NULL;
 
 	return rk_obj->kvaddr;
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
index ad22618473a4..18b3488db4ec 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.h
@@ -23,7 +23,7 @@ struct rockchip_gem_object {
 
 	void *kvaddr;
 	dma_addr_t dma_addr;
-	struct dma_attrs dma_attrs;
+	unsigned long dma_attrs;
 };
 
 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj);
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:22 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:22 +0200
Subject: [RFC v3 20/45] xen: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-21-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/xen/swiotlb-xen.c | 14 +++++++-------
 include/xen/swiotlb-xen.h | 12 ++++++------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/drivers/xen/swiotlb-xen.c b/drivers/xen/swiotlb-xen.c
index 7399782c0998..87e6035c9e81 100644
--- a/drivers/xen/swiotlb-xen.c
+++ b/drivers/xen/swiotlb-xen.c
@@ -294,7 +294,7 @@ error:
 void *
 xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 			   dma_addr_t *dma_handle, gfp_t flags,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	void *ret;
 	int order = get_order(size);
@@ -346,7 +346,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_alloc_coherent);
 
 void
 xen_swiotlb_free_coherent(struct device *hwdev, size_t size, void *vaddr,
-			  dma_addr_t dev_addr, struct dma_attrs *attrs)
+			  dma_addr_t dev_addr, unsigned long attrs)
 {
 	int order = get_order(size);
 	phys_addr_t phys;
@@ -378,7 +378,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_free_coherent);
 dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
 				unsigned long offset, size_t size,
 				enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	phys_addr_t map, phys = page_to_phys(page) + offset;
 	dma_addr_t dev_addr = xen_phys_to_bus(phys);
@@ -434,7 +434,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_map_page);
  */
 static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
 			     size_t size, enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	phys_addr_t paddr = xen_bus_to_phys(dev_addr);
 
@@ -462,7 +462,7 @@ static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
 
 void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
 			    size_t size, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	xen_unmap_single(hwdev, dev_addr, size, dir, attrs);
 }
@@ -538,7 +538,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_sync_single_for_device);
 int
 xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
 			 int nelems, enum dma_data_direction dir,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -599,7 +599,7 @@ EXPORT_SYMBOL_GPL(xen_swiotlb_map_sg_attrs);
 void
 xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
 			   int nelems, enum dma_data_direction dir,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
diff --git a/include/xen/swiotlb-xen.h b/include/xen/swiotlb-xen.h
index 8b2eb93ae8ba..7c35e279d1e3 100644
--- a/include/xen/swiotlb-xen.h
+++ b/include/xen/swiotlb-xen.h
@@ -9,30 +9,30 @@ extern int xen_swiotlb_init(int verbose, bool early);
 extern void
 *xen_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 			    dma_addr_t *dma_handle, gfp_t flags,
-			    struct dma_attrs *attrs);
+			    unsigned long attrs);
 
 extern void
 xen_swiotlb_free_coherent(struct device *hwdev, size_t size,
 			  void *vaddr, dma_addr_t dma_handle,
-			  struct dma_attrs *attrs);
+			  unsigned long attrs);
 
 extern dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
 				       unsigned long offset, size_t size,
 				       enum dma_data_direction dir,
-				       struct dma_attrs *attrs);
+				       unsigned long attrs);
 
 extern void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
 				   size_t size, enum dma_data_direction dir,
-				   struct dma_attrs *attrs);
+				   unsigned long attrs);
 extern int
 xen_swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
 			 int nelems, enum dma_data_direction dir,
-			 struct dma_attrs *attrs);
+			 unsigned long attrs);
 
 extern void
 xen_swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
 			   int nelems, enum dma_data_direction dir,
-			   struct dma_attrs *attrs);
+			   unsigned long attrs);
 
 extern void
 xen_swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr,
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:24 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:24 +0200
Subject: [RFC v3 22/45] powerpc: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-23-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/powerpc/include/asm/dma-mapping.h    |  7 +++----
 arch/powerpc/include/asm/iommu.h          | 10 +++++-----
 arch/powerpc/kernel/dma-iommu.c           | 12 ++++++------
 arch/powerpc/kernel/dma.c                 | 18 +++++++++---------
 arch/powerpc/kernel/ibmebus.c             | 12 ++++++------
 arch/powerpc/kernel/iommu.c               | 12 ++++++------
 arch/powerpc/kernel/vio.c                 | 12 ++++++------
 arch/powerpc/platforms/cell/iommu.c       | 14 +++++++-------
 arch/powerpc/platforms/pasemi/iommu.c     |  2 +-
 arch/powerpc/platforms/powernv/npu-dma.c  |  8 ++++----
 arch/powerpc/platforms/powernv/pci-ioda.c |  4 ++--
 arch/powerpc/platforms/powernv/pci.c      |  2 +-
 arch/powerpc/platforms/powernv/pci.h      |  2 +-
 arch/powerpc/platforms/ps3/system-bus.c   | 18 +++++++++---------
 arch/powerpc/platforms/pseries/iommu.c    |  6 +++---
 arch/powerpc/sysdev/dart_iommu.c          |  2 +-
 16 files changed, 70 insertions(+), 71 deletions(-)

diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h
index 77816acd4fd9..84e3f8dd5e4f 100644
--- a/arch/powerpc/include/asm/dma-mapping.h
+++ b/arch/powerpc/include/asm/dma-mapping.h
@@ -13,7 +13,6 @@
 /* need struct page definitions */
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-debug.h>
 #include <asm/io.h>
 #include <asm/swiotlb.h>
@@ -25,14 +24,14 @@
 /* Some dma direct funcs must be visible for use in other dma_ops */
 extern void *__dma_direct_alloc_coherent(struct device *dev, size_t size,
 					 dma_addr_t *dma_handle, gfp_t flag,
-					 struct dma_attrs *attrs);
+					 unsigned long attrs);
 extern void __dma_direct_free_coherent(struct device *dev, size_t size,
 				       void *vaddr, dma_addr_t dma_handle,
-				       struct dma_attrs *attrs);
+				       unsigned long attrs);
 extern int dma_direct_mmap_coherent(struct device *dev,
 				    struct vm_area_struct *vma,
 				    void *cpu_addr, dma_addr_t handle,
-				    size_t size, struct dma_attrs *attrs);
+				    size_t size, unsigned long attrs);
 
 #ifdef CONFIG_NOT_COHERENT_CACHE
 /*
diff --git a/arch/powerpc/include/asm/iommu.h b/arch/powerpc/include/asm/iommu.h
index 7b87bab09564..760915241ce2 100644
--- a/arch/powerpc/include/asm/iommu.h
+++ b/arch/powerpc/include/asm/iommu.h
@@ -53,7 +53,7 @@ struct iommu_table_ops {
 			long index, long npages,
 			unsigned long uaddr,
 			enum dma_data_direction direction,
-			struct dma_attrs *attrs);
+			unsigned long attrs);
 #ifdef CONFIG_IOMMU_API
 	/*
 	 * Exchanges existing TCE with new TCE plus direction bits;
@@ -248,12 +248,12 @@ extern int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 			    struct scatterlist *sglist, int nelems,
 			    unsigned long mask,
 			    enum dma_data_direction direction,
-			    struct dma_attrs *attrs);
+			    unsigned long attrs);
 extern void ppc_iommu_unmap_sg(struct iommu_table *tbl,
 			       struct scatterlist *sglist,
 			       int nelems,
 			       enum dma_data_direction direction,
-			       struct dma_attrs *attrs);
+			       unsigned long attrs);
 
 extern void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
 				  size_t size, dma_addr_t *dma_handle,
@@ -264,10 +264,10 @@ extern dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
 				 struct page *page, unsigned long offset,
 				 size_t size, unsigned long mask,
 				 enum dma_data_direction direction,
-				 struct dma_attrs *attrs);
+				 unsigned long attrs);
 extern void iommu_unmap_page(struct iommu_table *tbl, dma_addr_t dma_handle,
 			     size_t size, enum dma_data_direction direction,
-			     struct dma_attrs *attrs);
+			     unsigned long attrs);
 
 extern void iommu_init_early_pSeries(void);
 extern void iommu_init_early_dart(struct pci_controller_ops *controller_ops);
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c
index 41a7d9d49a5a..fb7cbaa37658 100644
--- a/arch/powerpc/kernel/dma-iommu.c
+++ b/arch/powerpc/kernel/dma-iommu.c
@@ -18,7 +18,7 @@
  */
 static void *dma_iommu_alloc_coherent(struct device *dev, size_t size,
 				      dma_addr_t *dma_handle, gfp_t flag,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	return iommu_alloc_coherent(dev, get_iommu_table_base(dev), size,
 				    dma_handle, dev->coherent_dma_mask, flag,
@@ -27,7 +27,7 @@ static void *dma_iommu_alloc_coherent(struct device *dev, size_t size,
 
 static void dma_iommu_free_coherent(struct device *dev, size_t size,
 				    void *vaddr, dma_addr_t dma_handle,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	iommu_free_coherent(get_iommu_table_base(dev), size, vaddr, dma_handle);
 }
@@ -40,7 +40,7 @@ static void dma_iommu_free_coherent(struct device *dev, size_t size,
 static dma_addr_t dma_iommu_map_page(struct device *dev, struct page *page,
 				     unsigned long offset, size_t size,
 				     enum dma_data_direction direction,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	return iommu_map_page(dev, get_iommu_table_base(dev), page, offset,
 			      size, device_to_mask(dev), direction, attrs);
@@ -49,7 +49,7 @@ static dma_addr_t dma_iommu_map_page(struct device *dev, struct page *page,
 
 static void dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
 				 size_t size, enum dma_data_direction direction,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	iommu_unmap_page(get_iommu_table_base(dev), dma_handle, size, direction,
 			 attrs);
@@ -58,7 +58,7 @@ static void dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
 
 static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
 			    int nelems, enum dma_data_direction direction,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	return ppc_iommu_map_sg(dev, get_iommu_table_base(dev), sglist, nelems,
 				device_to_mask(dev), direction, attrs);
@@ -66,7 +66,7 @@ static int dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static void dma_iommu_unmap_sg(struct device *dev, struct scatterlist *sglist,
 		int nelems, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	ppc_iommu_unmap_sg(get_iommu_table_base(dev), sglist, nelems,
 			   direction, attrs);
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma.c
index 3f1472a78f39..e64a6016fba7 100644
--- a/arch/powerpc/kernel/dma.c
+++ b/arch/powerpc/kernel/dma.c
@@ -64,7 +64,7 @@ static int dma_direct_dma_supported(struct device *dev, u64 mask)
 
 void *__dma_direct_alloc_coherent(struct device *dev, size_t size,
 				  dma_addr_t *dma_handle, gfp_t flag,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	void *ret;
 #ifdef CONFIG_NOT_COHERENT_CACHE
@@ -121,7 +121,7 @@ void *__dma_direct_alloc_coherent(struct device *dev, size_t size,
 
 void __dma_direct_free_coherent(struct device *dev, size_t size,
 				void *vaddr, dma_addr_t dma_handle,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 #ifdef CONFIG_NOT_COHERENT_CACHE
 	__dma_free_coherent(size, vaddr);
@@ -132,7 +132,7 @@ void __dma_direct_free_coherent(struct device *dev, size_t size,
 
 static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 				       dma_addr_t *dma_handle, gfp_t flag,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	struct iommu_table *iommu;
 
@@ -156,7 +156,7 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 
 static void dma_direct_free_coherent(struct device *dev, size_t size,
 				     void *vaddr, dma_addr_t dma_handle,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct iommu_table *iommu;
 
@@ -177,7 +177,7 @@ static void dma_direct_free_coherent(struct device *dev, size_t size,
 
 int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
 			     void *cpu_addr, dma_addr_t handle, size_t size,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	unsigned long pfn;
 
@@ -195,7 +195,7 @@ int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
 
 static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
 			     int nents, enum dma_data_direction direction,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -211,7 +211,7 @@ static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
 
 static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
 				int nents, enum dma_data_direction direction,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 }
 
@@ -232,7 +232,7 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev,
 					     unsigned long offset,
 					     size_t size,
 					     enum dma_data_direction dir,
-					     struct dma_attrs *attrs)
+					     unsigned long attrs)
 {
 	BUG_ON(dir == DMA_NONE);
 	__dma_sync_page(page, offset, size, dir);
@@ -243,7 +243,7 @@ static inline void dma_direct_unmap_page(struct device *dev,
 					 dma_addr_t dma_address,
 					 size_t size,
 					 enum dma_data_direction direction,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 }
 
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index a89f4f7a66bd..c1ca9282f4a0 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -65,7 +65,7 @@ static void *ibmebus_alloc_coherent(struct device *dev,
 				    size_t size,
 				    dma_addr_t *dma_handle,
 				    gfp_t flag,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	void *mem;
 
@@ -78,7 +78,7 @@ static void *ibmebus_alloc_coherent(struct device *dev,
 static void ibmebus_free_coherent(struct device *dev,
 				  size_t size, void *vaddr,
 				  dma_addr_t dma_handle,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	kfree(vaddr);
 }
@@ -88,7 +88,7 @@ static dma_addr_t ibmebus_map_page(struct device *dev,
 				   unsigned long offset,
 				   size_t size,
 				   enum dma_data_direction direction,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	return (dma_addr_t)(page_address(page) + offset);
 }
@@ -97,7 +97,7 @@ static void ibmebus_unmap_page(struct device *dev,
 			       dma_addr_t dma_addr,
 			       size_t size,
 			       enum dma_data_direction direction,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	return;
 }
@@ -105,7 +105,7 @@ static void ibmebus_unmap_page(struct device *dev,
 static int ibmebus_map_sg(struct device *dev,
 			  struct scatterlist *sgl,
 			  int nents, enum dma_data_direction direction,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -121,7 +121,7 @@ static int ibmebus_map_sg(struct device *dev,
 static void ibmebus_unmap_sg(struct device *dev,
 			     struct scatterlist *sg,
 			     int nents, enum dma_data_direction direction,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	return;
 }
diff --git a/arch/powerpc/kernel/iommu.c b/arch/powerpc/kernel/iommu.c
index a8e3490b54e3..37d6e741be82 100644
--- a/arch/powerpc/kernel/iommu.c
+++ b/arch/powerpc/kernel/iommu.c
@@ -307,7 +307,7 @@ static dma_addr_t iommu_alloc(struct device *dev, struct iommu_table *tbl,
 			      void *page, unsigned int npages,
 			      enum dma_data_direction direction,
 			      unsigned long mask, unsigned int align_order,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	unsigned long entry;
 	dma_addr_t ret = DMA_ERROR_CODE;
@@ -431,7 +431,7 @@ static void iommu_free(struct iommu_table *tbl, dma_addr_t dma_addr,
 int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 		     struct scatterlist *sglist, int nelems,
 		     unsigned long mask, enum dma_data_direction direction,
-		     struct dma_attrs *attrs)
+		     unsigned long attrs)
 {
 	dma_addr_t dma_next = 0, dma_addr;
 	struct scatterlist *s, *outs, *segstart;
@@ -574,7 +574,7 @@ int ppc_iommu_map_sg(struct device *dev, struct iommu_table *tbl,
 
 void ppc_iommu_unmap_sg(struct iommu_table *tbl, struct scatterlist *sglist,
 			int nelems, enum dma_data_direction direction,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	struct scatterlist *sg;
 
@@ -753,7 +753,7 @@ void iommu_free_table(struct iommu_table *tbl, const char *node_name)
 dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
 			  struct page *page, unsigned long offset, size_t size,
 			  unsigned long mask, enum dma_data_direction direction,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	dma_addr_t dma_handle = DMA_ERROR_CODE;
 	void *vaddr;
@@ -790,7 +790,7 @@ dma_addr_t iommu_map_page(struct device *dev, struct iommu_table *tbl,
 
 void iommu_unmap_page(struct iommu_table *tbl, dma_addr_t dma_handle,
 		      size_t size, enum dma_data_direction direction,
-		      struct dma_attrs *attrs)
+		      unsigned long attrs)
 {
 	unsigned int npages;
 
@@ -845,7 +845,7 @@ void *iommu_alloc_coherent(struct device *dev, struct iommu_table *tbl,
 	nio_pages = size >> tbl->it_page_shift;
 	io_order = get_iommu_order(size, tbl);
 	mapping = iommu_alloc(dev, tbl, ret, nio_pages, DMA_BIDIRECTIONAL,
-			      mask >> tbl->it_page_shift, io_order, NULL);
+			      mask >> tbl->it_page_shift, io_order, 0);
 	if (mapping == DMA_ERROR_CODE) {
 		free_pages((unsigned long)ret, order);
 		return NULL;
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index 8d7358f3a273..b3813ddb2fb4 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -482,7 +482,7 @@ static void vio_cmo_balance(struct work_struct *work)
 
 static void *vio_dma_iommu_alloc_coherent(struct device *dev, size_t size,
 					  dma_addr_t *dma_handle, gfp_t flag,
-					  struct dma_attrs *attrs)
+					  unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 	void *ret;
@@ -503,7 +503,7 @@ static void *vio_dma_iommu_alloc_coherent(struct device *dev, size_t size,
 
 static void vio_dma_iommu_free_coherent(struct device *dev, size_t size,
 					void *vaddr, dma_addr_t dma_handle,
-					struct dma_attrs *attrs)
+					unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 
@@ -515,7 +515,7 @@ static void vio_dma_iommu_free_coherent(struct device *dev, size_t size,
 static dma_addr_t vio_dma_iommu_map_page(struct device *dev, struct page *page,
                                          unsigned long offset, size_t size,
                                          enum dma_data_direction direction,
-                                         struct dma_attrs *attrs)
+                                         unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 	struct iommu_table *tbl;
@@ -539,7 +539,7 @@ static dma_addr_t vio_dma_iommu_map_page(struct device *dev, struct page *page,
 static void vio_dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
 				     size_t size,
 				     enum dma_data_direction direction,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 	struct iommu_table *tbl;
@@ -552,7 +552,7 @@ static void vio_dma_iommu_unmap_page(struct device *dev, dma_addr_t dma_handle,
 
 static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
                                 int nelems, enum dma_data_direction direction,
-                                struct dma_attrs *attrs)
+                                unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 	struct iommu_table *tbl;
@@ -588,7 +588,7 @@ static int vio_dma_iommu_map_sg(struct device *dev, struct scatterlist *sglist,
 static void vio_dma_iommu_unmap_sg(struct device *dev,
 		struct scatterlist *sglist, int nelems,
 		enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	struct vio_dev *viodev = to_vio_dev(dev);
 	struct iommu_table *tbl;
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 0c2794d2b6c0..5d1a7ef3fdee 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -166,7 +166,7 @@ static void invalidate_tce_cache(struct cbe_iommu *iommu, unsigned long *pte,
 
 static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
 		unsigned long uaddr, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 	unsigned long *io_pte, base_pte;
@@ -572,7 +572,7 @@ static struct iommu_table *cell_get_iommu_table(struct device *dev)
 
 static void *dma_fixed_alloc_coherent(struct device *dev, size_t size,
 				      dma_addr_t *dma_handle, gfp_t flag,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	if (iommu_fixed_is_weak)
 		return iommu_alloc_coherent(dev, cell_get_iommu_table(dev),
@@ -586,7 +586,7 @@ static void *dma_fixed_alloc_coherent(struct device *dev, size_t size,
 
 static void dma_fixed_free_coherent(struct device *dev, size_t size,
 				    void *vaddr, dma_addr_t dma_handle,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	if (iommu_fixed_is_weak)
 		iommu_free_coherent(cell_get_iommu_table(dev), size, vaddr,
@@ -598,7 +598,7 @@ static void dma_fixed_free_coherent(struct device *dev, size_t size,
 static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page,
 				     unsigned long offset, size_t size,
 				     enum dma_data_direction direction,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		return dma_direct_ops.map_page(dev, page, offset, size,
@@ -611,7 +611,7 @@ static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page,
 
 static void dma_fixed_unmap_page(struct device *dev, dma_addr_t dma_addr,
 				 size_t size, enum dma_data_direction direction,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		dma_direct_ops.unmap_page(dev, dma_addr, size, direction,
@@ -623,7 +623,7 @@ static void dma_fixed_unmap_page(struct device *dev, dma_addr_t dma_addr,
 
 static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
 			   int nents, enum dma_data_direction direction,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
@@ -635,7 +635,7 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
 
 static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
 			       int nents, enum dma_data_direction direction,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
 		dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c
index c929644e74a6..efab5568cfef 100644
--- a/arch/powerpc/platforms/pasemi/iommu.c
+++ b/arch/powerpc/platforms/pasemi/iommu.c
@@ -88,7 +88,7 @@ static int iommu_table_iobmap_inited;
 static int iobmap_build(struct iommu_table *tbl, long index,
 			 long npages, unsigned long uaddr,
 			 enum dma_data_direction direction,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	u32 *ip;
 	u32 rpn;
diff --git a/arch/powerpc/platforms/powernv/npu-dma.c b/arch/powerpc/platforms/powernv/npu-dma.c
index 0459e100b4e7..bba4c53aaea7 100644
--- a/arch/powerpc/platforms/powernv/npu-dma.c
+++ b/arch/powerpc/platforms/powernv/npu-dma.c
@@ -73,7 +73,7 @@ EXPORT_SYMBOL(pnv_pci_get_npu_dev);
 
 static void *dma_npu_alloc(struct device *dev, size_t size,
 			   dma_addr_t *dma_handle, gfp_t flag,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	NPU_DMA_OP_UNSUPPORTED();
 	return NULL;
@@ -81,7 +81,7 @@ static void *dma_npu_alloc(struct device *dev, size_t size,
 
 static void dma_npu_free(struct device *dev, size_t size,
 			 void *vaddr, dma_addr_t dma_handle,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	NPU_DMA_OP_UNSUPPORTED();
 }
@@ -89,7 +89,7 @@ static void dma_npu_free(struct device *dev, size_t size,
 static dma_addr_t dma_npu_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction direction,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	NPU_DMA_OP_UNSUPPORTED();
 	return 0;
@@ -97,7 +97,7 @@ static dma_addr_t dma_npu_map_page(struct device *dev, struct page *page,
 
 static int dma_npu_map_sg(struct device *dev, struct scatterlist *sglist,
 			  int nelems, enum dma_data_direction direction,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	NPU_DMA_OP_UNSUPPORTED();
 	return 0;
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 3a5ea8236db8..40ef7e21439d 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -1783,7 +1783,7 @@ static void pnv_pci_ioda1_tce_invalidate(struct iommu_table *tbl,
 static int pnv_ioda1_tce_build(struct iommu_table *tbl, long index,
 		long npages, unsigned long uaddr,
 		enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
 			attrs);
@@ -1912,7 +1912,7 @@ static void pnv_pci_ioda2_tce_invalidate(struct iommu_table *tbl,
 static int pnv_ioda2_tce_build(struct iommu_table *tbl, long index,
 		long npages, unsigned long uaddr,
 		enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int ret = pnv_tce_build(tbl, index, npages, uaddr, direction,
 			attrs);
diff --git a/arch/powerpc/platforms/powernv/pci.c b/arch/powerpc/platforms/powernv/pci.c
index 1d92bd93bcd9..bb16ebbfca77 100644
--- a/arch/powerpc/platforms/powernv/pci.c
+++ b/arch/powerpc/platforms/powernv/pci.c
@@ -587,7 +587,7 @@ static __be64 *pnv_tce(struct iommu_table *tbl, long idx)
 
 int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
 		unsigned long uaddr, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	u64 proto_tce = iommu_direction_to_tce_perm(direction);
 	u64 rpn = __pa(uaddr) >> tbl->it_page_shift;
diff --git a/arch/powerpc/platforms/powernv/pci.h b/arch/powerpc/platforms/powernv/pci.h
index 7dee25e304db..9db1d1ab2549 100644
--- a/arch/powerpc/platforms/powernv/pci.h
+++ b/arch/powerpc/platforms/powernv/pci.h
@@ -178,7 +178,7 @@ struct pnv_phb {
 extern struct pci_ops pnv_pci_ops;
 extern int pnv_tce_build(struct iommu_table *tbl, long index, long npages,
 		unsigned long uaddr, enum dma_data_direction direction,
-		struct dma_attrs *attrs);
+		unsigned long attrs);
 extern void pnv_tce_free(struct iommu_table *tbl, long index, long npages);
 extern int pnv_tce_xchg(struct iommu_table *tbl, long index,
 		unsigned long *hpa, enum dma_data_direction *direction);
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c
index 5606fe36faf2..8af1c15aef85 100644
--- a/arch/powerpc/platforms/ps3/system-bus.c
+++ b/arch/powerpc/platforms/ps3/system-bus.c
@@ -516,7 +516,7 @@ core_initcall(ps3_system_bus_init);
  */
 static void * ps3_alloc_coherent(struct device *_dev, size_t size,
 				 dma_addr_t *dma_handle, gfp_t flag,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	int result;
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
@@ -553,7 +553,7 @@ clean_none:
 }
 
 static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
-			      dma_addr_t dma_handle, struct dma_attrs *attrs)
+			      dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
 
@@ -569,7 +569,7 @@ static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr,
 
 static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
 	unsigned long offset, size_t size, enum dma_data_direction direction,
-	struct dma_attrs *attrs)
+	unsigned long attrs)
 {
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
 	int result;
@@ -592,7 +592,7 @@ static dma_addr_t ps3_sb_map_page(struct device *_dev, struct page *page,
 static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
 				    unsigned long offset, size_t size,
 				    enum dma_data_direction direction,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
 	int result;
@@ -626,7 +626,7 @@ static dma_addr_t ps3_ioc0_map_page(struct device *_dev, struct page *page,
 }
 
 static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr,
-	size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)
+	size_t size, enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ps3_system_bus_device *dev = ps3_dev_to_system_bus_dev(_dev);
 	int result;
@@ -640,7 +640,7 @@ static void ps3_unmap_page(struct device *_dev, dma_addr_t dma_addr,
 }
 
 static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl,
-	int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
+	int nents, enum dma_data_direction direction, unsigned long attrs)
 {
 #if defined(CONFIG_PS3_DYNAMIC_DMA)
 	BUG_ON("do");
@@ -670,14 +670,14 @@ static int ps3_sb_map_sg(struct device *_dev, struct scatterlist *sgl,
 static int ps3_ioc0_map_sg(struct device *_dev, struct scatterlist *sg,
 			   int nents,
 			   enum dma_data_direction direction,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	BUG();
 	return 0;
 }
 
 static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,
-	int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
+	int nents, enum dma_data_direction direction, unsigned long attrs)
 {
 #if defined(CONFIG_PS3_DYNAMIC_DMA)
 	BUG_ON("do");
@@ -686,7 +686,7 @@ static void ps3_sb_unmap_sg(struct device *_dev, struct scatterlist *sg,
 
 static void ps3_ioc0_unmap_sg(struct device *_dev, struct scatterlist *sg,
 			    int nents, enum dma_data_direction direction,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	BUG();
 }
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index b7dfc1359d01..b52e61b840a6 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -152,7 +152,7 @@ static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
 static int tce_build_pSeries(struct iommu_table *tbl, long index,
 			      long npages, unsigned long uaddr,
 			      enum dma_data_direction direction,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	u64 proto_tce;
 	__be64 *tcep, *tces;
@@ -208,7 +208,7 @@ static void tce_freemulti_pSeriesLP(struct iommu_table*, long, long);
 static int tce_build_pSeriesLP(struct iommu_table *tbl, long tcenum,
 				long npages, unsigned long uaddr,
 				enum dma_data_direction direction,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	u64 rc = 0;
 	u64 proto_tce, tce;
@@ -251,7 +251,7 @@ static DEFINE_PER_CPU(__be64 *, tce_page);
 static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
 				     long npages, unsigned long uaddr,
 				     enum dma_data_direction direction,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	u64 rc = 0;
 	u64 proto_tce;
diff --git a/arch/powerpc/sysdev/dart_iommu.c b/arch/powerpc/sysdev/dart_iommu.c
index b7348637eae0..9c24f8e6246e 100644
--- a/arch/powerpc/sysdev/dart_iommu.c
+++ b/arch/powerpc/sysdev/dart_iommu.c
@@ -163,7 +163,7 @@ static void dart_flush(struct iommu_table *tbl)
 static int dart_build(struct iommu_table *tbl, long index,
 		       long npages, unsigned long uaddr,
 		       enum dma_data_direction direction,
-		       struct dma_attrs *attrs)
+		       unsigned long attrs)
 {
 	unsigned int *dp;
 	unsigned int rpn;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:19 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:19 +0200
Subject: [RFC v3 17/45] infiniband: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-18-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/infiniband/core/umem.c | 7 +++----
 include/rdma/ib_verbs.h        | 8 ++++----
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index fe4d2e1a8b58..75d8a8b178a5 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -37,7 +37,6 @@
 #include <linux/sched.h>
 #include <linux/export.h>
 #include <linux/hugetlb.h>
-#include <linux/dma-attrs.h>
 #include <linux/slab.h>
 #include <rdma/ib_umem_odp.h>
 
@@ -92,12 +91,12 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 	unsigned long npages;
 	int ret;
 	int i;
-	DEFINE_DMA_ATTRS(attrs);
+	unsigned long attrs = 0;
 	struct scatterlist *sg, *sg_list_start;
 	int need_release = 0;
 
 	if (dmasync)
-		dma_set_attr(DMA_ATTR_WRITE_BARRIER, &attrs);
+		attrs |= DMA_ATTR_WRITE_BARRIER;
 
 	if (!size)
 		return ERR_PTR(-EINVAL);
@@ -215,7 +214,7 @@ struct ib_umem *ib_umem_get(struct ib_ucontext *context, unsigned long addr,
 				  umem->sg_head.sgl,
 				  umem->npages,
 				  DMA_BIDIRECTIONAL,
-				  &attrs);
+				  attrs);
 
 	if (umem->nmap <= 0) {
 		ret = -ENOMEM;
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 432bed510369..4d73928962c7 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -2819,7 +2819,7 @@ static inline void ib_dma_unmap_single(struct ib_device *dev,
 static inline u64 ib_dma_map_single_attrs(struct ib_device *dev,
 					  void *cpu_addr, size_t size,
 					  enum dma_data_direction direction,
-					  struct dma_attrs *attrs)
+					  unsigned long attrs)
 {
 	return dma_map_single_attrs(dev->dma_device, cpu_addr, size,
 				    direction, attrs);
@@ -2828,7 +2828,7 @@ static inline u64 ib_dma_map_single_attrs(struct ib_device *dev,
 static inline void ib_dma_unmap_single_attrs(struct ib_device *dev,
 					     u64 addr, size_t size,
 					     enum dma_data_direction direction,
-					     struct dma_attrs *attrs)
+					     unsigned long attrs)
 {
 	return dma_unmap_single_attrs(dev->dma_device, addr, size,
 				      direction, attrs);
@@ -2906,7 +2906,7 @@ static inline void ib_dma_unmap_sg(struct ib_device *dev,
 static inline int ib_dma_map_sg_attrs(struct ib_device *dev,
 				      struct scatterlist *sg, int nents,
 				      enum dma_data_direction direction,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	return dma_map_sg_attrs(dev->dma_device, sg, nents, direction, attrs);
 }
@@ -2914,7 +2914,7 @@ static inline int ib_dma_map_sg_attrs(struct ib_device *dev,
 static inline void ib_dma_unmap_sg_attrs(struct ib_device *dev,
 					 struct scatterlist *sg, int nents,
 					 enum dma_data_direction direction,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 	dma_unmap_sg_attrs(dev->dma_device, sg, nents, direction, attrs);
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:20 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:20 +0200
Subject: [RFC v3 18/45] iommu: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-19-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/iommu/amd_iommu.c | 12 ++++++------
 drivers/iommu/dma-iommu.c |  6 +++---
 include/linux/dma-iommu.h |  6 +++---
 3 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 634f636393d5..afbb0de6a7f5 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2716,7 +2716,7 @@ static void __unmap_single(struct dma_ops_domain *dma_dom,
 static dma_addr_t map_page(struct device *dev, struct page *page,
 			   unsigned long offset, size_t size,
 			   enum dma_data_direction dir,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	phys_addr_t paddr = page_to_phys(page) + offset;
 	struct protection_domain *domain;
@@ -2738,7 +2738,7 @@ static dma_addr_t map_page(struct device *dev, struct page *page,
  * The exported unmap_single function for dma_ops.
  */
 static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
-		       enum dma_data_direction dir, struct dma_attrs *attrs)
+		       enum dma_data_direction dir, unsigned long attrs)
 {
 	struct protection_domain *domain;
 
@@ -2755,7 +2755,7 @@ static void unmap_page(struct device *dev, dma_addr_t dma_addr, size_t size,
  */
 static int map_sg(struct device *dev, struct scatterlist *sglist,
 		  int nelems, enum dma_data_direction dir,
-		  struct dma_attrs *attrs)
+		  unsigned long attrs)
 {
 	struct protection_domain *domain;
 	int i;
@@ -2803,7 +2803,7 @@ unmap:
  */
 static void unmap_sg(struct device *dev, struct scatterlist *sglist,
 		     int nelems, enum dma_data_direction dir,
-		     struct dma_attrs *attrs)
+		     unsigned long attrs)
 {
 	struct protection_domain *domain;
 	struct scatterlist *s;
@@ -2825,7 +2825,7 @@ static void unmap_sg(struct device *dev, struct scatterlist *sglist,
  */
 static void *alloc_coherent(struct device *dev, size_t size,
 			    dma_addr_t *dma_addr, gfp_t flag,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	u64 dma_mask = dev->coherent_dma_mask;
 	struct protection_domain *domain;
@@ -2879,7 +2879,7 @@ out_free:
  */
 static void free_coherent(struct device *dev, size_t size,
 			  void *virt_addr, dma_addr_t dma_addr,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	struct protection_domain *domain;
 	struct page *page;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index ea5a9ebf0f78..6c1bda504fb1 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -286,7 +286,7 @@ void iommu_dma_free(struct device *dev, struct page **pages, size_t size,
  *	   or NULL on failure.
  */
 struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
-		struct dma_attrs *attrs, int prot, dma_addr_t *handle,
+		unsigned long attrs, int prot, dma_addr_t *handle,
 		void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
 	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
@@ -400,7 +400,7 @@ dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
 }
 
 void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
-		enum dma_data_direction dir, struct dma_attrs *attrs)
+		enum dma_data_direction dir, unsigned long attrs)
 {
 	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle);
 }
@@ -560,7 +560,7 @@ out_restore_sg:
 }
 
 void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-		enum dma_data_direction dir, struct dma_attrs *attrs)
+		enum dma_data_direction dir, unsigned long attrs)
 {
 	/*
 	 * The scatterlist segments are mapped into a single
diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h
index 8443bbb5c071..81c5c8d167ad 100644
--- a/include/linux/dma-iommu.h
+++ b/include/linux/dma-iommu.h
@@ -39,7 +39,7 @@ int dma_direction_to_prot(enum dma_data_direction dir, bool coherent);
  * the arch code to take care of attributes and cache maintenance
  */
 struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
-		struct dma_attrs *attrs, int prot, dma_addr_t *handle,
+		unsigned long attrs, int prot, dma_addr_t *handle,
 		void (*flush_page)(struct device *, const void *, phys_addr_t));
 void iommu_dma_free(struct device *dev, struct page **pages, size_t size,
 		dma_addr_t *handle);
@@ -56,9 +56,9 @@ int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
  * directly as DMA mapping callbacks for simplicity
  */
 void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
-		enum dma_data_direction dir, struct dma_attrs *attrs);
+		enum dma_data_direction dir, unsigned long attrs);
 void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-		enum dma_data_direction dir, struct dma_attrs *attrs);
+		enum dma_data_direction dir, unsigned long attrs);
 int iommu_dma_supported(struct device *dev, u64 mask);
 int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr);
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:23 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:23 +0200
Subject: [RFC v3 21/45] swiotlb: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-22-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 include/linux/swiotlb.h | 10 +++++-----
 lib/swiotlb.c           | 13 +++++++------
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/include/linux/swiotlb.h b/include/linux/swiotlb.h
index 017fced60242..5f81f8a187f2 100644
--- a/include/linux/swiotlb.h
+++ b/include/linux/swiotlb.h
@@ -6,7 +6,6 @@
 #include <linux/types.h>
 
 struct device;
-struct dma_attrs;
 struct page;
 struct scatterlist;
 
@@ -68,10 +67,10 @@ swiotlb_free_coherent(struct device *hwdev, size_t size,
 extern dma_addr_t swiotlb_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs);
+				   unsigned long attrs);
 extern void swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
 			       size_t size, enum dma_data_direction dir,
-			       struct dma_attrs *attrs);
+			       unsigned long attrs);
 
 extern int
 swiotlb_map_sg(struct device *hwdev, struct scatterlist *sg, int nents,
@@ -83,12 +82,13 @@ swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sg, int nents,
 
 extern int
 swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
-		     enum dma_data_direction dir, struct dma_attrs *attrs);
+		     enum dma_data_direction dir,
+		     unsigned long attrs);
 
 extern void
 swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
 		       int nelems, enum dma_data_direction dir,
-		       struct dma_attrs *attrs);
+		       unsigned long attrs);
 
 extern void
 swiotlb_sync_single_for_cpu(struct device *hwdev, dma_addr_t dev_addr,
diff --git a/lib/swiotlb.c b/lib/swiotlb.c
index 76f29ecba8f4..22e13a0e19d7 100644
--- a/lib/swiotlb.c
+++ b/lib/swiotlb.c
@@ -738,7 +738,7 @@ swiotlb_full(struct device *dev, size_t size, enum dma_data_direction dir,
 dma_addr_t swiotlb_map_page(struct device *dev, struct page *page,
 			    unsigned long offset, size_t size,
 			    enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	phys_addr_t map, phys = page_to_phys(page) + offset;
 	dma_addr_t dev_addr = phys_to_dma(dev, phys);
@@ -807,7 +807,7 @@ static void unmap_single(struct device *hwdev, dma_addr_t dev_addr,
 
 void swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
 			size_t size, enum dma_data_direction dir,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	unmap_single(hwdev, dev_addr, size, dir);
 }
@@ -877,7 +877,7 @@ EXPORT_SYMBOL(swiotlb_sync_single_for_device);
  */
 int
 swiotlb_map_sg_attrs(struct device *hwdev, struct scatterlist *sgl, int nelems,
-		     enum dma_data_direction dir, struct dma_attrs *attrs)
+		     enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -914,7 +914,7 @@ int
 swiotlb_map_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
 	       enum dma_data_direction dir)
 {
-	return swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+	return swiotlb_map_sg_attrs(hwdev, sgl, nelems, dir, 0);
 }
 EXPORT_SYMBOL(swiotlb_map_sg);
 
@@ -924,7 +924,8 @@ EXPORT_SYMBOL(swiotlb_map_sg);
  */
 void
 swiotlb_unmap_sg_attrs(struct device *hwdev, struct scatterlist *sgl,
-		       int nelems, enum dma_data_direction dir, struct dma_attrs *attrs)
+		       int nelems, enum dma_data_direction dir,
+		       unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -941,7 +942,7 @@ void
 swiotlb_unmap_sg(struct device *hwdev, struct scatterlist *sgl, int nelems,
 		 enum dma_data_direction dir)
 {
-	return swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, NULL);
+	return swiotlb_unmap_sg_attrs(hwdev, sgl, nelems, dir, 0);
 }
 EXPORT_SYMBOL(swiotlb_unmap_sg);
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:25 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:25 +0200
Subject: [RFC v3 23/45] video: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-24-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/video/fbdev/omap2/omapfb/omapfb-main.c | 12 ++++++------
 drivers/video/fbdev/omap2/omapfb/omapfb.h      |  3 +--
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
index d3af01c94a58..59172a2185c0 100644
--- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c
@@ -1332,7 +1332,7 @@ static void omapfb_free_fbmem(struct fb_info *fbi)
 	}
 
 	dma_free_attrs(fbdev->dev, rg->size, rg->token, rg->dma_handle,
-			&rg->attrs);
+			rg->attrs);
 
 	rg->token = NULL;
 	rg->vaddr = NULL;
@@ -1370,7 +1370,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
 	struct omapfb2_device *fbdev = ofbi->fbdev;
 	struct omapfb2_mem_region *rg;
 	void *token;
-	DEFINE_DMA_ATTRS(attrs);
+	unsigned long attrs;
 	dma_addr_t dma_handle;
 	int r;
 
@@ -1386,15 +1386,15 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
 
 	size = PAGE_ALIGN(size);
 
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+	attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+		attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
 
 	token = dma_alloc_attrs(fbdev->dev, size, &dma_handle,
-			GFP_KERNEL, &attrs);
+			GFP_KERNEL, attrs);
 
 	if (token == NULL) {
 		dev_err(fbdev->dev, "failed to allocate framebuffer\n");
@@ -1408,7 +1408,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
 		r = omap_vrfb_request_ctx(&rg->vrfb);
 		if (r) {
 			dma_free_attrs(fbdev->dev, size, token, dma_handle,
-					&attrs);
+					attrs);
 			dev_err(fbdev->dev, "vrfb create ctx failed\n");
 			return r;
 		}
diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb.h b/drivers/video/fbdev/omap2/omapfb/omapfb.h
index 623cd872a367..8aaa2f643820 100644
--- a/drivers/video/fbdev/omap2/omapfb/omapfb.h
+++ b/drivers/video/fbdev/omap2/omapfb/omapfb.h
@@ -28,7 +28,6 @@
 #endif
 
 #include <linux/rwsem.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-mapping.h>
 
 #include <video/omapdss.h>
@@ -51,7 +50,7 @@ extern bool omapfb_debug;
 
 struct omapfb2_mem_region {
 	int             id;
-	struct dma_attrs attrs;
+	unsigned long	attrs;
 	void		*token;
 	dma_addr_t	dma_handle;
 	u32		paddr;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:26 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:26 +0200
Subject: [RFC v3 24/45] x86: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-25-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/x86/include/asm/dma-mapping.h       |  5 ++---
 arch/x86/include/asm/swiotlb.h           |  4 ++--
 arch/x86/include/asm/xen/page-coherent.h |  9 ++++-----
 arch/x86/kernel/amd_gart_64.c            | 20 ++++++++++----------
 arch/x86/kernel/pci-calgary_64.c         | 14 +++++++-------
 arch/x86/kernel/pci-dma.c                |  4 ++--
 arch/x86/kernel/pci-nommu.c              |  4 ++--
 arch/x86/kernel/pci-swiotlb.c            |  4 ++--
 arch/x86/pci/sta2x11-fixup.c             |  2 +-
 arch/x86/pci/vmd.c                       | 16 ++++++++--------
 10 files changed, 40 insertions(+), 42 deletions(-)

diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h
index 3a27b93e6261..44461626830e 100644
--- a/arch/x86/include/asm/dma-mapping.h
+++ b/arch/x86/include/asm/dma-mapping.h
@@ -9,7 +9,6 @@
 #include <linux/kmemcheck.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-debug.h>
-#include <linux/dma-attrs.h>
 #include <asm/io.h>
 #include <asm/swiotlb.h>
 #include <linux/dma-contiguous.h>
@@ -48,11 +47,11 @@ extern int dma_supported(struct device *hwdev, u64 mask);
 
 extern void *dma_generic_alloc_coherent(struct device *dev, size_t size,
 					dma_addr_t *dma_addr, gfp_t flag,
-					struct dma_attrs *attrs);
+					unsigned long attrs);
 
 extern void dma_generic_free_coherent(struct device *dev, size_t size,
 				      void *vaddr, dma_addr_t dma_addr,
-				      struct dma_attrs *attrs);
+				      unsigned long attrs);
 
 #ifdef CONFIG_X86_DMA_REMAP /* Platform code defines bridge-specific code */
 extern bool dma_capable(struct device *dev, dma_addr_t addr, size_t size);
diff --git a/arch/x86/include/asm/swiotlb.h b/arch/x86/include/asm/swiotlb.h
index ab05d73e2bb7..d2f69b9ff732 100644
--- a/arch/x86/include/asm/swiotlb.h
+++ b/arch/x86/include/asm/swiotlb.h
@@ -31,9 +31,9 @@ static inline void dma_mark_clean(void *addr, size_t size) {}
 
 extern void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 					dma_addr_t *dma_handle, gfp_t flags,
-					struct dma_attrs *attrs);
+					unsigned long attrs);
 extern void x86_swiotlb_free_coherent(struct device *dev, size_t size,
 					void *vaddr, dma_addr_t dma_addr,
-					struct dma_attrs *attrs);
+					unsigned long attrs);
 
 #endif /* _ASM_X86_SWIOTLB_H */
diff --git a/arch/x86/include/asm/xen/page-coherent.h b/arch/x86/include/asm/xen/page-coherent.h
index acd844c017d3..f02f025ff988 100644
--- a/arch/x86/include/asm/xen/page-coherent.h
+++ b/arch/x86/include/asm/xen/page-coherent.h
@@ -2,12 +2,11 @@
 #define _ASM_X86_XEN_PAGE_COHERENT_H
 
 #include <asm/page.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-mapping.h>
 
 static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
 		dma_addr_t *dma_handle, gfp_t flags,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	void *vstart = (void*)__get_free_pages(flags, get_order(size));
 	*dma_handle = virt_to_phys(vstart);
@@ -16,18 +15,18 @@ static inline void *xen_alloc_coherent_pages(struct device *hwdev, size_t size,
 
 static inline void xen_free_coherent_pages(struct device *hwdev, size_t size,
 		void *cpu_addr, dma_addr_t dma_handle,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	free_pages((unsigned long) cpu_addr, get_order(size));
 }
 
 static inline void xen_dma_map_page(struct device *hwdev, struct page *page,
 	     dma_addr_t dev_addr, unsigned long offset, size_t size,
-	     enum dma_data_direction dir, struct dma_attrs *attrs) { }
+	     enum dma_data_direction dir, unsigned long attrs) { }
 
 static inline void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs) { }
+		unsigned long attrs) { }
 
 static inline void xen_dma_sync_single_for_cpu(struct device *hwdev,
 		dma_addr_t handle, size_t size, enum dma_data_direction dir) { }
diff --git a/arch/x86/kernel/amd_gart_64.c b/arch/x86/kernel/amd_gart_64.c
index 8e3842fc8bea..4aff288e37a4 100644
--- a/arch/x86/kernel/amd_gart_64.c
+++ b/arch/x86/kernel/amd_gart_64.c
@@ -242,7 +242,7 @@ static dma_addr_t dma_map_area(struct device *dev, dma_addr_t phys_mem,
 static dma_addr_t gart_map_page(struct device *dev, struct page *page,
 				unsigned long offset, size_t size,
 				enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	unsigned long bus;
 	phys_addr_t paddr = page_to_phys(page) + offset;
@@ -264,7 +264,7 @@ static dma_addr_t gart_map_page(struct device *dev, struct page *page,
  */
 static void gart_unmap_page(struct device *dev, dma_addr_t dma_addr,
 			    size_t size, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	unsigned long iommu_page;
 	int npages;
@@ -286,7 +286,7 @@ static void gart_unmap_page(struct device *dev, dma_addr_t dma_addr,
  * Wrapper for pci_unmap_single working with scatterlists.
  */
 static void gart_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-			  enum dma_data_direction dir, struct dma_attrs *attrs)
+			  enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
@@ -294,7 +294,7 @@ static void gart_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
 	for_each_sg(sg, s, nents, i) {
 		if (!s->dma_length || !s->length)
 			break;
-		gart_unmap_page(dev, s->dma_address, s->dma_length, dir, NULL);
+		gart_unmap_page(dev, s->dma_address, s->dma_length, dir, 0);
 	}
 }
 
@@ -316,7 +316,7 @@ static int dma_map_sg_nonforce(struct device *dev, struct scatterlist *sg,
 			addr = dma_map_area(dev, addr, s->length, dir, 0);
 			if (addr == bad_dma_addr) {
 				if (i > 0)
-					gart_unmap_sg(dev, sg, i, dir, NULL);
+					gart_unmap_sg(dev, sg, i, dir, 0);
 				nents = 0;
 				sg[0].dma_length = 0;
 				break;
@@ -387,7 +387,7 @@ dma_map_cont(struct device *dev, struct scatterlist *start, int nelems,
  * Merge chunks that have page aligned sizes into a continuous mapping.
  */
 static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
-		       enum dma_data_direction dir, struct dma_attrs *attrs)
+		       enum dma_data_direction dir, unsigned long attrs)
 {
 	struct scatterlist *s, *ps, *start_sg, *sgmap;
 	int need = 0, nextneed, i, out, start;
@@ -457,7 +457,7 @@ static int gart_map_sg(struct device *dev, struct scatterlist *sg, int nents,
 
 error:
 	flush_gart();
-	gart_unmap_sg(dev, sg, out, dir, NULL);
+	gart_unmap_sg(dev, sg, out, dir, 0);
 
 	/* When it was forced or merged try again in a dumb way */
 	if (force_iommu || iommu_merge) {
@@ -477,7 +477,7 @@ error:
 /* allocate and map a coherent mapping */
 static void *
 gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
-		    gfp_t flag, struct dma_attrs *attrs)
+		    gfp_t flag, unsigned long attrs)
 {
 	dma_addr_t paddr;
 	unsigned long align_mask;
@@ -509,9 +509,9 @@ gart_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr,
 /* free a coherent mapping */
 static void
 gart_free_coherent(struct device *dev, size_t size, void *vaddr,
-		   dma_addr_t dma_addr, struct dma_attrs *attrs)
+		   dma_addr_t dma_addr, unsigned long attrs)
 {
-	gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, NULL);
+	gart_unmap_page(dev, dma_addr, size, DMA_BIDIRECTIONAL, 0);
 	dma_generic_free_coherent(dev, size, vaddr, dma_addr, attrs);
 }
 
diff --git a/arch/x86/kernel/pci-calgary_64.c b/arch/x86/kernel/pci-calgary_64.c
index 833b1d329c47..5d400ba1349d 100644
--- a/arch/x86/kernel/pci-calgary_64.c
+++ b/arch/x86/kernel/pci-calgary_64.c
@@ -340,7 +340,7 @@ static inline struct iommu_table *find_iommu_table(struct device *dev)
 
 static void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist,
 			     int nelems,enum dma_data_direction dir,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	struct iommu_table *tbl = find_iommu_table(dev);
 	struct scatterlist *s;
@@ -364,7 +364,7 @@ static void calgary_unmap_sg(struct device *dev, struct scatterlist *sglist,
 
 static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
 			  int nelems, enum dma_data_direction dir,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	struct iommu_table *tbl = find_iommu_table(dev);
 	struct scatterlist *s;
@@ -396,7 +396,7 @@ static int calgary_map_sg(struct device *dev, struct scatterlist *sg,
 
 	return nelems;
 error:
-	calgary_unmap_sg(dev, sg, nelems, dir, NULL);
+	calgary_unmap_sg(dev, sg, nelems, dir, 0);
 	for_each_sg(sg, s, nelems, i) {
 		sg->dma_address = DMA_ERROR_CODE;
 		sg->dma_length = 0;
@@ -407,7 +407,7 @@ error:
 static dma_addr_t calgary_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	void *vaddr = page_address(page) + offset;
 	unsigned long uaddr;
@@ -422,7 +422,7 @@ static dma_addr_t calgary_map_page(struct device *dev, struct page *page,
 
 static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr,
 			       size_t size, enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	struct iommu_table *tbl = find_iommu_table(dev);
 	unsigned int npages;
@@ -432,7 +432,7 @@ static void calgary_unmap_page(struct device *dev, dma_addr_t dma_addr,
 }
 
 static void* calgary_alloc_coherent(struct device *dev, size_t size,
-	dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs)
+	dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs)
 {
 	void *ret = NULL;
 	dma_addr_t mapping;
@@ -466,7 +466,7 @@ error:
 
 static void calgary_free_coherent(struct device *dev, size_t size,
 				  void *vaddr, dma_addr_t dma_handle,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	unsigned int npages;
 	struct iommu_table *tbl = find_iommu_table(dev);
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index 6ba014c61d62..d30c37750765 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -77,7 +77,7 @@ void __init pci_iommu_alloc(void)
 }
 void *dma_generic_alloc_coherent(struct device *dev, size_t size,
 				 dma_addr_t *dma_addr, gfp_t flag,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	unsigned long dma_mask;
 	struct page *page;
@@ -120,7 +120,7 @@ again:
 }
 
 void dma_generic_free_coherent(struct device *dev, size_t size, void *vaddr,
-			       dma_addr_t dma_addr, struct dma_attrs *attrs)
+			       dma_addr_t dma_addr, unsigned long attrs)
 {
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 	struct page *page = virt_to_page(vaddr);
diff --git a/arch/x86/kernel/pci-nommu.c b/arch/x86/kernel/pci-nommu.c
index da15918d1c81..00e71ce396a8 100644
--- a/arch/x86/kernel/pci-nommu.c
+++ b/arch/x86/kernel/pci-nommu.c
@@ -28,7 +28,7 @@ check_addr(char *name, struct device *hwdev, dma_addr_t bus, size_t size)
 static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
 				 unsigned long offset, size_t size,
 				 enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	dma_addr_t bus = page_to_phys(page) + offset;
 	WARN_ON(size == 0);
@@ -55,7 +55,7 @@ static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
  */
 static int nommu_map_sg(struct device *hwdev, struct scatterlist *sg,
 			int nents, enum dma_data_direction dir,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
diff --git a/arch/x86/kernel/pci-swiotlb.c b/arch/x86/kernel/pci-swiotlb.c
index 7c577a178859..db88bfe56a77 100644
--- a/arch/x86/kernel/pci-swiotlb.c
+++ b/arch/x86/kernel/pci-swiotlb.c
@@ -16,7 +16,7 @@ int swiotlb __read_mostly;
 
 void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 					dma_addr_t *dma_handle, gfp_t flags,
-					struct dma_attrs *attrs)
+					unsigned long attrs)
 {
 	void *vaddr;
 
@@ -37,7 +37,7 @@ void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
 
 void x86_swiotlb_free_coherent(struct device *dev, size_t size,
 				      void *vaddr, dma_addr_t dma_addr,
-				      struct dma_attrs *attrs)
+				      unsigned long attrs)
 {
 	if (is_swiotlb_buffer(dma_to_phys(dev, dma_addr)))
 		swiotlb_free_coherent(dev, size, vaddr, dma_addr);
diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c
index 5ceda85b8687..052c1cb76305 100644
--- a/arch/x86/pci/sta2x11-fixup.c
+++ b/arch/x86/pci/sta2x11-fixup.c
@@ -169,7 +169,7 @@ static void *sta2x11_swiotlb_alloc_coherent(struct device *dev,
 					    size_t size,
 					    dma_addr_t *dma_handle,
 					    gfp_t flags,
-					    struct dma_attrs *attrs)
+					    unsigned long attrs)
 {
 	void *vaddr;
 
diff --git a/arch/x86/pci/vmd.c b/arch/x86/pci/vmd.c
index 7792aba266df..d5834cc658c0 100644
--- a/arch/x86/pci/vmd.c
+++ b/arch/x86/pci/vmd.c
@@ -265,14 +265,14 @@ static struct dma_map_ops *vmd_dma_ops(struct device *dev)
 }
 
 static void *vmd_alloc(struct device *dev, size_t size, dma_addr_t *addr,
-		       gfp_t flag, struct dma_attrs *attrs)
+		       gfp_t flag, unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->alloc(to_vmd_dev(dev), size, addr, flag,
 				       attrs);
 }
 
 static void vmd_free(struct device *dev, size_t size, void *vaddr,
-		     dma_addr_t addr, struct dma_attrs *attrs)
+		     dma_addr_t addr, unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->free(to_vmd_dev(dev), size, vaddr, addr,
 				      attrs);
@@ -280,7 +280,7 @@ static void vmd_free(struct device *dev, size_t size, void *vaddr,
 
 static int vmd_mmap(struct device *dev, struct vm_area_struct *vma,
 		    void *cpu_addr, dma_addr_t addr, size_t size,
-		    struct dma_attrs *attrs)
+		    unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->mmap(to_vmd_dev(dev), vma, cpu_addr, addr,
 				      size, attrs);
@@ -288,7 +288,7 @@ static int vmd_mmap(struct device *dev, struct vm_area_struct *vma,
 
 static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt,
 			   void *cpu_addr, dma_addr_t addr, size_t size,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->get_sgtable(to_vmd_dev(dev), sgt, cpu_addr,
 					     addr, size, attrs);
@@ -297,26 +297,26 @@ static int vmd_get_sgtable(struct device *dev, struct sg_table *sgt,
 static dma_addr_t vmd_map_page(struct device *dev, struct page *page,
 			       unsigned long offset, size_t size,
 			       enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->map_page(to_vmd_dev(dev), page, offset, size,
 					  dir, attrs);
 }
 
 static void vmd_unmap_page(struct device *dev, dma_addr_t addr, size_t size,
-			   enum dma_data_direction dir, struct dma_attrs *attrs)
+			   enum dma_data_direction dir, unsigned long attrs)
 {
 	vmd_dma_ops(dev)->unmap_page(to_vmd_dev(dev), addr, size, dir, attrs);
 }
 
 static int vmd_map_sg(struct device *dev, struct scatterlist *sg, int nents,
-		      enum dma_data_direction dir, struct dma_attrs *attrs)
+		      enum dma_data_direction dir, unsigned long attrs)
 {
 	return vmd_dma_ops(dev)->map_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
 }
 
 static void vmd_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
-			 enum dma_data_direction dir, struct dma_attrs *attrs)
+			 enum dma_data_direction dir, unsigned long attrs)
 {
 	vmd_dma_ops(dev)->unmap_sg(to_vmd_dev(dev), sg, nents, dir, attrs);
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:27 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:27 +0200
Subject: [RFC v3 25/45] iommu: intel: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-26-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/iommu/intel-iommu.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index a644d0cec2d8..ba9d28acbf3a 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -3545,7 +3545,7 @@ error:
 static dma_addr_t intel_map_page(struct device *dev, struct page *page,
 				 unsigned long offset, size_t size,
 				 enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	return __intel_map_single(dev, page_to_phys(page) + offset, size,
 				  dir, *dev->dma_mask);
@@ -3704,14 +3704,14 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
 
 static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
 			     size_t size, enum dma_data_direction dir,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	intel_unmap(dev, dev_addr, size);
 }
 
 static void *intel_alloc_coherent(struct device *dev, size_t size,
 				  dma_addr_t *dma_handle, gfp_t flags,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	struct page *page = NULL;
 	int order;
@@ -3757,7 +3757,7 @@ static void *intel_alloc_coherent(struct device *dev, size_t size,
 }
 
 static void intel_free_coherent(struct device *dev, size_t size, void *vaddr,
-				dma_addr_t dma_handle, struct dma_attrs *attrs)
+				dma_addr_t dma_handle, unsigned long attrs)
 {
 	int order;
 	struct page *page = virt_to_page(vaddr);
@@ -3772,7 +3772,7 @@ static void intel_free_coherent(struct device *dev, size_t size, void *vaddr,
 
 static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist,
 			   int nelems, enum dma_data_direction dir,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	dma_addr_t startaddr = sg_dma_address(sglist) & PAGE_MASK;
 	unsigned long nrpages = 0;
@@ -3801,7 +3801,7 @@ static int intel_nontranslate_map_sg(struct device *hddev,
 }
 
 static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
-			enum dma_data_direction dir, struct dma_attrs *attrs)
+			enum dma_data_direction dir, unsigned long attrs)
 {
 	int i;
 	struct dmar_domain *domain;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:28 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:28 +0200
Subject: [RFC v3 26/45] h8300: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-27-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/h8300/kernel/dma.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/h8300/kernel/dma.c b/arch/h8300/kernel/dma.c
index eeb13d3f2424..3651da045806 100644
--- a/arch/h8300/kernel/dma.c
+++ b/arch/h8300/kernel/dma.c
@@ -12,7 +12,7 @@
 
 static void *dma_alloc(struct device *dev, size_t size,
 		       dma_addr_t *dma_handle, gfp_t gfp,
-		       struct dma_attrs *attrs)
+		       unsigned long attrs)
 {
 	void *ret;
 
@@ -32,7 +32,7 @@ static void *dma_alloc(struct device *dev, size_t size,
 
 static void dma_free(struct device *dev, size_t size,
 		     void *vaddr, dma_addr_t dma_handle,
-		     struct dma_attrs *attrs)
+		     unsigned long attrs)
 
 {
 	free_pages((unsigned long)vaddr, get_order(size));
@@ -41,14 +41,14 @@ static void dma_free(struct device *dev, size_t size,
 static dma_addr_t map_page(struct device *dev, struct page *page,
 				  unsigned long offset, size_t size,
 				  enum dma_data_direction direction,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	return page_to_phys(page) + offset;
 }
 
 static int map_sg(struct device *dev, struct scatterlist *sgl,
 		  int nents, enum dma_data_direction direction,
-		  struct dma_attrs *attrs)
+		  unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:29 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:29 +0200
Subject: [RFC v3 27/45] hexagon: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-28-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/hexagon/include/asm/dma-mapping.h | 1 -
 arch/hexagon/kernel/dma.c              | 8 ++++----
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/arch/hexagon/include/asm/dma-mapping.h b/arch/hexagon/include/asm/dma-mapping.h
index aa6203464520..7ef58df909fc 100644
--- a/arch/hexagon/include/asm/dma-mapping.h
+++ b/arch/hexagon/include/asm/dma-mapping.h
@@ -26,7 +26,6 @@
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-debug.h>
-#include <linux/dma-attrs.h>
 #include <asm/io.h>
 
 struct device;
diff --git a/arch/hexagon/kernel/dma.c b/arch/hexagon/kernel/dma.c
index 9e3ddf792bd3..b9017785fb71 100644
--- a/arch/hexagon/kernel/dma.c
+++ b/arch/hexagon/kernel/dma.c
@@ -51,7 +51,7 @@ static struct gen_pool *coherent_pool;
 
 static void *hexagon_dma_alloc_coherent(struct device *dev, size_t size,
 				 dma_addr_t *dma_addr, gfp_t flag,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	void *ret;
 
@@ -84,7 +84,7 @@ static void *hexagon_dma_alloc_coherent(struct device *dev, size_t size,
 }
 
 static void hexagon_free_coherent(struct device *dev, size_t size, void *vaddr,
-				  dma_addr_t dma_addr, struct dma_attrs *attrs)
+				  dma_addr_t dma_addr, unsigned long attrs)
 {
 	gen_pool_free(coherent_pool, (unsigned long) vaddr, size);
 }
@@ -105,7 +105,7 @@ static int check_addr(const char *name, struct device *hwdev,
 
 static int hexagon_map_sg(struct device *hwdev, struct scatterlist *sg,
 			  int nents, enum dma_data_direction dir,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
@@ -172,7 +172,7 @@ static inline void dma_sync(void *addr, size_t size,
 static dma_addr_t hexagon_map_page(struct device *dev, struct page *page,
 				   unsigned long offset, size_t size,
 				   enum dma_data_direction dir,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	dma_addr_t bus = page_to_phys(page) + offset;
 	WARN_ON(size == 0);
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:30 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:30 +0200
Subject: [RFC v3 28/45] ia64: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-29-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/ia64/hp/common/sba_iommu.c | 22 +++++++++++-----------
 arch/ia64/include/asm/machvec.h |  1 -
 arch/ia64/kernel/pci-swiotlb.c  |  4 ++--
 arch/ia64/sn/pci/pci_dma.c      | 12 ++++++------
 4 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c
index a6d6190c9d24..630ee8073899 100644
--- a/arch/ia64/hp/common/sba_iommu.c
+++ b/arch/ia64/hp/common/sba_iommu.c
@@ -919,7 +919,7 @@ sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
 static dma_addr_t sba_map_page(struct device *dev, struct page *page,
 			       unsigned long poff, size_t size,
 			       enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	struct ioc *ioc;
 	void *addr = page_address(page) + poff;
@@ -1005,7 +1005,7 @@ static dma_addr_t sba_map_page(struct device *dev, struct page *page,
 
 static dma_addr_t sba_map_single_attrs(struct device *dev, void *addr,
 				       size_t size, enum dma_data_direction dir,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	return sba_map_page(dev, virt_to_page(addr),
 			    (unsigned long)addr & ~PAGE_MASK, size, dir, attrs);
@@ -1046,7 +1046,7 @@ sba_mark_clean(struct ioc *ioc, dma_addr_t iova, size_t size)
  * See Documentation/DMA-API-HOWTO.txt
  */
 static void sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
-			   enum dma_data_direction dir, struct dma_attrs *attrs)
+			   enum dma_data_direction dir, unsigned long attrs)
 {
 	struct ioc *ioc;
 #if DELAYED_RESOURCE_CNT > 0
@@ -1115,7 +1115,7 @@ static void sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
 }
 
 void sba_unmap_single_attrs(struct device *dev, dma_addr_t iova, size_t size,
-			    enum dma_data_direction dir, struct dma_attrs *attrs)
+			    enum dma_data_direction dir, unsigned long attrs)
 {
 	sba_unmap_page(dev, iova, size, dir, attrs);
 }
@@ -1130,7 +1130,7 @@ void sba_unmap_single_attrs(struct device *dev, dma_addr_t iova, size_t size,
  */
 static void *
 sba_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
-		   gfp_t flags, struct dma_attrs *attrs)
+		   gfp_t flags, unsigned long attrs)
 {
 	struct ioc *ioc;
 	void *addr;
@@ -1175,7 +1175,7 @@ sba_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
 	 * device to map single to get an iova mapping.
 	 */
 	*dma_handle = sba_map_single_attrs(&ioc->sac_only_dev->dev, addr,
-					   size, 0, NULL);
+					   size, 0, 0);
 
 	return addr;
 }
@@ -1191,9 +1191,9 @@ sba_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
  * See Documentation/DMA-API-HOWTO.txt
  */
 static void sba_free_coherent(struct device *dev, size_t size, void *vaddr,
-			      dma_addr_t dma_handle, struct dma_attrs *attrs)
+			      dma_addr_t dma_handle, unsigned long attrs)
 {
-	sba_unmap_single_attrs(dev, dma_handle, size, 0, NULL);
+	sba_unmap_single_attrs(dev, dma_handle, size, 0, 0);
 	free_pages((unsigned long) vaddr, get_order(size));
 }
 
@@ -1442,7 +1442,7 @@ sba_coalesce_chunks(struct ioc *ioc, struct device *dev,
 
 static void sba_unmap_sg_attrs(struct device *dev, struct scatterlist *sglist,
 			       int nents, enum dma_data_direction dir,
-			       struct dma_attrs *attrs);
+			       unsigned long attrs);
 /**
  * sba_map_sg - map Scatter/Gather list
  * @dev: instance of PCI owned by the driver that's asking.
@@ -1455,7 +1455,7 @@ static void sba_unmap_sg_attrs(struct device *dev, struct scatterlist *sglist,
  */
 static int sba_map_sg_attrs(struct device *dev, struct scatterlist *sglist,
 			    int nents, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct ioc *ioc;
 	int coalesced, filled = 0;
@@ -1551,7 +1551,7 @@ static int sba_map_sg_attrs(struct device *dev, struct scatterlist *sglist,
  */
 static void sba_unmap_sg_attrs(struct device *dev, struct scatterlist *sglist,
 			       int nents, enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 #ifdef ASSERT_PDIR_SANITY
 	struct ioc *ioc;
diff --git a/arch/ia64/include/asm/machvec.h b/arch/ia64/include/asm/machvec.h
index 9c39bdfc2da8..ed7f09089f12 100644
--- a/arch/ia64/include/asm/machvec.h
+++ b/arch/ia64/include/asm/machvec.h
@@ -22,7 +22,6 @@ struct pci_bus;
 struct task_struct;
 struct pci_dev;
 struct msi_desc;
-struct dma_attrs;
 
 typedef void ia64_mv_setup_t (char **);
 typedef void ia64_mv_cpu_init_t (void);
diff --git a/arch/ia64/kernel/pci-swiotlb.c b/arch/ia64/kernel/pci-swiotlb.c
index 939260aeac98..2933208c0285 100644
--- a/arch/ia64/kernel/pci-swiotlb.c
+++ b/arch/ia64/kernel/pci-swiotlb.c
@@ -16,7 +16,7 @@ EXPORT_SYMBOL(swiotlb);
 
 static void *ia64_swiotlb_alloc_coherent(struct device *dev, size_t size,
 					 dma_addr_t *dma_handle, gfp_t gfp,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 	if (dev->coherent_dma_mask != DMA_BIT_MASK(64))
 		gfp |= GFP_DMA;
@@ -25,7 +25,7 @@ static void *ia64_swiotlb_alloc_coherent(struct device *dev, size_t size,
 
 static void ia64_swiotlb_free_coherent(struct device *dev, size_t size,
 				       void *vaddr, dma_addr_t dma_addr,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_addr);
 }
diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
index 8f59907007cb..6b78fc953c4b 100644
--- a/arch/ia64/sn/pci/pci_dma.c
+++ b/arch/ia64/sn/pci/pci_dma.c
@@ -77,7 +77,7 @@ EXPORT_SYMBOL(sn_dma_set_mask);
  */
 static void *sn_dma_alloc_coherent(struct device *dev, size_t size,
 				   dma_addr_t * dma_handle, gfp_t flags,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	void *cpuaddr;
 	unsigned long phys_addr;
@@ -138,7 +138,7 @@ static void *sn_dma_alloc_coherent(struct device *dev, size_t size,
  * any associated IOMMU mappings.
  */
 static void sn_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
-				 dma_addr_t dma_handle, struct dma_attrs *attrs)
+				 dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
@@ -176,7 +176,7 @@ static void sn_dma_free_coherent(struct device *dev, size_t size, void *cpu_addr
 static dma_addr_t sn_dma_map_page(struct device *dev, struct page *page,
 				  unsigned long offset, size_t size,
 				  enum dma_data_direction dir,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	void *cpu_addr = page_address(page) + offset;
 	dma_addr_t dma_addr;
@@ -218,7 +218,7 @@ static dma_addr_t sn_dma_map_page(struct device *dev, struct page *page,
  */
 static void sn_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 			      size_t size, enum dma_data_direction dir,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
@@ -240,7 +240,7 @@ static void sn_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
  */
 static void sn_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
 			    int nhwentries, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	int i;
 	struct pci_dev *pdev = to_pci_dev(dev);
@@ -273,7 +273,7 @@ static void sn_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
  */
 static int sn_dma_map_sg(struct device *dev, struct scatterlist *sgl,
 			 int nhwentries, enum dma_data_direction dir,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	unsigned long phys_addr;
 	struct scatterlist *saved_sg = sgl, *sg;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:33 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:33 +0200
Subject: [RFC v3 31/45] microblaze: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-32-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/microblaze/include/asm/dma-mapping.h |  1 -
 arch/microblaze/kernel/dma.c              | 12 ++++++------
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/arch/microblaze/include/asm/dma-mapping.h b/arch/microblaze/include/asm/dma-mapping.h
index 1884783d15c0..1768d4bdc8d3 100644
--- a/arch/microblaze/include/asm/dma-mapping.h
+++ b/arch/microblaze/include/asm/dma-mapping.h
@@ -25,7 +25,6 @@
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
 #include <linux/dma-debug.h>
-#include <linux/dma-attrs.h>
 #include <asm/io.h>
 #include <asm/cacheflush.h>
 
diff --git a/arch/microblaze/kernel/dma.c b/arch/microblaze/kernel/dma.c
index bf4dec229437..ec04dc1e2527 100644
--- a/arch/microblaze/kernel/dma.c
+++ b/arch/microblaze/kernel/dma.c
@@ -17,7 +17,7 @@
 
 static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 				       dma_addr_t *dma_handle, gfp_t flag,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 #ifdef NOT_COHERENT_CACHE
 	return consistent_alloc(flag, size, dma_handle);
@@ -42,7 +42,7 @@ static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 
 static void dma_direct_free_coherent(struct device *dev, size_t size,
 				     void *vaddr, dma_addr_t dma_handle,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 #ifdef NOT_COHERENT_CACHE
 	consistent_free(size, vaddr);
@@ -53,7 +53,7 @@ static void dma_direct_free_coherent(struct device *dev, size_t size,
 
 static int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl,
 			     int nents, enum dma_data_direction direction,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -78,7 +78,7 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev,
 					     unsigned long offset,
 					     size_t size,
 					     enum dma_data_direction direction,
-					     struct dma_attrs *attrs)
+					     unsigned long attrs)
 {
 	__dma_sync(page_to_phys(page) + offset, size, direction);
 	return page_to_phys(page) + offset;
@@ -88,7 +88,7 @@ static inline void dma_direct_unmap_page(struct device *dev,
 					 dma_addr_t dma_address,
 					 size_t size,
 					 enum dma_data_direction direction,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 /* There is not necessary to do cache cleanup
  *
@@ -157,7 +157,7 @@ dma_direct_sync_sg_for_device(struct device *dev,
 static
 int dma_direct_mmap_coherent(struct device *dev, struct vm_area_struct *vma,
 			     void *cpu_addr, dma_addr_t handle, size_t size,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 #ifdef CONFIG_MMU
 	unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:31 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:31 +0200
Subject: [RFC v3 29/45] m68k: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-30-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/m68k/kernel/dma.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c
index cbc78b4117b5..8cf97cbadc91 100644
--- a/arch/m68k/kernel/dma.c
+++ b/arch/m68k/kernel/dma.c
@@ -19,7 +19,7 @@
 #if defined(CONFIG_MMU) && !defined(CONFIG_COLDFIRE)
 
 static void *m68k_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
-		gfp_t flag, struct dma_attrs *attrs)
+		gfp_t flag, unsigned long attrs)
 {
 	struct page *page, **map;
 	pgprot_t pgprot;
@@ -62,7 +62,7 @@ static void *m68k_dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 }
 
 static void m68k_dma_free(struct device *dev, size_t size, void *addr,
-		dma_addr_t handle, struct dma_attrs *attrs)
+		dma_addr_t handle, unsigned long attrs)
 {
 	pr_debug("dma_free_coherent: %p, %x\n", addr, handle);
 	vfree(addr);
@@ -73,7 +73,7 @@ static void m68k_dma_free(struct device *dev, size_t size, void *addr,
 #include <asm/cacheflush.h>
 
 static void *m68k_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 	/* ignore region specifiers */
@@ -91,7 +91,7 @@ static void *m68k_dma_alloc(struct device *dev, size_t size,
 }
 
 static void m68k_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	free_pages((unsigned long)vaddr, get_order(size));
 }
@@ -130,7 +130,7 @@ static void m68k_dma_sync_sg_for_device(struct device *dev,
 
 static dma_addr_t m68k_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	dma_addr_t handle = page_to_phys(page) + offset;
 
@@ -139,7 +139,7 @@ static dma_addr_t m68k_dma_map_page(struct device *dev, struct page *page,
 }
 
 static int m68k_dma_map_sg(struct device *dev, struct scatterlist *sglist,
-		int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+		int nents, enum dma_data_direction dir, unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:34 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:34 +0200
Subject: [RFC v3 32/45] mips: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-33-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/mips/cavium-octeon/dma-octeon.c      |  8 ++++----
 arch/mips/loongson64/common/dma-swiotlb.c | 10 +++++-----
 arch/mips/mm/dma-default.c                | 14 +++++++-------
 arch/mips/netlogic/common/nlm-dma.c       |  4 ++--
 4 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/arch/mips/cavium-octeon/dma-octeon.c b/arch/mips/cavium-octeon/dma-octeon.c
index 2cd45f5f9481..fd69528b24fb 100644
--- a/arch/mips/cavium-octeon/dma-octeon.c
+++ b/arch/mips/cavium-octeon/dma-octeon.c
@@ -125,7 +125,7 @@ static phys_addr_t octeon_small_dma_to_phys(struct device *dev,
 
 static dma_addr_t octeon_dma_map_page(struct device *dev, struct page *page,
 	unsigned long offset, size_t size, enum dma_data_direction direction,
-	struct dma_attrs *attrs)
+	unsigned long attrs)
 {
 	dma_addr_t daddr = swiotlb_map_page(dev, page, offset, size,
 					    direction, attrs);
@@ -135,7 +135,7 @@ static dma_addr_t octeon_dma_map_page(struct device *dev, struct page *page,
 }
 
 static int octeon_dma_map_sg(struct device *dev, struct scatterlist *sg,
-	int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
+	int nents, enum dma_data_direction direction, unsigned long attrs)
 {
 	int r = swiotlb_map_sg_attrs(dev, sg, nents, direction, attrs);
 	mb();
@@ -157,7 +157,7 @@ static void octeon_dma_sync_sg_for_device(struct device *dev,
 }
 
 static void *octeon_dma_alloc_coherent(struct device *dev, size_t size,
-	dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+	dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -189,7 +189,7 @@ static void *octeon_dma_alloc_coherent(struct device *dev, size_t size,
 }
 
 static void octeon_dma_free_coherent(struct device *dev, size_t size,
-	void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
+	void *vaddr, dma_addr_t dma_handle, unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_handle);
 }
diff --git a/arch/mips/loongson64/common/dma-swiotlb.c b/arch/mips/loongson64/common/dma-swiotlb.c
index 4ffa6fc81c8f..1a80b6f73ab2 100644
--- a/arch/mips/loongson64/common/dma-swiotlb.c
+++ b/arch/mips/loongson64/common/dma-swiotlb.c
@@ -10,7 +10,7 @@
 #include <dma-coherence.h>
 
 static void *loongson_dma_alloc_coherent(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -41,7 +41,7 @@ static void *loongson_dma_alloc_coherent(struct device *dev, size_t size,
 }
 
 static void loongson_dma_free_coherent(struct device *dev, size_t size,
-		void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
+		void *vaddr, dma_addr_t dma_handle, unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_handle);
 }
@@ -49,7 +49,7 @@ static void loongson_dma_free_coherent(struct device *dev, size_t size,
 static dma_addr_t loongson_dma_map_page(struct device *dev, struct page *page,
 				unsigned long offset, size_t size,
 				enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	dma_addr_t daddr = swiotlb_map_page(dev, page, offset, size,
 					dir, attrs);
@@ -59,9 +59,9 @@ static dma_addr_t loongson_dma_map_page(struct device *dev, struct page *page,
 
 static int loongson_dma_map_sg(struct device *dev, struct scatterlist *sg,
 				int nents, enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
-	int r = swiotlb_map_sg_attrs(dev, sg, nents, dir, NULL);
+	int r = swiotlb_map_sg_attrs(dev, sg, nents, dir, 0);
 	mb();
 
 	return r;
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index cb557d28cb21..c9052108094f 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -131,7 +131,7 @@ static void *mips_dma_alloc_noncoherent(struct device *dev, size_t size,
 }
 
 static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
-	dma_addr_t * dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+	dma_addr_t * dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 	struct page *page = NULL;
@@ -176,7 +176,7 @@ static void mips_dma_free_noncoherent(struct device *dev, size_t size,
 }
 
 static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
-	dma_addr_t dma_handle, struct dma_attrs *attrs)
+	dma_addr_t dma_handle, unsigned long attrs)
 {
 	unsigned long addr = (unsigned long) vaddr;
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
@@ -200,7 +200,7 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
 
 static int mips_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 	void *cpu_addr, dma_addr_t dma_addr, size_t size,
-	struct dma_attrs *attrs)
+	unsigned long attrs)
 {
 	unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
 	unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
@@ -291,7 +291,7 @@ static inline void __dma_sync(struct page *page,
 }
 
 static void mips_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
-	size_t size, enum dma_data_direction direction, struct dma_attrs *attrs)
+	size_t size, enum dma_data_direction direction, unsigned long attrs)
 {
 	if (cpu_needs_post_dma_flush(dev))
 		__dma_sync(dma_addr_to_page(dev, dma_addr),
@@ -301,7 +301,7 @@ static void mips_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 }
 
 static int mips_dma_map_sg(struct device *dev, struct scatterlist *sglist,
-	int nents, enum dma_data_direction direction, struct dma_attrs *attrs)
+	int nents, enum dma_data_direction direction, unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
@@ -322,7 +322,7 @@ static int mips_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static dma_addr_t mips_dma_map_page(struct device *dev, struct page *page,
 	unsigned long offset, size_t size, enum dma_data_direction direction,
-	struct dma_attrs *attrs)
+	unsigned long attrs)
 {
 	if (!plat_device_is_coherent(dev))
 		__dma_sync(page, offset, size, direction);
@@ -332,7 +332,7 @@ static dma_addr_t mips_dma_map_page(struct device *dev, struct page *page,
 
 static void mips_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
 	int nhwentries, enum dma_data_direction direction,
-	struct dma_attrs *attrs)
+	unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
diff --git a/arch/mips/netlogic/common/nlm-dma.c b/arch/mips/netlogic/common/nlm-dma.c
index 3758715d4ab6..0630693bec2a 100644
--- a/arch/mips/netlogic/common/nlm-dma.c
+++ b/arch/mips/netlogic/common/nlm-dma.c
@@ -45,7 +45,7 @@
 static char *nlm_swiotlb;
 
 static void *nlm_dma_alloc_coherent(struct device *dev, size_t size,
-	dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+	dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	/* ignore region specifiers */
 	gfp &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
@@ -62,7 +62,7 @@ static void *nlm_dma_alloc_coherent(struct device *dev, size_t size,
 }
 
 static void nlm_dma_free_coherent(struct device *dev, size_t size,
-	void *vaddr, dma_addr_t dma_handle, struct dma_attrs *attrs)
+	void *vaddr, dma_addr_t dma_handle, unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_handle);
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:32 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:32 +0200
Subject: [RFC v3 30/45] metag: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-31-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/metag/kernel/dma.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/arch/metag/kernel/dma.c b/arch/metag/kernel/dma.c
index e12368d02155..d68f498e82a1 100644
--- a/arch/metag/kernel/dma.c
+++ b/arch/metag/kernel/dma.c
@@ -172,7 +172,7 @@ out:
  * virtual and bus address for that space.
  */
 static void *metag_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
 {
 	struct page *page;
 	struct metag_vm_region *c;
@@ -268,7 +268,7 @@ no_page:
  * free a page as defined by the above mapping.
  */
 static void metag_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct metag_vm_region *c;
 	unsigned long flags, addr;
@@ -331,7 +331,7 @@ no_area:
 
 static int metag_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 		void *cpu_addr, dma_addr_t dma_addr, size_t size,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	unsigned long flags, user_size, kern_size;
 	struct metag_vm_region *c;
@@ -482,7 +482,7 @@ static void dma_sync_for_cpu(void *vaddr, size_t size, int dma_direction)
 
 static dma_addr_t metag_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	dma_sync_for_device((void *)(page_to_phys(page) + offset), size,
 			    direction);
@@ -491,14 +491,14 @@ static dma_addr_t metag_dma_map_page(struct device *dev, struct page *page,
 
 static void metag_dma_unmap_page(struct device *dev, dma_addr_t dma_address,
 		size_t size, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	dma_sync_for_cpu(phys_to_virt(dma_address), size, direction);
 }
 
 static int metag_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -516,7 +516,7 @@ static int metag_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static void metag_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
 		int nhwentries, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:35 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:35 +0200
Subject: [RFC v3 33/45] mn10300: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-34-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/mn10300/mm/dma-alloc.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/mn10300/mm/dma-alloc.c b/arch/mn10300/mm/dma-alloc.c
index 8842394cb49a..4f4b9029f0ea 100644
--- a/arch/mn10300/mm/dma-alloc.c
+++ b/arch/mn10300/mm/dma-alloc.c
@@ -21,7 +21,7 @@
 static unsigned long pci_sram_allocated = 0xbc000000;
 
 static void *mn10300_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	unsigned long addr;
 	void *ret;
@@ -63,7 +63,7 @@ done:
 }
 
 static void mn10300_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	unsigned long addr = (unsigned long) vaddr & ~0x20000000;
 
@@ -75,7 +75,7 @@ static void mn10300_dma_free(struct device *dev, size_t size, void *vaddr,
 
 static int mn10300_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -92,7 +92,7 @@ static int mn10300_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static dma_addr_t mn10300_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	return page_to_bus(page) + offset;
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:36 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:36 +0200
Subject: [RFC v3 34/45] nios2: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-35-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/nios2/mm/dma-mapping.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/nios2/mm/dma-mapping.c b/arch/nios2/mm/dma-mapping.c
index 90422c367ed3..d800fad87896 100644
--- a/arch/nios2/mm/dma-mapping.c
+++ b/arch/nios2/mm/dma-mapping.c
@@ -59,7 +59,7 @@ static inline void __dma_sync_for_cpu(void *vaddr, size_t size,
 }
 
 static void *nios2_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t gfp, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -84,7 +84,7 @@ static void *nios2_dma_alloc(struct device *dev, size_t size,
 }
 
 static void nios2_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	unsigned long addr = (unsigned long) CAC_ADDR((unsigned long) vaddr);
 
@@ -93,7 +93,7 @@ static void nios2_dma_free(struct device *dev, size_t size, void *vaddr,
 
 static int nios2_dma_map_sg(struct device *dev, struct scatterlist *sg,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 
@@ -113,7 +113,7 @@ static int nios2_dma_map_sg(struct device *dev, struct scatterlist *sg,
 static dma_addr_t nios2_dma_map_page(struct device *dev, struct page *page,
 			unsigned long offset, size_t size,
 			enum dma_data_direction direction,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	void *addr = page_address(page) + offset;
 
@@ -123,14 +123,14 @@ static dma_addr_t nios2_dma_map_page(struct device *dev, struct page *page,
 
 static void nios2_dma_unmap_page(struct device *dev, dma_addr_t dma_address,
 		size_t size, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	__dma_sync_for_cpu(phys_to_virt(dma_address), size, direction);
 }
 
 static void nios2_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
 		int nhwentries, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	void *addr;
 	int i;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:37 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:37 +0200
Subject: [RFC v3 35/45] openrisc: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-36-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/openrisc/kernel/dma.c | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c
index 0b77ddb1ee07..50eb1f26c540 100644
--- a/arch/openrisc/kernel/dma.c
+++ b/arch/openrisc/kernel/dma.c
@@ -22,7 +22,6 @@
 #include <linux/dma-mapping.h>
 #include <linux/dma-debug.h>
 #include <linux/export.h>
-#include <linux/dma-attrs.h>
 
 #include <asm/cpuinfo.h>
 #include <asm/spr_defs.h>
@@ -83,7 +82,7 @@ page_clear_nocache(pte_t *pte, unsigned long addr,
 static void *
 or1k_dma_alloc(struct device *dev, size_t size,
 	       dma_addr_t *dma_handle, gfp_t gfp,
-	       struct dma_attrs *attrs)
+	       unsigned long attrs)
 {
 	unsigned long va;
 	void *page;
@@ -117,7 +116,7 @@ or1k_dma_alloc(struct device *dev, size_t size,
 
 static void
 or1k_dma_free(struct device *dev, size_t size, void *vaddr,
-	      dma_addr_t dma_handle, struct dma_attrs *attrs)
+	      dma_addr_t dma_handle, unsigned long attrs)
 {
 	unsigned long va = (unsigned long)vaddr;
 	struct mm_walk walk = {
@@ -137,7 +136,7 @@ static dma_addr_t
 or1k_map_page(struct device *dev, struct page *page,
 	      unsigned long offset, size_t size,
 	      enum dma_data_direction dir,
-	      struct dma_attrs *attrs)
+	      unsigned long attrs)
 {
 	unsigned long cl;
 	dma_addr_t addr = page_to_phys(page) + offset;
@@ -170,7 +169,7 @@ or1k_map_page(struct device *dev, struct page *page,
 static void
 or1k_unmap_page(struct device *dev, dma_addr_t dma_handle,
 		size_t size, enum dma_data_direction dir,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	/* Nothing special to do here... */
 }
@@ -178,14 +177,14 @@ or1k_unmap_page(struct device *dev, dma_addr_t dma_handle,
 static int
 or1k_map_sg(struct device *dev, struct scatterlist *sg,
 	    int nents, enum dma_data_direction dir,
-	    struct dma_attrs *attrs)
+	    unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
 
 	for_each_sg(sg, s, nents, i) {
 		s->dma_address = or1k_map_page(dev, sg_page(s), s->offset,
-					       s->length, dir, NULL);
+					       s->length, dir, 0);
 	}
 
 	return nents;
@@ -194,13 +193,13 @@ or1k_map_sg(struct device *dev, struct scatterlist *sg,
 static void
 or1k_unmap_sg(struct device *dev, struct scatterlist *sg,
 	      int nents, enum dma_data_direction dir,
-	      struct dma_attrs *attrs)
+	      unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
 
 	for_each_sg(sg, s, nents, i) {
-		or1k_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, NULL);
+		or1k_unmap_page(dev, sg_dma_address(s), sg_dma_len(s), dir, 0);
 	}
 }
 
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:40 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:40 +0200
Subject: [RFC v3 38/45] s390: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-39-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/s390/include/asm/dma-mapping.h |  1 -
 arch/s390/pci/pci_dma.c             | 23 ++++++++++++-----------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h
index 3249b7464889..ffaba07f50ab 100644
--- a/arch/s390/include/asm/dma-mapping.h
+++ b/arch/s390/include/asm/dma-mapping.h
@@ -5,7 +5,6 @@
 #include <linux/types.h>
 #include <linux/mm.h>
 #include <linux/scatterlist.h>
-#include <linux/dma-attrs.h>
 #include <linux/dma-debug.h>
 #include <linux/io.h>
 
diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c
index 1ea8c07eab84..159f767f9011 100644
--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -285,7 +285,7 @@ static inline void zpci_err_dma(unsigned long rc, unsigned long addr)
 static dma_addr_t s390_dma_map_pages(struct device *dev, struct page *page,
 				     unsigned long offset, size_t size,
 				     enum dma_data_direction direction,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
 	unsigned long nr_pages, iommu_page_index;
@@ -331,7 +331,7 @@ out_err:
 
 static void s390_dma_unmap_pages(struct device *dev, dma_addr_t dma_addr,
 				 size_t size, enum dma_data_direction direction,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
 	unsigned long iommu_page_index;
@@ -354,7 +354,7 @@ static void s390_dma_unmap_pages(struct device *dev, dma_addr_t dma_addr,
 
 static void *s390_dma_alloc(struct device *dev, size_t size,
 			    dma_addr_t *dma_handle, gfp_t flag,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
 	struct page *page;
@@ -369,7 +369,7 @@ static void *s390_dma_alloc(struct device *dev, size_t size,
 	pa = page_to_phys(page);
 	memset((void *) pa, 0, size);
 
-	map = s390_dma_map_pages(dev, page, 0, size, DMA_BIDIRECTIONAL, NULL);
+	map = s390_dma_map_pages(dev, page, 0, size, DMA_BIDIRECTIONAL, 0);
 	if (dma_mapping_error(dev, map)) {
 		free_pages(pa, get_order(size));
 		return NULL;
@@ -383,19 +383,19 @@ static void *s390_dma_alloc(struct device *dev, size_t size,
 
 static void s390_dma_free(struct device *dev, size_t size,
 			  void *pa, dma_addr_t dma_handle,
-			  struct dma_attrs *attrs)
+			  unsigned long attrs)
 {
 	struct zpci_dev *zdev = to_zpci(to_pci_dev(dev));
 
 	size = PAGE_ALIGN(size);
 	atomic64_sub(size / PAGE_SIZE, &zdev->allocated_pages);
-	s390_dma_unmap_pages(dev, dma_handle, size, DMA_BIDIRECTIONAL, NULL);
+	s390_dma_unmap_pages(dev, dma_handle, size, DMA_BIDIRECTIONAL, 0);
 	free_pages((unsigned long) pa, get_order(size));
 }
 
 static int s390_dma_map_sg(struct device *dev, struct scatterlist *sg,
 			   int nr_elements, enum dma_data_direction dir,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	int mapped_elements = 0;
 	struct scatterlist *s;
@@ -404,7 +404,7 @@ static int s390_dma_map_sg(struct device *dev, struct scatterlist *sg,
 	for_each_sg(sg, s, nr_elements, i) {
 		struct page *page = sg_page(s);
 		s->dma_address = s390_dma_map_pages(dev, page, s->offset,
-						    s->length, dir, NULL);
+						    s->length, dir, 0);
 		if (!dma_mapping_error(dev, s->dma_address)) {
 			s->dma_length = s->length;
 			mapped_elements++;
@@ -418,7 +418,7 @@ unmap:
 	for_each_sg(sg, s, mapped_elements, i) {
 		if (s->dma_address)
 			s390_dma_unmap_pages(dev, s->dma_address, s->dma_length,
-					     dir, NULL);
+					     dir, 0);
 		s->dma_address = 0;
 		s->dma_length = 0;
 	}
@@ -428,13 +428,14 @@ unmap:
 
 static void s390_dma_unmap_sg(struct device *dev, struct scatterlist *sg,
 			      int nr_elements, enum dma_data_direction dir,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
 
 	for_each_sg(sg, s, nr_elements, i) {
-		s390_dma_unmap_pages(dev, s->dma_address, s->dma_length, dir, NULL);
+		s390_dma_unmap_pages(dev, s->dma_address, s->dma_length, dir,
+				     0);
 		s->dma_address = 0;
 		s->dma_length = 0;
 	}
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:38 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:38 +0200
Subject: [RFC v3 36/45] parisc: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-37-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/parisc/kernel/pci-dma.c | 16 ++++++++--------
 drivers/parisc/ccio-dma.c    | 16 ++++++++--------
 drivers/parisc/sba_iommu.c   | 16 ++++++++--------
 3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index a27e4928bf73..845fdd52e4c5 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -414,7 +414,7 @@ pcxl_dma_init(void)
 __initcall(pcxl_dma_init);
 
 static void *pa11_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs)
 {
 	unsigned long vaddr;
 	unsigned long paddr;
@@ -441,7 +441,7 @@ static void *pa11_dma_alloc(struct device *dev, size_t size,
 }
 
 static void pa11_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
 	int order;
 
@@ -454,7 +454,7 @@ static void pa11_dma_free(struct device *dev, size_t size, void *vaddr,
 
 static dma_addr_t pa11_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	void *addr = page_address(page) + offset;
 	BUG_ON(direction == DMA_NONE);
@@ -465,7 +465,7 @@ static dma_addr_t pa11_dma_map_page(struct device *dev, struct page *page,
 
 static void pa11_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
 		size_t size, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	BUG_ON(direction == DMA_NONE);
 
@@ -484,7 +484,7 @@ static void pa11_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
 
 static int pa11_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
@@ -503,7 +503,7 @@ static int pa11_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static void pa11_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
 		int nents, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	int i;
 	struct scatterlist *sg;
@@ -577,7 +577,7 @@ struct dma_map_ops pcxl_dma_ops = {
 };
 
 static void *pcx_dma_alloc(struct device *dev, size_t size,
-		dma_addr_t *dma_handle, gfp_t flag, struct dma_attrs *attrs)
+		dma_addr_t *dma_handle, gfp_t flag, unsigned long attrs)
 {
 	void *addr;
 
@@ -592,7 +592,7 @@ static void *pcx_dma_alloc(struct device *dev, size_t size,
 }
 
 static void pcx_dma_free(struct device *dev, size_t size, void *vaddr,
-		dma_addr_t iova, struct dma_attrs *attrs)
+		dma_addr_t iova, unsigned long attrs)
 {
 	free_pages((unsigned long)vaddr, get_order(size));
 	return;
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index e24b05996a1b..3ed6238f8f6e 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -790,7 +790,7 @@ ccio_map_single(struct device *dev, void *addr, size_t size,
 static dma_addr_t
 ccio_map_page(struct device *dev, struct page *page, unsigned long offset,
 		size_t size, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	return ccio_map_single(dev, page_address(page) + offset, size,
 			direction);
@@ -806,7 +806,7 @@ ccio_map_page(struct device *dev, struct page *page, unsigned long offset,
  */
 static void 
 ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 	unsigned long flags; 
@@ -844,7 +844,7 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
  */
 static void * 
 ccio_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
       void *ret;
 #if 0
@@ -878,9 +878,9 @@ ccio_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag,
  */
 static void 
 ccio_free(struct device *dev, size_t size, void *cpu_addr,
-		dma_addr_t dma_handle, struct dma_attrs *attrs)
+		dma_addr_t dma_handle, unsigned long attrs)
 {
-	ccio_unmap_page(dev, dma_handle, size, 0, NULL);
+	ccio_unmap_page(dev, dma_handle, size, 0, 0);
 	free_pages((unsigned long)cpu_addr, get_order(size));
 }
 
@@ -907,7 +907,7 @@ ccio_free(struct device *dev, size_t size, void *cpu_addr,
  */
 static int
 ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents, 
-	    enum dma_data_direction direction, struct dma_attrs *attrs)
+	    enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 	int coalesced, filled = 0;
@@ -984,7 +984,7 @@ ccio_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
  */
 static void 
 ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents, 
-	      enum dma_data_direction direction, struct dma_attrs *attrs)
+	      enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 
@@ -1004,7 +1004,7 @@ ccio_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
 		ioc->usg_pages += sg_dma_len(sglist) >> PAGE_SHIFT;
 #endif
 		ccio_unmap_page(dev, sg_dma_address(sglist),
-				  sg_dma_len(sglist), direction, NULL);
+				  sg_dma_len(sglist), direction, 0);
 		++sglist;
 	}
 
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
index 42ec4600b7e4..151b86b6d2e2 100644
--- a/drivers/parisc/sba_iommu.c
+++ b/drivers/parisc/sba_iommu.c
@@ -783,7 +783,7 @@ sba_map_single(struct device *dev, void *addr, size_t size,
 static dma_addr_t
 sba_map_page(struct device *dev, struct page *page, unsigned long offset,
 		size_t size, enum dma_data_direction direction,
-		struct dma_attrs *attrs)
+		unsigned long attrs)
 {
 	return sba_map_single(dev, page_address(page) + offset, size,
 			direction);
@@ -801,7 +801,7 @@ sba_map_page(struct device *dev, struct page *page, unsigned long offset,
  */
 static void
 sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
-		enum dma_data_direction direction, struct dma_attrs *attrs)
+		enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 #if DELAYED_RESOURCE_CNT > 0
@@ -876,7 +876,7 @@ sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
  * See Documentation/DMA-API-HOWTO.txt
  */
 static void *sba_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_handle,
-		gfp_t gfp, struct dma_attrs *attrs)
+		gfp_t gfp, unsigned long attrs)
 {
 	void *ret;
 
@@ -908,9 +908,9 @@ static void *sba_alloc(struct device *hwdev, size_t size, dma_addr_t *dma_handle
  */
 static void
 sba_free(struct device *hwdev, size_t size, void *vaddr,
-		    dma_addr_t dma_handle, struct dma_attrs *attrs)
+		    dma_addr_t dma_handle, unsigned long attrs)
 {
-	sba_unmap_page(hwdev, dma_handle, size, 0, NULL);
+	sba_unmap_page(hwdev, dma_handle, size, 0, 0);
 	free_pages((unsigned long) vaddr, get_order(size));
 }
 
@@ -943,7 +943,7 @@ int dump_run_sg = 0;
  */
 static int
 sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
-	   enum dma_data_direction direction, struct dma_attrs *attrs)
+	   enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 	int coalesced, filled = 0;
@@ -1026,7 +1026,7 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
  */
 static void 
 sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
-	     enum dma_data_direction direction, struct dma_attrs *attrs)
+	     enum dma_data_direction direction, unsigned long attrs)
 {
 	struct ioc *ioc;
 #ifdef ASSERT_PDIR_SANITY
@@ -1051,7 +1051,7 @@ sba_unmap_sg(struct device *dev, struct scatterlist *sglist, int nents,
 	while (sg_dma_len(sglist) && nents--) {
 
 		sba_unmap_page(dev, sg_dma_address(sglist), sg_dma_len(sglist),
-				direction, NULL);
+				direction, 0);
 #ifdef SBA_COLLECT_STATS
 		ioc->usg_pages += ((sg_dma_address(sglist) & ~IOVP_MASK) + sg_dma_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT;
 		ioc->usingle_calls--;	/* kluge since call is unmap_sg() */
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:41 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:41 +0200
Subject: [RFC v3 39/45] sh: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-40-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/sh/include/asm/dma-mapping.h | 4 ++--
 arch/sh/kernel/dma-nommu.c        | 4 ++--
 arch/sh/mm/consistent.c           | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/sh/include/asm/dma-mapping.h b/arch/sh/include/asm/dma-mapping.h
index e11cf0c8206b..0052ad40e86d 100644
--- a/arch/sh/include/asm/dma-mapping.h
+++ b/arch/sh/include/asm/dma-mapping.h
@@ -17,9 +17,9 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
 /* arch/sh/mm/consistent.c */
 extern void *dma_generic_alloc_coherent(struct device *dev, size_t size,
 					dma_addr_t *dma_addr, gfp_t flag,
-					struct dma_attrs *attrs);
+					unsigned long attrs);
 extern void dma_generic_free_coherent(struct device *dev, size_t size,
 				      void *vaddr, dma_addr_t dma_handle,
-				      struct dma_attrs *attrs);
+				      unsigned long attrs);
 
 #endif /* __ASM_SH_DMA_MAPPING_H */
diff --git a/arch/sh/kernel/dma-nommu.c b/arch/sh/kernel/dma-nommu.c
index 5b0bfcda6d0b..eadb669a7329 100644
--- a/arch/sh/kernel/dma-nommu.c
+++ b/arch/sh/kernel/dma-nommu.c
@@ -13,7 +13,7 @@
 static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
 				 unsigned long offset, size_t size,
 				 enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	dma_addr_t addr = page_to_phys(page) + offset;
 
@@ -25,7 +25,7 @@ static dma_addr_t nommu_map_page(struct device *dev, struct page *page,
 
 static int nommu_map_sg(struct device *dev, struct scatterlist *sg,
 			int nents, enum dma_data_direction dir,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
diff --git a/arch/sh/mm/consistent.c b/arch/sh/mm/consistent.c
index b81d9dbf9fef..92b6976fde59 100644
--- a/arch/sh/mm/consistent.c
+++ b/arch/sh/mm/consistent.c
@@ -34,7 +34,7 @@ fs_initcall(dma_init);
 
 void *dma_generic_alloc_coherent(struct device *dev, size_t size,
 				 dma_addr_t *dma_handle, gfp_t gfp,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	void *ret, *ret_nocache;
 	int order = get_order(size);
@@ -66,7 +66,7 @@ void *dma_generic_alloc_coherent(struct device *dev, size_t size,
 
 void dma_generic_free_coherent(struct device *dev, size_t size,
 			       void *vaddr, dma_addr_t dma_handle,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	int order = get_order(size);
 	unsigned long pfn = dma_handle >> PAGE_SHIFT;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:44 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:44 +0200
Subject: [RFC v3 42/45] unicore32: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-43-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/unicore32/mm/dma-swiotlb.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/unicore32/mm/dma-swiotlb.c b/arch/unicore32/mm/dma-swiotlb.c
index 16c08b2143a7..3e9f6489ba38 100644
--- a/arch/unicore32/mm/dma-swiotlb.c
+++ b/arch/unicore32/mm/dma-swiotlb.c
@@ -19,14 +19,14 @@
 
 static void *unicore_swiotlb_alloc_coherent(struct device *dev, size_t size,
 					    dma_addr_t *dma_handle, gfp_t flags,
-					    struct dma_attrs *attrs)
+					    unsigned long attrs)
 {
 	return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
 }
 
 static void unicore_swiotlb_free_coherent(struct device *dev, size_t size,
 					  void *vaddr, dma_addr_t dma_addr,
-					  struct dma_attrs *attrs)
+					  unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_addr);
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:47 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:47 +0200
Subject: [RFC v3 45/45] dma-mapping: Document the DMA attributes right in
 declaration
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-46-git-send-email-k.kozlowski@samsung.com>

Copy documentation abstract about each DMA attribute from
Documentation/DMA-attributes.txt to the place with declaration.

Suggested-by: Christoph Hellwig <hch at infradead.org>
Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 include/linux/dma-mapping.h | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 6d013ba94213..f349df4cb009 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -14,14 +14,47 @@
 /**
  * List of possible attributes associated with a DMA mapping. The semantics
  * of each attribute should be defined in Documentation/DMA-attributes.txt.
+ *
+ * DMA_ATTR_WRITE_BARRIER: DMA to a memory region with this attribute
+ * forces all pending DMA writes to complete.
  */
 #define DMA_ATTR_WRITE_BARRIER		(1UL << 1)
+/*
+ * DMA_ATTR_WEAK_ORDERING: Specifies that reads and writes to the mapping
+ * may be weakly ordered, that is that reads and writes may pass each other.
+ */
 #define DMA_ATTR_WEAK_ORDERING		(1UL << 2)
+/*
+ * DMA_ATTR_WRITE_COMBINE: Specifies that writes to the mapping may be
+ * buffered to improve performance.
+ */
 #define DMA_ATTR_WRITE_COMBINE		(1UL << 3)
+/*
+ * DMA_ATTR_NON_CONSISTENT: Lets the platform to choose to return either
+ * consistent or non-consistent memory as it sees fit.
+ */
 #define DMA_ATTR_NON_CONSISTENT		(1UL << 4)
+/*
+ * DMA_ATTR_NO_KERNEL_MAPPING: Lets the platform to avoid creating a kernel
+ * virtual mapping for the allocated buffer.
+ */
 #define DMA_ATTR_NO_KERNEL_MAPPING	(1UL << 5)
+/*
+ * DMA_ATTR_SKIP_CPU_SYNC: Allows platform code to skip synchronization of
+ * the CPU cache for the given buffer assuming that it has been already
+ * transferred to 'device' domain.
+ */
 #define DMA_ATTR_SKIP_CPU_SYNC		(1UL << 6)
+/*
+ * DMA_ATTR_FORCE_CONTIGUOUS: Forces contiguous allocation of the buffer
+ * in physical memory.
+ */
 #define DMA_ATTR_FORCE_CONTIGUOUS	(1UL << 7)
+/*
+ * DMA_ATTR_ALLOC_SINGLE_PAGES: This is a hint to the DMA-mapping subsystem
+ * that it's probably not worth the time to try to allocate memory to in a way
+ * that gives better TLB efficiency.
+ */
 #define DMA_ATTR_ALLOC_SINGLE_PAGES	(1UL << 8)
 
 /*
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:39 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:39 +0200
Subject: [RFC v3 37/45] misc: mic: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-38-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/misc/mic/host/mic_boot.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c
index e047efd83f57..9599d732aff3 100644
--- a/drivers/misc/mic/host/mic_boot.c
+++ b/drivers/misc/mic/host/mic_boot.c
@@ -38,7 +38,7 @@ static inline struct mic_device *vpdev_to_mdev(struct device *dev)
 static dma_addr_t
 _mic_dma_map_page(struct device *dev, struct page *page,
 		  unsigned long offset, size_t size,
-		  enum dma_data_direction dir, struct dma_attrs *attrs)
+		  enum dma_data_direction dir, unsigned long attrs)
 {
 	void *va = phys_to_virt(page_to_phys(page)) + offset;
 	struct mic_device *mdev = vpdev_to_mdev(dev);
@@ -48,7 +48,7 @@ _mic_dma_map_page(struct device *dev, struct page *page,
 
 static void _mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 				size_t size, enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	struct mic_device *mdev = vpdev_to_mdev(dev);
 
@@ -144,7 +144,7 @@ static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev)
 
 static void *__mic_dma_alloc(struct device *dev, size_t size,
 			     dma_addr_t *dma_handle, gfp_t gfp,
-			     struct dma_attrs *attrs)
+			     unsigned long attrs)
 {
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
 	struct mic_device *mdev = scdev_to_mdev(scdev);
@@ -164,7 +164,7 @@ static void *__mic_dma_alloc(struct device *dev, size_t size,
 }
 
 static void __mic_dma_free(struct device *dev, size_t size, void *vaddr,
-			   dma_addr_t dma_handle, struct dma_attrs *attrs)
+			   dma_addr_t dma_handle, unsigned long attrs)
 {
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
 	struct mic_device *mdev = scdev_to_mdev(scdev);
@@ -176,7 +176,7 @@ static void __mic_dma_free(struct device *dev, size_t size, void *vaddr,
 static dma_addr_t
 __mic_dma_map_page(struct device *dev, struct page *page, unsigned long offset,
 		   size_t size, enum dma_data_direction dir,
-		   struct dma_attrs *attrs)
+		   unsigned long attrs)
 {
 	void *va = phys_to_virt(page_to_phys(page)) + offset;
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
@@ -188,7 +188,7 @@ __mic_dma_map_page(struct device *dev, struct page *page, unsigned long offset,
 static void
 __mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 		     size_t size, enum dma_data_direction dir,
-		     struct dma_attrs *attrs)
+		     unsigned long attrs)
 {
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
 	struct mic_device *mdev = scdev_to_mdev(scdev);
@@ -198,7 +198,7 @@ __mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 
 static int __mic_dma_map_sg(struct device *dev, struct scatterlist *sg,
 			    int nents, enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
 	struct mic_device *mdev = scdev_to_mdev(scdev);
@@ -229,7 +229,7 @@ err:
 static void __mic_dma_unmap_sg(struct device *dev,
 			       struct scatterlist *sg, int nents,
 			       enum dma_data_direction dir,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	struct scif_hw_dev *scdev = dev_get_drvdata(dev);
 	struct mic_device *mdev = scdev_to_mdev(scdev);
@@ -327,7 +327,7 @@ static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev)
 static dma_addr_t
 mic_dma_map_page(struct device *dev, struct page *page,
 		 unsigned long offset, size_t size, enum dma_data_direction dir,
-		 struct dma_attrs *attrs)
+		 unsigned long attrs)
 {
 	void *va = phys_to_virt(page_to_phys(page)) + offset;
 	struct mic_device *mdev = dev_get_drvdata(dev->parent);
@@ -338,7 +338,7 @@ mic_dma_map_page(struct device *dev, struct page *page,
 static void
 mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
 		   size_t size, enum dma_data_direction dir,
-		   struct dma_attrs *attrs)
+		   unsigned long attrs)
 {
 	struct mic_device *mdev = dev_get_drvdata(dev->parent);
 	mic_unmap_single(mdev, dma_addr, size);
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:43 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:43 +0200
Subject: [RFC v3 41/45] tile: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-42-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/tile/kernel/pci-dma.c | 28 ++++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/arch/tile/kernel/pci-dma.c b/arch/tile/kernel/pci-dma.c
index b6bc0547a4f6..09bb774b39cd 100644
--- a/arch/tile/kernel/pci-dma.c
+++ b/arch/tile/kernel/pci-dma.c
@@ -34,7 +34,7 @@
 
 static void *tile_dma_alloc_coherent(struct device *dev, size_t size,
 				     dma_addr_t *dma_handle, gfp_t gfp,
-				     struct dma_attrs *attrs)
+				     unsigned long attrs)
 {
 	u64 dma_mask = (dev && dev->coherent_dma_mask) ?
 		dev->coherent_dma_mask : DMA_BIT_MASK(32);
@@ -78,7 +78,7 @@ static void *tile_dma_alloc_coherent(struct device *dev, size_t size,
  */
 static void tile_dma_free_coherent(struct device *dev, size_t size,
 				   void *vaddr, dma_addr_t dma_handle,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	homecache_free_pages((unsigned long)vaddr, get_order(size));
 }
@@ -202,7 +202,7 @@ static void __dma_complete_pa_range(dma_addr_t dma_addr, size_t size,
 
 static int tile_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 			   int nents, enum dma_data_direction direction,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -224,7 +224,7 @@ static int tile_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 
 static void tile_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
 			      int nents, enum dma_data_direction direction,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -240,7 +240,7 @@ static void tile_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
 static dma_addr_t tile_dma_map_page(struct device *dev, struct page *page,
 				    unsigned long offset, size_t size,
 				    enum dma_data_direction direction,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	BUG_ON(!valid_dma_direction(direction));
 
@@ -252,7 +252,7 @@ static dma_addr_t tile_dma_map_page(struct device *dev, struct page *page,
 
 static void tile_dma_unmap_page(struct device *dev, dma_addr_t dma_address,
 				size_t size, enum dma_data_direction direction,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	BUG_ON(!valid_dma_direction(direction));
 
@@ -343,7 +343,7 @@ EXPORT_SYMBOL(tile_dma_map_ops);
 
 static void *tile_pci_dma_alloc_coherent(struct device *dev, size_t size,
 					 dma_addr_t *dma_handle, gfp_t gfp,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 	int node = dev_to_node(dev);
 	int order = get_order(size);
@@ -368,14 +368,14 @@ static void *tile_pci_dma_alloc_coherent(struct device *dev, size_t size,
  */
 static void tile_pci_dma_free_coherent(struct device *dev, size_t size,
 				       void *vaddr, dma_addr_t dma_handle,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	homecache_free_pages((unsigned long)vaddr, get_order(size));
 }
 
 static int tile_pci_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 			       int nents, enum dma_data_direction direction,
-			       struct dma_attrs *attrs)
+			       unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -400,7 +400,7 @@ static int tile_pci_dma_map_sg(struct device *dev, struct scatterlist *sglist,
 static void tile_pci_dma_unmap_sg(struct device *dev,
 				  struct scatterlist *sglist, int nents,
 				  enum dma_data_direction direction,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int i;
@@ -416,7 +416,7 @@ static void tile_pci_dma_unmap_sg(struct device *dev,
 static dma_addr_t tile_pci_dma_map_page(struct device *dev, struct page *page,
 					unsigned long offset, size_t size,
 					enum dma_data_direction direction,
-					struct dma_attrs *attrs)
+					unsigned long attrs)
 {
 	BUG_ON(!valid_dma_direction(direction));
 
@@ -429,7 +429,7 @@ static dma_addr_t tile_pci_dma_map_page(struct device *dev, struct page *page,
 static void tile_pci_dma_unmap_page(struct device *dev, dma_addr_t dma_address,
 				    size_t size,
 				    enum dma_data_direction direction,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	BUG_ON(!valid_dma_direction(direction));
 
@@ -531,7 +531,7 @@ EXPORT_SYMBOL(gx_pci_dma_map_ops);
 #ifdef CONFIG_SWIOTLB
 static void *tile_swiotlb_alloc_coherent(struct device *dev, size_t size,
 					 dma_addr_t *dma_handle, gfp_t gfp,
-					 struct dma_attrs *attrs)
+					 unsigned long attrs)
 {
 	gfp |= GFP_DMA;
 	return swiotlb_alloc_coherent(dev, size, dma_handle, gfp);
@@ -539,7 +539,7 @@ static void *tile_swiotlb_alloc_coherent(struct device *dev, size_t size,
 
 static void tile_swiotlb_free_coherent(struct device *dev, size_t size,
 				       void *vaddr, dma_addr_t dma_addr,
-				       struct dma_attrs *attrs)
+				       unsigned long attrs)
 {
 	swiotlb_free_coherent(dev, size, vaddr, dma_addr);
 }
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:45 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:45 +0200
Subject: [RFC v3 43/45] xtensa: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-44-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/xtensa/kernel/pci-dma.c | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/xtensa/kernel/pci-dma.c b/arch/xtensa/kernel/pci-dma.c
index cd66698348ca..1e68806d6695 100644
--- a/arch/xtensa/kernel/pci-dma.c
+++ b/arch/xtensa/kernel/pci-dma.c
@@ -142,7 +142,7 @@ static void xtensa_sync_sg_for_device(struct device *dev,
 
 static void *xtensa_dma_alloc(struct device *dev, size_t size,
 			      dma_addr_t *handle, gfp_t flag,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	unsigned long ret;
 	unsigned long uncached = 0;
@@ -171,7 +171,7 @@ static void *xtensa_dma_alloc(struct device *dev, size_t size,
 }
 
 static void xtensa_dma_free(struct device *hwdev, size_t size, void *vaddr,
-			    dma_addr_t dma_handle, struct dma_attrs *attrs)
+			    dma_addr_t dma_handle, unsigned long attrs)
 {
 	unsigned long addr = (unsigned long)vaddr +
 		XCHAL_KSEG_CACHED_VADDR - XCHAL_KSEG_BYPASS_VADDR;
@@ -185,7 +185,7 @@ static void xtensa_dma_free(struct device *hwdev, size_t size, void *vaddr,
 static dma_addr_t xtensa_map_page(struct device *dev, struct page *page,
 				  unsigned long offset, size_t size,
 				  enum dma_data_direction dir,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	dma_addr_t dma_handle = page_to_phys(page) + offset;
 
@@ -195,14 +195,14 @@ static dma_addr_t xtensa_map_page(struct device *dev, struct page *page,
 
 static void xtensa_unmap_page(struct device *dev, dma_addr_t dma_handle,
 			      size_t size, enum dma_data_direction dir,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	xtensa_sync_single_for_cpu(dev, dma_handle, size, dir);
 }
 
 static int xtensa_map_sg(struct device *dev, struct scatterlist *sg,
 			 int nents, enum dma_data_direction dir,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
@@ -217,7 +217,7 @@ static int xtensa_map_sg(struct device *dev, struct scatterlist *sg,
 static void xtensa_unmap_sg(struct device *dev,
 			    struct scatterlist *sg, int nents,
 			    enum dma_data_direction dir,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct scatterlist *s;
 	int i;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:42 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:42 +0200
Subject: [RFC v3 40/45] sparc: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-41-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 arch/sparc/kernel/iommu.c     | 12 ++++++------
 arch/sparc/kernel/ioport.c    | 24 ++++++++++++------------
 arch/sparc/kernel/pci_sun4v.c | 12 ++++++------
 3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/arch/sparc/kernel/iommu.c b/arch/sparc/kernel/iommu.c
index 37686828c3d9..5c615abff030 100644
--- a/arch/sparc/kernel/iommu.c
+++ b/arch/sparc/kernel/iommu.c
@@ -196,7 +196,7 @@ static inline void iommu_free_ctx(struct iommu *iommu, int ctx)
 
 static void *dma_4u_alloc_coherent(struct device *dev, size_t size,
 				   dma_addr_t *dma_addrp, gfp_t gfp,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	unsigned long order, first_page;
 	struct iommu *iommu;
@@ -245,7 +245,7 @@ static void *dma_4u_alloc_coherent(struct device *dev, size_t size,
 
 static void dma_4u_free_coherent(struct device *dev, size_t size,
 				 void *cpu, dma_addr_t dvma,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	struct iommu *iommu;
 	unsigned long order, npages;
@@ -263,7 +263,7 @@ static void dma_4u_free_coherent(struct device *dev, size_t size,
 static dma_addr_t dma_4u_map_page(struct device *dev, struct page *page,
 				  unsigned long offset, size_t sz,
 				  enum dma_data_direction direction,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	struct iommu *iommu;
 	struct strbuf *strbuf;
@@ -385,7 +385,7 @@ do_flush_sync:
 
 static void dma_4u_unmap_page(struct device *dev, dma_addr_t bus_addr,
 			      size_t sz, enum dma_data_direction direction,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct iommu *iommu;
 	struct strbuf *strbuf;
@@ -431,7 +431,7 @@ static void dma_4u_unmap_page(struct device *dev, dma_addr_t bus_addr,
 
 static int dma_4u_map_sg(struct device *dev, struct scatterlist *sglist,
 			 int nelems, enum dma_data_direction direction,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	struct scatterlist *s, *outs, *segstart;
 	unsigned long flags, handle, prot, ctx;
@@ -607,7 +607,7 @@ static unsigned long fetch_sg_ctx(struct iommu *iommu, struct scatterlist *sg)
 
 static void dma_4u_unmap_sg(struct device *dev, struct scatterlist *sglist,
 			    int nelems, enum dma_data_direction direction,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	unsigned long flags, ctx;
 	struct scatterlist *sg;
diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c
index ffd5ff4678cf..2344103414d1 100644
--- a/arch/sparc/kernel/ioport.c
+++ b/arch/sparc/kernel/ioport.c
@@ -260,7 +260,7 @@ EXPORT_SYMBOL(sbus_set_sbus64);
  */
 static void *sbus_alloc_coherent(struct device *dev, size_t len,
 				 dma_addr_t *dma_addrp, gfp_t gfp,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	struct platform_device *op = to_platform_device(dev);
 	unsigned long len_total = PAGE_ALIGN(len);
@@ -315,7 +315,7 @@ err_nopages:
 }
 
 static void sbus_free_coherent(struct device *dev, size_t n, void *p,
-			       dma_addr_t ba, struct dma_attrs *attrs)
+			       dma_addr_t ba, unsigned long attrs)
 {
 	struct resource *res;
 	struct page *pgv;
@@ -355,7 +355,7 @@ static void sbus_free_coherent(struct device *dev, size_t n, void *p,
 static dma_addr_t sbus_map_page(struct device *dev, struct page *page,
 				unsigned long offset, size_t len,
 				enum dma_data_direction dir,
-				struct dma_attrs *attrs)
+				unsigned long attrs)
 {
 	void *va = page_address(page) + offset;
 
@@ -371,20 +371,20 @@ static dma_addr_t sbus_map_page(struct device *dev, struct page *page,
 }
 
 static void sbus_unmap_page(struct device *dev, dma_addr_t ba, size_t n,
-			    enum dma_data_direction dir, struct dma_attrs *attrs)
+			    enum dma_data_direction dir, unsigned long attrs)
 {
 	mmu_release_scsi_one(dev, ba, n);
 }
 
 static int sbus_map_sg(struct device *dev, struct scatterlist *sg, int n,
-		       enum dma_data_direction dir, struct dma_attrs *attrs)
+		       enum dma_data_direction dir, unsigned long attrs)
 {
 	mmu_get_scsi_sgl(dev, sg, n);
 	return n;
 }
 
 static void sbus_unmap_sg(struct device *dev, struct scatterlist *sg, int n,
-			  enum dma_data_direction dir, struct dma_attrs *attrs)
+			  enum dma_data_direction dir, unsigned long attrs)
 {
 	mmu_release_scsi_sgl(dev, sg, n);
 }
@@ -429,7 +429,7 @@ arch_initcall(sparc_register_ioport);
  */
 static void *pci32_alloc_coherent(struct device *dev, size_t len,
 				  dma_addr_t *pba, gfp_t gfp,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	unsigned long len_total = PAGE_ALIGN(len);
 	void *va;
@@ -482,7 +482,7 @@ err_nopages:
  * past this call are illegal.
  */
 static void pci32_free_coherent(struct device *dev, size_t n, void *p,
-				dma_addr_t ba, struct dma_attrs *attrs)
+				dma_addr_t ba, unsigned long attrs)
 {
 	struct resource *res;
 
@@ -518,14 +518,14 @@ static void pci32_free_coherent(struct device *dev, size_t n, void *p,
 static dma_addr_t pci32_map_page(struct device *dev, struct page *page,
 				 unsigned long offset, size_t size,
 				 enum dma_data_direction dir,
-				 struct dma_attrs *attrs)
+				 unsigned long attrs)
 {
 	/* IIep is write-through, not flushing. */
 	return page_to_phys(page) + offset;
 }
 
 static void pci32_unmap_page(struct device *dev, dma_addr_t ba, size_t size,
-			     enum dma_data_direction dir, struct dma_attrs *attrs)
+			     enum dma_data_direction dir, unsigned long attrs)
 {
 	if (dir != PCI_DMA_TODEVICE)
 		dma_make_coherent(ba, PAGE_ALIGN(size));
@@ -548,7 +548,7 @@ static void pci32_unmap_page(struct device *dev, dma_addr_t ba, size_t size,
  */
 static int pci32_map_sg(struct device *device, struct scatterlist *sgl,
 			int nents, enum dma_data_direction dir,
-			struct dma_attrs *attrs)
+			unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int n;
@@ -567,7 +567,7 @@ static int pci32_map_sg(struct device *device, struct scatterlist *sgl,
  */
 static void pci32_unmap_sg(struct device *dev, struct scatterlist *sgl,
 			   int nents, enum dma_data_direction dir,
-			   struct dma_attrs *attrs)
+			   unsigned long attrs)
 {
 	struct scatterlist *sg;
 	int n;
diff --git a/arch/sparc/kernel/pci_sun4v.c b/arch/sparc/kernel/pci_sun4v.c
index 836e8cef47e2..61c6f935accc 100644
--- a/arch/sparc/kernel/pci_sun4v.c
+++ b/arch/sparc/kernel/pci_sun4v.c
@@ -130,7 +130,7 @@ static inline long iommu_batch_end(void)
 
 static void *dma_4v_alloc_coherent(struct device *dev, size_t size,
 				   dma_addr_t *dma_addrp, gfp_t gfp,
-				   struct dma_attrs *attrs)
+				   unsigned long attrs)
 {
 	unsigned long flags, order, first_page, npages, n;
 	struct iommu *iommu;
@@ -213,7 +213,7 @@ static void dma_4v_iommu_demap(void *demap_arg, unsigned long entry,
 }
 
 static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
-				 dma_addr_t dvma, struct dma_attrs *attrs)
+				 dma_addr_t dvma, unsigned long attrs)
 {
 	struct pci_pbm_info *pbm;
 	struct iommu *iommu;
@@ -235,7 +235,7 @@ static void dma_4v_free_coherent(struct device *dev, size_t size, void *cpu,
 static dma_addr_t dma_4v_map_page(struct device *dev, struct page *page,
 				  unsigned long offset, size_t sz,
 				  enum dma_data_direction direction,
-				  struct dma_attrs *attrs)
+				  unsigned long attrs)
 {
 	struct iommu *iommu;
 	unsigned long flags, npages, oaddr;
@@ -294,7 +294,7 @@ iommu_map_fail:
 
 static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
 			      size_t sz, enum dma_data_direction direction,
-			      struct dma_attrs *attrs)
+			      unsigned long attrs)
 {
 	struct pci_pbm_info *pbm;
 	struct iommu *iommu;
@@ -322,7 +322,7 @@ static void dma_4v_unmap_page(struct device *dev, dma_addr_t bus_addr,
 
 static int dma_4v_map_sg(struct device *dev, struct scatterlist *sglist,
 			 int nelems, enum dma_data_direction direction,
-			 struct dma_attrs *attrs)
+			 unsigned long attrs)
 {
 	struct scatterlist *s, *outs, *segstart;
 	unsigned long flags, handle, prot;
@@ -466,7 +466,7 @@ iommu_map_failed:
 
 static void dma_4v_unmap_sg(struct device *dev, struct scatterlist *sglist,
 			    int nelems, enum dma_data_direction direction,
-			    struct dma_attrs *attrs)
+			    unsigned long attrs)
 {
 	struct pci_pbm_info *pbm;
 	struct scatterlist *sg;
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:46 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:46 +0200
Subject: [RFC v3 44/45] dma-mapping: Remove dma_get_attr
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-45-git-send-email-k.kozlowski@samsung.com>

After switching DMA attributes to unsigned long it is easier to just
compare the bits.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 Documentation/DMA-API.txt                      |  4 +--
 arch/arc/mm/dma.c                              |  4 +--
 arch/arm/mm/dma-mapping.c                      | 36 ++++++++++++--------------
 arch/arm/xen/mm.c                              |  4 +--
 arch/arm64/mm/dma-mapping.c                    | 10 +++----
 arch/avr32/mm/dma-coherent.c                   |  4 +--
 arch/ia64/sn/pci/pci_dma.c                     | 10 ++-----
 arch/metag/kernel/dma.c                        |  2 +-
 arch/mips/mm/dma-default.c                     |  6 ++---
 arch/openrisc/kernel/dma.c                     |  4 +--
 arch/parisc/kernel/pci-dma.c                   |  2 +-
 arch/powerpc/platforms/cell/iommu.c            | 10 +++----
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c    |  2 +-
 drivers/iommu/dma-iommu.c                      |  2 +-
 drivers/media/v4l2-core/videobuf2-dma-contig.c |  2 +-
 include/linux/dma-mapping.h                    | 13 ----------
 16 files changed, 46 insertions(+), 69 deletions(-)

diff --git a/Documentation/DMA-API.txt b/Documentation/DMA-API.txt
index 24f9688bb98a..1d26eeb6b5f6 100644
--- a/Documentation/DMA-API.txt
+++ b/Documentation/DMA-API.txt
@@ -422,9 +422,7 @@ void whizco_dma_map_sg_attrs(struct device *dev, dma_addr_t dma_addr,
 			     unsigned long attrs)
 {
 	....
-	int foo =  dma_get_attr(DMA_ATTR_FOO, attrs);
-	....
-	if (foo)
+	if (attrs & DMA_ATTR_FOO)
 		/* twizzle the frobnozzle */
 	....
 
diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 3d1f467d1792..74bbe68dce9d 100644
--- a/arch/arc/mm/dma.c
+++ b/arch/arc/mm/dma.c
@@ -46,7 +46,7 @@ static void *arc_dma_alloc(struct device *dev, size_t size,
 	 *   (vs. always going to memory - thus are faster)
 	 */
 	if ((is_isa_arcv2() && ioc_exists) ||
-	    dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	    (attrs & DMA_ATTR_NON_CONSISTENT))
 		need_coh = 0;
 
 	/*
@@ -95,7 +95,7 @@ static void arc_dma_free(struct device *dev, size_t size, void *vaddr,
 	struct page *page = virt_to_page(dma_handle);
 	int is_non_coh = 1;
 
-	is_non_coh = dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs) ||
+	is_non_coh = (attrs & DMA_ATTR_NON_CONSISTENT) ||
 			(is_isa_arcv2() && ioc_exists);
 
 	if (PageHighMem(page) || !is_non_coh)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ebb3fde99043..43e03b5293d0 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -126,7 +126,7 @@ static dma_addr_t arm_dma_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
 	     unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_cpu_to_dev(page, offset, size, dir);
 	return pfn_to_dma(dev, page_to_pfn(page)) + offset;
 }
@@ -155,7 +155,7 @@ static dma_addr_t arm_coherent_dma_map_page(struct device *dev, struct page *pag
 static void arm_dma_unmap_page(struct device *dev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_dev_to_cpu(pfn_to_page(dma_to_pfn(dev, handle)),
 				      handle & ~PAGE_MASK, size, dir);
 }
@@ -622,9 +622,9 @@ static void __free_from_contiguous(struct device *dev, struct page *page,
 
 static inline pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot)
 {
-	prot = dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs) ?
-			    pgprot_writecombine(prot) :
-			    pgprot_dmacoherent(prot);
+	prot = (attrs & DMA_ATTR_WRITE_COMBINE) ?
+			pgprot_writecombine(prot) :
+			pgprot_dmacoherent(prot);
 	return prot;
 }
 
@@ -744,7 +744,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle,
 		.gfp = gfp,
 		.prot = prot,
 		.caller = caller,
-		.want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs),
+		.want_vaddr = ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0),
 	};
 
 #ifdef CONFIG_DMA_API_DEBUG
@@ -887,7 +887,7 @@ static void __arm_dma_free(struct device *dev, size_t size, void *cpu_addr,
 		.size = PAGE_ALIGN(size),
 		.cpu_addr = cpu_addr,
 		.page = page,
-		.want_vaddr = !dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs),
+		.want_vaddr = ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0),
 	};
 
 	buf = arm_dma_buffer_find(cpu_addr);
@@ -1267,7 +1267,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 	if (!pages)
 		return NULL;
 
-	if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs))
+	if (attrs & DMA_ATTR_FORCE_CONTIGUOUS)
 	{
 		unsigned long order = get_order(size);
 		struct page *page;
@@ -1285,7 +1285,7 @@ static struct page **__iommu_alloc_buffer(struct device *dev, size_t size,
 	}
 
 	/* Go straight to 4K chunks if caller says it's OK. */
-	if (dma_get_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, attrs))
+	if (attrs & DMA_ATTR_ALLOC_SINGLE_PAGES)
 		order_idx = ARRAY_SIZE(iommu_order_array) - 1;
 
 	/*
@@ -1346,7 +1346,7 @@ static int __iommu_free_buffer(struct device *dev, struct page **pages,
 	int count = size >> PAGE_SHIFT;
 	int i;
 
-	if (dma_get_attr(DMA_ATTR_FORCE_CONTIGUOUS, attrs)) {
+	if (attrs & DMA_ATTR_FORCE_CONTIGUOUS) {
 		dma_release_from_contiguous(dev, pages[0], count);
 	} else {
 		for (i = 0; i < count; i++)
@@ -1445,7 +1445,7 @@ static struct page **__iommu_get_pages(void *cpu_addr, unsigned long attrs)
 	if (__in_atomic_pool(cpu_addr, PAGE_SIZE))
 		return __atomic_get_pages(cpu_addr);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+	if (attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return cpu_addr;
 
 	area = find_vm_area(cpu_addr);
@@ -1512,7 +1512,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
 	if (*handle == DMA_ERROR_CODE)
 		goto err_buffer;
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs))
+	if (attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return pages;
 
 	addr = __iommu_alloc_remap(pages, size, gfp, prot,
@@ -1583,7 +1583,7 @@ void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
 		return;
 	}
 
-	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, attrs)) {
+	if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0) {
 		dma_common_free_remap(cpu_addr, size,
 			VM_ARM_DMA_CONSISTENT | VM_USERMAP);
 	}
@@ -1653,8 +1653,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
 		phys_addr_t phys = page_to_phys(sg_page(s));
 		unsigned int len = PAGE_ALIGN(s->offset + s->length);
 
-		if (!is_coherent &&
-			!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+		if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 			__dma_page_cpu_to_dev(sg_page(s), s->offset, s->length, dir);
 
 		prot = __dma_direction_to_prot(dir);
@@ -1767,8 +1766,7 @@ static void __iommu_unmap_sg(struct device *dev, struct scatterlist *sg,
 		if (sg_dma_len(s))
 			__iommu_remove_mapping(dev, sg_dma_address(s),
 					       sg_dma_len(s));
-		if (!is_coherent &&
-		    !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+		if (!is_coherent && (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 			__dma_page_dev_to_cpu(sg_page(s), s->offset,
 					      s->length, dir);
 	}
@@ -1892,7 +1890,7 @@ static dma_addr_t arm_iommu_map_page(struct device *dev, struct page *page,
 	     unsigned long offset, size_t size, enum dma_data_direction dir,
 	     unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_cpu_to_dev(page, offset, size, dir);
 
 	return arm_coherent_iommu_map_page(dev, page, offset, size, dir, attrs);
@@ -1943,7 +1941,7 @@ static void arm_iommu_unmap_page(struct device *dev, dma_addr_t handle,
 	if (!iova)
 		return;
 
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__dma_page_dev_to_cpu(page, offset, size, dir);
 
 	iommu_unmap(mapping->domain, iova, len);
diff --git a/arch/arm/xen/mm.c b/arch/arm/xen/mm.c
index fc67ed236a10..d062f08f5020 100644
--- a/arch/arm/xen/mm.c
+++ b/arch/arm/xen/mm.c
@@ -102,7 +102,7 @@ void __xen_dma_map_page(struct device *hwdev, struct page *page,
 {
 	if (is_device_dma_coherent(hwdev))
 		return;
-	if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if (attrs & DMA_ATTR_SKIP_CPU_SYNC)
 		return;
 
 	__xen_dma_page_cpu_to_dev(hwdev, dev_addr, size, dir);
@@ -115,7 +115,7 @@ void __xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
 {
 	if (is_device_dma_coherent(hwdev))
 		return;
-	if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if (attrs & DMA_ATTR_SKIP_CPU_SYNC)
 		return;
 
 	__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir);
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index a7686028dfeb..e78bf269ee4f 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -32,7 +32,7 @@
 static pgprot_t __get_dma_pgprot(unsigned long attrs, pgprot_t prot,
 				 bool coherent)
 {
-	if (!coherent || dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (!coherent || (attrs & DMA_ATTR_WRITE_COMBINE))
 		return pgprot_writecombine(prot);
 	return prot;
 }
@@ -702,7 +702,7 @@ static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
 	dma_addr_t dev_addr = iommu_dma_map_page(dev, page, offset, size, prot);
 
 	if (!iommu_dma_mapping_error(dev, dev_addr) &&
-	    !dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	    (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
 		__iommu_sync_single_for_device(dev, dev_addr, size, dir);
 
 	return dev_addr;
@@ -712,7 +712,7 @@ static void __iommu_unmap_page(struct device *dev, dma_addr_t dev_addr,
 			       size_t size, enum dma_data_direction dir,
 			       unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC == 0)
 		__iommu_sync_single_for_cpu(dev, dev_addr, size, dir);
 
 	iommu_dma_unmap_page(dev, dev_addr, size, dir, attrs);
@@ -752,7 +752,7 @@ static int __iommu_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
 {
 	bool coherent = is_device_dma_coherent(dev);
 
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC == 0)
 		__iommu_sync_sg_for_device(dev, sgl, nelems, dir);
 
 	return iommu_dma_map_sg(dev, sgl, nelems,
@@ -764,7 +764,7 @@ static void __iommu_unmap_sg_attrs(struct device *dev,
 				   enum dma_data_direction dir,
 				   unsigned long attrs)
 {
-	if (!dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
+	if ((attrs & DMA_ATTR_SKIP_CPU_SYNC == 0)
 		__iommu_sync_sg_for_cpu(dev, sgl, nelems, dir);
 
 	iommu_dma_unmap_sg(dev, sgl, nelems, dir, attrs);
diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
index fc51f4421933..58610d0df7ed 100644
--- a/arch/avr32/mm/dma-coherent.c
+++ b/arch/avr32/mm/dma-coherent.c
@@ -109,7 +109,7 @@ static void *avr32_dma_alloc(struct device *dev, size_t size,
 		return NULL;
 	phys = page_to_phys(page);
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
+	if (attrs & DMA_ATTR_WRITE_COMBINE) {
 		/* Now, map the page into P3 with write-combining turned on */
 		*handle = phys;
 		return __ioremap(phys, size, _PAGE_BUFFER);
@@ -123,7 +123,7 @@ static void avr32_dma_free(struct device *dev, size_t size,
 {
 	struct page *page;
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
+	if (attrs & DMA_ATTR_WRITE_COMBINE) {
 		iounmap(cpu_addr);
 
 		page = phys_to_page(handle);
diff --git a/arch/ia64/sn/pci/pci_dma.c b/arch/ia64/sn/pci/pci_dma.c
index 6b78fc953c4b..74c934a997bb 100644
--- a/arch/ia64/sn/pci/pci_dma.c
+++ b/arch/ia64/sn/pci/pci_dma.c
@@ -183,14 +183,11 @@ static dma_addr_t sn_dma_map_page(struct device *dev, struct page *page,
 	unsigned long phys_addr;
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
-	int dmabarr;
-
-	dmabarr = dma_get_attr(DMA_ATTR_WRITE_BARRIER, attrs);
 
 	BUG_ON(!dev_is_pci(dev));
 
 	phys_addr = __pa(cpu_addr);
-	if (dmabarr)
+	if (attrs & DMA_ATTR_WRITE_BARRIER)
 		dma_addr = provider->dma_map_consistent(pdev, phys_addr,
 							size, SN_DMA_ADDR_PHYS);
 	else
@@ -280,9 +277,6 @@ static int sn_dma_map_sg(struct device *dev, struct scatterlist *sgl,
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
 	int i;
-	int dmabarr;
-
-	dmabarr = dma_get_attr(DMA_ATTR_WRITE_BARRIER, attrs);
 
 	BUG_ON(!dev_is_pci(dev));
 
@@ -292,7 +286,7 @@ static int sn_dma_map_sg(struct device *dev, struct scatterlist *sgl,
 	for_each_sg(sgl, sg, nhwentries, i) {
 		dma_addr_t dma_addr;
 		phys_addr = SG_ENT_PHYS_ADDRESS(sg);
-		if (dmabarr)
+		if (attrs & DMA_ATTR_WRITE_BARRIER)
 			dma_addr = provider->dma_map_consistent(pdev,
 								phys_addr,
 								sg->length,
diff --git a/arch/metag/kernel/dma.c b/arch/metag/kernel/dma.c
index d68f498e82a1..0db31e24c541 100644
--- a/arch/metag/kernel/dma.c
+++ b/arch/metag/kernel/dma.c
@@ -337,7 +337,7 @@ static int metag_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 	struct metag_vm_region *c;
 	int ret = -ENXIO;
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (attrs & DMA_ATTR_WRITE_COMBINE)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
 	else
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c
index c9052108094f..0306dd0f300a 100644
--- a/arch/mips/mm/dma-default.c
+++ b/arch/mips/mm/dma-default.c
@@ -141,7 +141,7 @@ static void *mips_dma_alloc_coherent(struct device *dev, size_t size,
 	 * XXX: seems like the coherent and non-coherent implementations could
 	 * be consolidated.
 	 */
-	if (dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	if (attrs & DMA_ATTR_NON_CONSISTENT)
 		return mips_dma_alloc_noncoherent(dev, size, dma_handle, gfp);
 
 	gfp = massage_gfp_flags(dev, gfp);
@@ -182,7 +182,7 @@ static void mips_dma_free_coherent(struct device *dev, size_t size, void *vaddr,
 	unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
 	struct page *page = NULL;
 
-	if (dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if (attrs & DMA_ATTR_NON_CONSISTENT) {
 		mips_dma_free_noncoherent(dev, size, vaddr, dma_handle);
 		return;
 	}
@@ -214,7 +214,7 @@ static int mips_dma_mmap(struct device *dev, struct vm_area_struct *vma,
 
 	pfn = page_to_pfn(virt_to_page((void *)addr));
 
-	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs))
+	if (attrs & DMA_ATTR_WRITE_COMBINE)
 		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
 	else
 		vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
diff --git a/arch/openrisc/kernel/dma.c b/arch/openrisc/kernel/dma.c
index 50eb1f26c540..140c99140649 100644
--- a/arch/openrisc/kernel/dma.c
+++ b/arch/openrisc/kernel/dma.c
@@ -100,7 +100,7 @@ or1k_dma_alloc(struct device *dev, size_t size,
 
 	va = (unsigned long)page;
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0) {
 		/*
 		 * We need to iterate through the pages, clearing the dcache for
 		 * them and setting the cache-inhibit bit.
@@ -124,7 +124,7 @@ or1k_dma_free(struct device *dev, size_t size, void *vaddr,
 		.mm = &init_mm
 	};
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs)) {
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0) {
 		/* walk_page_range shouldn't be able to fail here */
 		WARN_ON(walk_page_range(va, va + size, &walk));
 	}
diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c
index 845fdd52e4c5..02d9ed0f3949 100644
--- a/arch/parisc/kernel/pci-dma.c
+++ b/arch/parisc/kernel/pci-dma.c
@@ -581,7 +581,7 @@ static void *pcx_dma_alloc(struct device *dev, size_t size,
 {
 	void *addr;
 
-	if (!dma_get_attr(DMA_ATTR_NON_CONSISTENT, attrs))
+	if ((attrs & DMA_ATTR_NON_CONSISTENT) == 0)
 		return NULL;
 
 	addr = (void *)__get_free_pages(flag, get_order(size));
diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
index 5d1a7ef3fdee..2018e562d124 100644
--- a/arch/powerpc/platforms/cell/iommu.c
+++ b/arch/powerpc/platforms/cell/iommu.c
@@ -193,7 +193,7 @@ static int tce_build_cell(struct iommu_table *tbl, long index, long npages,
 	base_pte = CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | CBE_IOPTE_M |
 		CBE_IOPTE_SO_RW | (window->ioid & CBE_IOPTE_IOID_Mask);
 #endif
-	if (unlikely(dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs)))
+	if (unlikely(attrs & DMA_ATTR_WEAK_ORDERING))
 		base_pte &= ~CBE_IOPTE_SO_RW;
 
 	io_pte = (unsigned long *)tbl->it_base + (index - tbl->it_offset);
@@ -600,7 +600,7 @@ static dma_addr_t dma_fixed_map_page(struct device *dev, struct page *page,
 				     enum dma_data_direction direction,
 				     unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		return dma_direct_ops.map_page(dev, page, offset, size,
 					       direction, attrs);
 	else
@@ -613,7 +613,7 @@ static void dma_fixed_unmap_page(struct device *dev, dma_addr_t dma_addr,
 				 size_t size, enum dma_data_direction direction,
 				 unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		dma_direct_ops.unmap_page(dev, dma_addr, size, direction,
 					  attrs);
 	else
@@ -625,7 +625,7 @@ static int dma_fixed_map_sg(struct device *dev, struct scatterlist *sg,
 			   int nents, enum dma_data_direction direction,
 			   unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		return dma_direct_ops.map_sg(dev, sg, nents, direction, attrs);
 	else
 		return ppc_iommu_map_sg(dev, cell_get_iommu_table(dev), sg,
@@ -637,7 +637,7 @@ static void dma_fixed_unmap_sg(struct device *dev, struct scatterlist *sg,
 			       int nents, enum dma_data_direction direction,
 			       unsigned long attrs)
 {
-	if (iommu_fixed_is_weak == dma_get_attr(DMA_ATTR_WEAK_ORDERING, attrs))
+	if (iommu_fixed_is_weak == (attrs & DMA_ATTR_WEAK_ORDERING))
 		dma_direct_ops.unmap_sg(dev, sg, nents, direction, attrs);
 	else
 		ppc_iommu_unmap_sg(cell_get_iommu_table(dev), sg, nents,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
index 7b1788e2a808..4a28d6348c76 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_gem.c
@@ -273,7 +273,7 @@ void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
 {
 	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
 
-	if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, rk_obj->dma_attrs))
+	if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
 		return NULL;
 
 	return rk_obj->kvaddr;
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 6c1bda504fb1..08a1e2f3690f 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -306,7 +306,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
 	} else {
 		size = ALIGN(size, min_size);
 	}
-	if (dma_get_attr(DMA_ATTR_ALLOC_SINGLE_PAGES, attrs))
+	if (attrs & DMA_ATTR_ALLOC_SINGLE_PAGES)
 		alloc_sizes = min_size;
 
 	count = PAGE_ALIGN(size) >> PAGE_SHIFT;
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 8009a582326b..d2ab541614b8 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -160,7 +160,7 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	if (!dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, buf->attrs))
+	if ((buf->attrs & DMA_ATTR_NO_KERNEL_MAPPING) == 0)
 		buf->vaddr = buf->cookie;
 
 	/* Prevent the device from being released while the buffer is used */
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index b752b9ccec78..6d013ba94213 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -101,19 +101,6 @@ static inline int is_device_dma_capable(struct device *dev)
 	return dev->dma_mask != NULL && *dev->dma_mask != DMA_MASK_NONE;
 }
 
-/**
- * dma_get_attr - check for a specific attribute
- * @attr: attribute to look for
- * @attrs: attributes to check within
- *
- * Unlike all other dma-mapping functions, this one gets pointer to
- * attributes (for old-code compatiblity reasons).
- */
-static inline bool dma_get_attr(unsigned long attr, unsigned long attrs)
-{
-	return !!(attr & attrs);
-}
-
 #ifdef CONFIG_HAVE_GENERIC_DMA_COHERENT
 /*
  * These three functions are only for dma allocator.
-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun  2 08:39:21 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 02 Jun 2016 17:39:21 +0200
Subject: [RFC v3 19/45] [media] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1464881987-13203-20-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/media/platform/sti/bdisp/bdisp-hw.c    | 26 +++++++---------------
 drivers/media/v4l2-core/videobuf2-dma-contig.c | 30 +++++++++++---------------
 drivers/media/v4l2-core/videobuf2-dma-sg.c     | 19 ++++------------
 include/media/videobuf2-dma-contig.h           |  7 ++----
 4 files changed, 26 insertions(+), 56 deletions(-)

diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/sti/bdisp/bdisp-hw.c
index 052c932ac942..1600958939a5 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-hw.c
+++ b/drivers/media/platform/sti/bdisp/bdisp-hw.c
@@ -125,14 +125,11 @@ int bdisp_hw_get_and_clear_irq(struct bdisp_dev *bdisp)
  */
 void bdisp_hw_free_nodes(struct bdisp_ctx *ctx)
 {
-	if (ctx && ctx->node[0]) {
-		DEFINE_DMA_ATTRS(attrs);
-
-		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+	if (ctx && ctx->node[0])
 		dma_free_attrs(ctx->bdisp_dev->dev,
 			       sizeof(struct bdisp_node) * MAX_NB_NODE,
-			       ctx->node[0], ctx->node_paddr[0], &attrs);
-	}
+			       ctx->node[0], ctx->node_paddr[0],
+			       DMA_ATTR_WRITE_COMBINE);
 }
 
 /**
@@ -150,12 +147,10 @@ int bdisp_hw_alloc_nodes(struct bdisp_ctx *ctx)
 	unsigned int i, node_size = sizeof(struct bdisp_node);
 	void *base;
 	dma_addr_t paddr;
-	DEFINE_DMA_ATTRS(attrs);
 
 	/* Allocate all the nodes within a single memory page */
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
 	base = dma_alloc_attrs(dev, node_size * MAX_NB_NODE, &paddr,
-			       GFP_KERNEL | GFP_DMA, &attrs);
+			       GFP_KERNEL | GFP_DMA, DMA_ATTR_WRITE_COMBINE);
 	if (!base) {
 		dev_err(dev, "%s no mem\n", __func__);
 		return -ENOMEM;
@@ -188,13 +183,9 @@ void bdisp_hw_free_filters(struct device *dev)
 {
 	int size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
 
-	if (bdisp_h_filter[0].virt) {
-		DEFINE_DMA_ATTRS(attrs);
-
-		dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
+	if (bdisp_h_filter[0].virt)
 		dma_free_attrs(dev, size, bdisp_h_filter[0].virt,
-			       bdisp_h_filter[0].paddr, &attrs);
-	}
+			       bdisp_h_filter[0].paddr,DMA_ATTR_WRITE_COMBINE);
 }
 
 /**
@@ -211,12 +202,11 @@ int bdisp_hw_alloc_filters(struct device *dev)
 	unsigned int i, size;
 	void *base;
 	dma_addr_t paddr;
-	DEFINE_DMA_ATTRS(attrs);
 
 	/* Allocate all the filters within a single memory page */
 	size = (BDISP_HF_NB * NB_H_FILTER) + (BDISP_VF_NB * NB_V_FILTER);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &attrs);
-	base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA, &attrs);
+	base = dma_alloc_attrs(dev, size, &paddr, GFP_KERNEL | GFP_DMA,
+			       DMA_ATTR_WRITE_COMBINE);
 	if (!base)
 		return -ENOMEM;
 
diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c
index 5361197f3e57..8009a582326b 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-contig.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c
@@ -23,7 +23,7 @@
 
 struct vb2_dc_conf {
 	struct device		*dev;
-	struct dma_attrs	attrs;
+	unsigned long		attrs;
 };
 
 struct vb2_dc_buf {
@@ -32,7 +32,7 @@ struct vb2_dc_buf {
 	unsigned long			size;
 	void				*cookie;
 	dma_addr_t			dma_addr;
-	struct dma_attrs		attrs;
+	unsigned long			attrs;
 	enum dma_data_direction		dma_dir;
 	struct sg_table			*dma_sgt;
 	struct frame_vector		*vec;
@@ -135,7 +135,7 @@ static void vb2_dc_put(void *buf_priv)
 		kfree(buf->sgt_base);
 	}
 	dma_free_attrs(buf->dev, buf->size, buf->cookie, buf->dma_addr,
-			&buf->attrs);
+		       buf->attrs);
 	put_device(buf->dev);
 	kfree(buf);
 }
@@ -153,14 +153,14 @@ static void *vb2_dc_alloc(void *alloc_ctx, unsigned long size,
 
 	buf->attrs = conf->attrs;
 	buf->cookie = dma_alloc_attrs(dev, size, &buf->dma_addr,
-					GFP_KERNEL | gfp_flags, &buf->attrs);
+					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))
+	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 */
@@ -194,7 +194,7 @@ static int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma)
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(buf->dev, vma, buf->cookie,
-		buf->dma_addr, buf->size, &buf->attrs);
+		buf->dma_addr, buf->size, buf->attrs);
 
 	if (ret) {
 		pr_err("Remapping memory failed, error: %d\n", ret);
@@ -377,7 +377,7 @@ static struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf)
 	}
 
 	ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr,
-		buf->size, &buf->attrs);
+		buf->size, buf->attrs);
 	if (ret < 0) {
 		dev_err(buf->dev, "failed to get scatterlist from DMA API\n");
 		kfree(sgt);
@@ -426,15 +426,12 @@ static void vb2_dc_put_userptr(void *buf_priv)
 	struct page **pages;
 
 	if (sgt) {
-		DEFINE_DMA_ATTRS(attrs);
-
-		dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 		/*
 		 * No need to sync to CPU, it's already synced to the CPU
 		 * since the finish() memop will have been called before this.
 		 */
 		dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-				   buf->dma_dir, &attrs);
+				   buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 		pages = frame_vector_pages(buf->vec);
 		/* sgt should exist only if vector contains pages... */
 		BUG_ON(IS_ERR(pages));
@@ -490,9 +487,6 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
 	struct sg_table *sgt;
 	unsigned long contig_size;
 	unsigned long dma_align = dma_get_cache_alignment();
-	DEFINE_DMA_ATTRS(attrs);
-
-	dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 
 	/* Only cache aligned DMA transfers are reliable */
 	if (!IS_ALIGNED(vaddr | size, dma_align)) {
@@ -554,7 +548,7 @@ static void *vb2_dc_get_userptr(void *alloc_ctx, unsigned long vaddr,
 	 * prepare() memop is called.
 	 */
 	sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-				      buf->dma_dir, &attrs);
+				      buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 	if (sgt->nents <= 0) {
 		pr_err("failed to map scatterlist\n");
 		ret = -EIO;
@@ -578,7 +572,7 @@ out:
 
 fail_map_sg:
 	dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-			   buf->dma_dir, &attrs);
+			   buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 
 fail_sgt_init:
 	sg_free_table(sgt);
@@ -730,7 +724,7 @@ const struct vb2_mem_ops vb2_dma_contig_memops = {
 EXPORT_SYMBOL_GPL(vb2_dma_contig_memops);
 
 void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
-				    struct dma_attrs *attrs)
+				    unsigned long attrs)
 {
 	struct vb2_dc_conf *conf;
 
@@ -740,7 +734,7 @@ void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
 
 	conf->dev = dev;
 	if (attrs)
-		conf->attrs = *attrs;
+		conf->attrs = attrs;
 
 	return conf;
 }
diff --git a/drivers/media/v4l2-core/videobuf2-dma-sg.c b/drivers/media/v4l2-core/videobuf2-dma-sg.c
index 9985c89f0513..94f24e610fe7 100644
--- a/drivers/media/v4l2-core/videobuf2-dma-sg.c
+++ b/drivers/media/v4l2-core/videobuf2-dma-sg.c
@@ -107,9 +107,6 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
 	struct sg_table *sgt;
 	int ret;
 	int num_pages;
-	DEFINE_DMA_ATTRS(attrs);
-
-	dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 
 	if (WARN_ON(alloc_ctx == NULL))
 		return NULL;
@@ -148,7 +145,7 @@ static void *vb2_dma_sg_alloc(void *alloc_ctx, unsigned long size,
 	 * prepare() memop is called.
 	 */
 	sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-				      buf->dma_dir, &attrs);
+				      buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 	if (!sgt->nents)
 		goto fail_map;
 
@@ -183,13 +180,10 @@ static void vb2_dma_sg_put(void *buf_priv)
 	int i = buf->num_pages;
 
 	if (atomic_dec_and_test(&buf->refcount)) {
-		DEFINE_DMA_ATTRS(attrs);
-
-		dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 		dprintk(1, "%s: Freeing buffer of %d pages\n", __func__,
 			buf->num_pages);
 		dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-				   buf->dma_dir, &attrs);
+				   buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 		if (buf->vaddr)
 			vm_unmap_ram(buf->vaddr, buf->num_pages);
 		sg_free_table(buf->dma_sgt);
@@ -233,10 +227,8 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
 	struct vb2_dma_sg_conf *conf = alloc_ctx;
 	struct vb2_dma_sg_buf *buf;
 	struct sg_table *sgt;
-	DEFINE_DMA_ATTRS(attrs);
 	struct frame_vector *vec;
 
-	dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 	buf = kzalloc(sizeof *buf, GFP_KERNEL);
 	if (!buf)
 		return NULL;
@@ -267,7 +259,7 @@ static void *vb2_dma_sg_get_userptr(void *alloc_ctx, unsigned long vaddr,
 	 * prepare() memop is called.
 	 */
 	sgt->nents = dma_map_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents,
-				      buf->dma_dir, &attrs);
+				      buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC);
 	if (!sgt->nents)
 		goto userptr_fail_map;
 
@@ -291,14 +283,11 @@ static void vb2_dma_sg_put_userptr(void *buf_priv)
 	struct vb2_dma_sg_buf *buf = buf_priv;
 	struct sg_table *sgt = &buf->sg_table;
 	int i = buf->num_pages;
-	DEFINE_DMA_ATTRS(attrs);
-
-	dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
 
 	dprintk(1, "%s: Releasing userspace buffer of %d pages\n",
 	       __func__, buf->num_pages);
 	dma_unmap_sg_attrs(buf->dev, sgt->sgl, sgt->orig_nents, buf->dma_dir,
-			   &attrs);
+			   DMA_ATTR_SKIP_CPU_SYNC);
 	if (buf->vaddr)
 		vm_unmap_ram(buf->vaddr, buf->num_pages);
 	sg_free_table(buf->dma_sgt);
diff --git a/include/media/videobuf2-dma-contig.h b/include/media/videobuf2-dma-contig.h
index 2087c9a68be3..048df1320451 100644
--- a/include/media/videobuf2-dma-contig.h
+++ b/include/media/videobuf2-dma-contig.h
@@ -16,8 +16,6 @@
 #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)
 {
@@ -26,12 +24,11 @@ vb2_dma_contig_plane_dma_addr(struct vb2_buffer *vb, unsigned int plane_no)
 	return *addr;
 }
 
-void *vb2_dma_contig_init_ctx_attrs(struct device *dev,
-				    struct dma_attrs *attrs);
+void *vb2_dma_contig_init_ctx_attrs(struct device *dev, unsigned long attrs);
 
 static inline void *vb2_dma_contig_init_ctx(struct device *dev)
 {
-	return vb2_dma_contig_init_ctx_attrs(dev, NULL);
+	return vb2_dma_contig_init_ctx_attrs(dev, 0);
 }
 
 void vb2_dma_contig_cleanup_ctx(void *alloc_ctx);
-- 
1.9.1



From broonie at kernel.org  Thu Jun  2 10:21:28 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 02 Jun 2016 18:21:28 +0100
Subject: Applied "ASoC: mediatek: add MCLK source selection" to the asoc tree
In-Reply-To: <1464782412-632-1-git-send-email-pc.liao@mediatek.com>
Message-ID: <E1b8WJE-0006KH-Sl@finisterre>

The patch

   ASoC: mediatek: add MCLK source selection

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 0c198ed2ba68b6cc728c5aa3c5486d9e5c328ecd Mon Sep 17 00:00:00 2001
From: PC Liao <pc.liao at mediatek.com>
Date: Wed, 1 Jun 2016 20:00:12 +0800
Subject: [PATCH] ASoC: mediatek: add MCLK source selection

The new machine's MCLK source is from mt8173 which is dynamic from
sampling rate*256. This patch provides the selection for device tree.

Signed-off-by: PC Liao <pc.liao at mediatek.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 .../devicetree/bindings/sound/mt8173-rt5650.txt    |  5 +++
 sound/soc/mediatek/mt8173-rt5650.c                 | 45 +++++++++++++++++++++-
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
index 5bfa6b60530b..f250fc7c7acc 100644
--- a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
+++ b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
@@ -12,12 +12,17 @@ Required codec-capture subnode properties:
   <&rt5650 0> : Default setting. Connect rt5650 I2S1 for capture. (dai_name = rt5645-aif1)
   <&rt5650 1> : Connect rt5650 I2S2 for capture. (dai_name = rt5645-aif2)
 
+- mediatek,mclk: the MCLK source
+  0 : external oscillator, MCLK = 12.288M
+  1 : internal source from mt8173, MCLK = sampling rate*256
+
 Example:
 
 	sound {
 		compatible = "mediatek,mt8173-rt5650";
 		mediatek,audio-codec = <&rt5650>;
 		mediatek,platform = <&afe>;
+		mediatek,mclk = <0>;
 		codec-capture {
 			sound-dai = <&rt5650 1>;
 		};
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c
index a27a6673dbe3..072934b470a8 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173-rt5650.c
@@ -23,6 +23,20 @@
 
 #define MCLK_FOR_CODECS		12288000
 
+enum mt8173_rt5650_mclk {
+	MT8173_RT5650_MCLK_EXTERNAL = 0,
+	MT8173_RT5650_MCLK_INTERNAL,
+};
+
+struct mt8173_rt5650_platform_data {
+	enum mt8173_rt5650_mclk pll_from;
+	/* 0 = external oscillator; 1 = internal source from mt8173 */
+};
+
+static struct mt8173_rt5650_platform_data mt8173_rt5650_priv = {
+	.pll_from = MT8173_RT5650_MCLK_EXTERNAL,
+};
+
 static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
 	SND_SOC_DAPM_SPK("Speaker", NULL),
 	SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -54,13 +68,29 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
 				   struct snd_pcm_hw_params *params)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int mclk_clock;
 	int i, ret;
 
+	switch (mt8173_rt5650_priv.pll_from) {
+	case MT8173_RT5650_MCLK_EXTERNAL:
+		/* mclk = 12.288M */
+		mclk_clock = MCLK_FOR_CODECS;
+		break;
+	case MT8173_RT5650_MCLK_INTERNAL:
+		/* mclk = sampling rate*256 */
+		mclk_clock = params_rate(params) * 256;
+		break;
+	default:
+		/* mclk = 12.288M */
+		mclk_clock = MCLK_FOR_CODECS;
+		break;
+	}
+
 	for (i = 0; i < rtd->num_codecs; i++) {
 		struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
 
-		/* pll from mclk 12.288M */
-		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+		/* pll from mclk */
+		ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
 					  params_rate(params) * 512);
 		if (ret)
 			return ret;
@@ -243,6 +273,17 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
 		mt8173_rt5650_codecs[1].dai_name = codec_capture_dai;
 	}
 
+	if (device_property_present(&pdev->dev, "mediatek,mclk")) {
+		ret = device_property_read_u32(&pdev->dev,
+					       "mediatek,mclk",
+					       &mt8173_rt5650_priv.pll_from);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s snd_soc_register_card fail %d\n",
+				__func__, ret);
+		}
+	}
+
 	card->dev = &pdev->dev;
 	platform_set_drvdata(pdev, card);
 
-- 
2.8.1



From laurent.pinchart at ideasonboard.com  Thu Jun  2 13:57:14 2016
From: laurent.pinchart at ideasonboard.com (Laurent Pinchart)
Date: Thu, 02 Jun 2016 23:57:14 +0300
Subject: [PATCH 09/20] drm: rcar-du: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-10-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-10-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <5863270.jtrd0Kruzj@avalon>

Hi Boris,

Thank you for the patch.

On Thursday 02 Jun 2016 16:31:36 Boris Brezillon wrote:
> All outputs have a 1:1 relationship between connectors and encoders,
> and the driver is relying on the atomic helpers: we can drop the custom
> ->best_encoder() implementations and let the core call
> drm_atomic_helper_best_encoder() for us.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ------------
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.h |  3 ---
>  drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c |  1 -
>  drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c |  1 -
>  drivers/gpu/drm/rcar-du/rcar_du_vgacon.c  |  1 -
>  5 files changed, 18 deletions(-)
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 4e939e4..55149e9 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> @@ -27,18 +27,6 @@
>  #include "rcar_du_vgacon.h"
> 
>  /*
> ---------------------------------------------------------------------------
> -- - * Common connector functions
> - */
> -
> -struct drm_encoder *
> -rcar_du_connector_best_encoder(struct drm_connector *connector)
> -{
> -	struct rcar_du_connector *rcon = to_rcar_connector(connector);
> -
> -	return rcar_encoder_to_drm_encoder(rcon->encoder);
> -}
> -
> -/*
> ---------------------------------------------------------------------------
> -- * Encoder
>   */
> 
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 719b6f2a..a8669c3 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> @@ -49,9 +49,6 @@ struct rcar_du_connector {
>  #define to_rcar_connector(c) \
>  	container_of(c, struct rcar_du_connector, connector)
> 
> -struct drm_encoder *
> -rcar_du_connector_best_encoder(struct drm_connector *connector);
> -
>  int rcar_du_encoder_init(struct rcar_du_device *rcdu,
>  			 enum rcar_du_encoder_type type,
>  			 enum rcar_du_output output,
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 6c92714..612b4d5 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> @@ -52,7 +52,6 @@ static int rcar_du_hdmi_connector_mode_valid(struct
> drm_connector *connector, static const struct drm_connector_helper_funcs
> connector_helper_funcs = { .get_modes = rcar_du_hdmi_connector_get_modes,
>  	.mode_valid = rcar_du_hdmi_connector_mode_valid,
> -	.best_encoder = rcar_du_connector_best_encoder,
>  };
> 
>  static enum drm_connector_status
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index e905f5d..6afd0af 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> @@ -59,7 +59,6 @@ static int rcar_du_lvds_connector_get_modes(struct
> drm_connector *connector)
> 
>  static const struct drm_connector_helper_funcs connector_helper_funcs = {
>  	.get_modes = rcar_du_lvds_connector_get_modes,
> -	.best_encoder = rcar_du_connector_best_encoder,
>  };
> 
>  static enum drm_connector_status
> diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 9d7e5c9..68f7ffa 100644
> --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> @@ -28,7 +28,6 @@ static int rcar_du_vga_connector_get_modes(struct
> drm_connector *connector)
> 
>  static const struct drm_connector_helper_funcs connector_helper_funcs = {
>  	.get_modes = rcar_du_vga_connector_get_modes,
> -	.best_encoder = rcar_du_connector_best_encoder,
>  };
> 
>  static enum drm_connector_status

You can also remove

        rcon->encoder = renc;

from rcar_du_vga_connector_init(), it's not needed anymore. The same code in 
rcar_du_hdmi_connector_init() has to stay for now though, as it's used to 
locate the slave encoder in the HDMI support code. That should change when the 
driver will be converted to use drm_bridge.

I can also fix this during the conversion to drm_bridge if you don't want to 
resubmit. In any case,

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

-- 
Regards,

Laurent Pinchart



From laurent.pinchart at ideasonboard.com  Thu Jun  2 14:00:07 2016
From: laurent.pinchart at ideasonboard.com (Laurent Pinchart)
Date: Fri, 03 Jun 2016 00:00:07 +0300
Subject: [PATCH 16/20] drm: omap: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-17-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-17-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <3702911.4V6efqXNem@avalon>

Hi Boris,

Thank you for the patch.

On Thursday 02 Jun 2016 16:31:43 Boris Brezillon wrote:
> We have a 1:1 relationship between connectors and encoders and the
> driver is relying on the atomic helpers: we can drop the custom
> ->best_encoder() implementation and let the core call
> drm_atomic_helper_best_encoder() for us.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>  drivers/gpu/drm/omapdrm/omap_connector.c | 8 --------
>  1 file changed, 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c
> b/drivers/gpu/drm/omapdrm/omap_connector.c index ce2d67b..80af5e1 100644
> --- a/drivers/gpu/drm/omapdrm/omap_connector.c
> +++ b/drivers/gpu/drm/omapdrm/omap_connector.c
> @@ -256,13 +256,6 @@ static int omap_connector_mode_valid(struct
> drm_connector *connector, return ret;
>  }
> 
> -struct drm_encoder *omap_connector_attached_encoder(
> -		struct drm_connector *connector)
> -{
> -	struct omap_connector *omap_connector = to_omap_connector(connector);
> -	return omap_connector->encoder;

The omap_connector::encoder field is assigned but not used anymore, you can 
remove it.

With that fixed,

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

> -}
> -
>  static const struct drm_connector_funcs omap_connector_funcs = {
>  	.dpms = drm_atomic_helper_connector_dpms,
>  	.reset = drm_atomic_helper_connector_reset,
> @@ -276,7 +269,6 @@ static const struct drm_connector_funcs
> omap_connector_funcs = { static const struct drm_connector_helper_funcs
> omap_connector_helper_funcs = { .get_modes = omap_connector_get_modes,
>  	.mode_valid = omap_connector_mode_valid,
> -	.best_encoder = omap_connector_attached_encoder,
>  };
> 
>  /* initialize connector */

-- 
Regards,

Laurent Pinchart



From laurent.pinchart at ideasonboard.com  Thu Jun  2 14:05:46 2016
From: laurent.pinchart at ideasonboard.com (Laurent Pinchart)
Date: Fri, 03 Jun 2016 00:05:46 +0300
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <8671722.LHaiaBNqfE@avalon>

Hi Boris,

Thank you for the patch.

On Thursday 02 Jun 2016 16:31:28 Boris Brezillon wrote:
> Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
> drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
> that DRM drivers can leave this hook unassigned if they know they want
> to use drm_atomic_helper_best_encoder().

Could you please update include/drm/drm_modeset_helper_vtables.h to document 
this new behaviour ?

The only drawback I see in this patch is the additional object lookup 
performed by drm_atomic_helper_best_encoder() at runtime. I wonder if we could 
somehow cache the information, as the assignment can't change when there's a 
1:1 correspondence between encoders and connectors.

> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>  drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
>  drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
>  2 files changed, 15 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> b/drivers/gpu/drm/drm_atomic_helper.c index f6a3350..849d029 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state
> *state, if (funcs->atomic_best_encoder)
>  		new_encoder = funcs->atomic_best_encoder(connector,
>  							 connector_state);
> -	else
> +	else if (funcs->best_encoder)
>  		new_encoder = funcs->best_encoder(connector);
> +	else
> +		new_encoder = drm_atomic_helper_best_encoder(connector);
> 
>  	if (!new_encoder) {
>  		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
> diff --git a/drivers/gpu/drm/drm_fb_helper.c
> b/drivers/gpu/drm/drm_fb_helper.c index 7c2eb75..d44389a 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper
> *fb_helper, my_score++;
> 
>  	connector_funcs = connector->helper_private;
> -	encoder = connector_funcs->best_encoder(connector);
> +
> +	/*
> +	 * If the DRM device implements atomic hooks and ->best_encoder() is
> +	 * NULL we fallback to the default drm_atomic_helper_best_encoder()
> +	 * helper.
> +	 */
> +	if (fb_helper->dev->mode_config.funcs->atomic_commit &&
> +	    !connector_funcs->best_encoder)
> +		encoder = drm_atomic_helper_best_encoder(connector);
> +	else
> +		encoder = connector_funcs->best_encoder(connector);
> +
>  	if (!encoder)
>  		goto out;

-- 
Regards,

Laurent Pinchart



From daniel at ffwll.ch  Thu Jun  2 14:57:02 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Thu, 2 Jun 2016 23:57:02 +0200
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <8671722.LHaiaBNqfE@avalon>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
 <8671722.LHaiaBNqfE@avalon>
Message-ID: <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>

On Thu, Jun 2, 2016 at 11:05 PM, Laurent Pinchart
<laurent.pinchart at ideasonboard.com> wrote:
> Hi Boris,
>
> Thank you for the patch.
>
> On Thursday 02 Jun 2016 16:31:28 Boris Brezillon wrote:
>> Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
>> drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
>> that DRM drivers can leave this hook unassigned if they know they want
>> to use drm_atomic_helper_best_encoder().
>
> Could you please update include/drm/drm_modeset_helper_vtables.h to document
> this new behaviour ?

Thanks for reminding me. Please update hooks for both
atomic_best_encoder and best_encoder. Also mention that it's only
optional for atomic drivers. There's lots of examples in that file for
the wording usually used.

> The only drawback I see in this patch is the additional object lookup
> performed by drm_atomic_helper_best_encoder() at runtime. I wonder if we could
> somehow cache the information, as the assignment can't change when there's a
> 1:1 correspondence between encoders and connectors.

drm_encoder_find is an idr lookup. That should be plenty fast,
especially for modeset code. Usually what's too expensive even for
modeset code is linear list walks. But Chris just submitted patches to
convert most of them into simple lookups.
-Daniel

>>> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
>> ---
>>  drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
>>  drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
>>  2 files changed, 15 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
>> b/drivers/gpu/drm/drm_atomic_helper.c index f6a3350..849d029 100644
>> --- a/drivers/gpu/drm/drm_atomic_helper.c
>> +++ b/drivers/gpu/drm/drm_atomic_helper.c
>> @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state
>> *state, if (funcs->atomic_best_encoder)
>>               new_encoder = funcs->atomic_best_encoder(connector,
>>                                                        connector_state);
>> -     else
>> +     else if (funcs->best_encoder)
>>               new_encoder = funcs->best_encoder(connector);
>> +     else
>> +             new_encoder = drm_atomic_helper_best_encoder(connector);
>>
>>       if (!new_encoder) {
>>               DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c
>> b/drivers/gpu/drm/drm_fb_helper.c index 7c2eb75..d44389a 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper
>> *fb_helper, my_score++;
>>
>>       connector_funcs = connector->helper_private;
>> -     encoder = connector_funcs->best_encoder(connector);
>> +
>> +     /*
>> +      * If the DRM device implements atomic hooks and ->best_encoder() is
>> +      * NULL we fallback to the default drm_atomic_helper_best_encoder()
>> +      * helper.
>> +      */
>> +     if (fb_helper->dev->mode_config.funcs->atomic_commit &&
>> +         !connector_funcs->best_encoder)
>> +             encoder = drm_atomic_helper_best_encoder(connector);
>> +     else
>> +             encoder = connector_funcs->best_encoder(connector);
>> +
>>       if (!encoder)
>>               goto out;
>
> --
> Regards,
>
> Laurent Pinchart
>



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch


From robh at kernel.org  Thu Jun  2 15:45:50 2016
From: robh at kernel.org (Rob Herring)
Date: Thu, 2 Jun 2016 17:45:50 -0500
Subject: [PATCH 1/3] dt-bindings: pwm: Add MediaTek display PWM bindings
In-Reply-To: <1464597712-5399-2-git-send-email-weiqing.kong@mediatek.com>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-2-git-send-email-weiqing.kong@mediatek.com>
Message-ID: <20160602224550.GA19256@rob-hp-laptop>

On Mon, May 30, 2016 at 04:41:50PM +0800, Weiqing Kong wrote:
> Add MT2701 compatible string.
> 
> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> ---
>  Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> index f8f59ba..f2f2fa9 100644
> --- a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> +++ b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> @@ -1,9 +1,10 @@
>  MediaTek display PWM controller
>  
>  Required properties:
> - - compatible: should be "mediatek,<name>-disp-pwm":
> -   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
> + - compatible: should be like these:
> +   - "mediatek,mt2701-disp-bls": found on mt2701 SoC.

Why bls instead of pwm?

>     - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
> +   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
>   - reg: physical base address and length of the controller's registers.
>   - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
>     the cell format.
> -- 
> 1.8.1.1.dirty
> 


From chris at chris-wilson.co.uk  Thu Jun  2 15:40:04 2016
From: chris at chris-wilson.co.uk (Chris Wilson)
Date: Thu, 2 Jun 2016 23:40:04 +0100
Subject: [Intel-gfx] [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
 <8671722.LHaiaBNqfE@avalon>
 <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>
Message-ID: <20160602224004.GA1819@nuc-i3427.alporthouse.com>

On Thu, Jun 02, 2016 at 11:57:02PM +0200, Daniel Vetter wrote:
> drm_encoder_find is an idr lookup. That should be plenty fast,
> especially for modeset code. Usually what's too expensive even for
> modeset code is linear list walks. But Chris just submitted patches to
> convert most of them into simple lookups.

For the idr_find, I'm tempted to replace the mutex with a rwlock. It
helps pathological cases, but in reality there are more crucial
locks around the hw that limit concurrency. ;-)
-Chris

-- 
Chris Wilson, Intel Open Source Technology Centre


From broonie at kernel.org  Thu Jun  2 17:16:00 2016
From: broonie at kernel.org (Mark Brown)
Date: Fri, 3 Jun 2016 01:16:00 +0100
Subject: [PATCH v2 1/3] regulator: DT: Add DT property for operation mode
 configuration
In-Reply-To: <1464682835.25847.3.camel@mtksdaap41>
References: <1463987611-32139-1-git-send-email-henryc.chen@mediatek.com>
 <1463987611-32139-2-git-send-email-henryc.chen@mediatek.com>
 <20160523104703.GB26992@leverpostej>
 <20160523112856.GR8206@sirena.org.uk>
 <1464682835.25847.3.camel@mtksdaap41>
Message-ID: <20160603001600.GX2282@sirena.org.uk>

On Tue, May 31, 2016 at 04:20:35PM +0800, Fan Chen wrote:
> On Mon, 2016-05-23 at 12:28 +0100, Mark Brown wrote:

> > Henry, *please* look at how the existing mode support in the bindings is
> > done and consider how a consumer would use this given that it doesn't
> > know anything about the regulator...

> In the case of svs[1], which Henry mentioned in cover letter, it can be
> regarded as a special consumer who requires very accurate voltage for
> calibration the hardware in its initialization stage. So, this kinds of
> consumers know their regulator very well and only need to switch to the
> modes they want in the particular conditions.

So what you're trying to do here is not so much set a specific mode as
set maximum regulation accuracy for a period of time.

> However, IIUC, you want a proposal to provide a sort of QoS framework
> which can cover most of use cases who care about the regular quality in
> runtime, is that correct?

Well, we want a coherent general use case that doesn't require a user to
know the specific details of the regulator they're working with since we
need to hide that knowledge from the user.

> IMHO, some quality index can be considered, for example:
> Minimum Current Requirement (mA): If a user specified this constraint in
> runtime, it means that he cares more about the supplying quality like
> transient voltage drop, ripple above certain load.
> Maximum Current Requirement (mA): If a user specified this constraint in
> runtime, it means that he cares more about the power consumption under
> certain load.
> It could be a flexible way instead to tie the operation modes directly.

I'm not sure I really understand these distinctions to be honest,
and specifying minimum loads seems very tricky from a robustness point
of view.

If all you need right now is a way to maximize regulation quality that's
probably a lot easier than anything based on absolute loads or on
multiple "normal operation" modes - it takes a lot of the complexity out
of things as there's no need to consider things like the distinctions
between modes.  We just need a standard operating mode and to know the
highest available mode.  I'm not sure exactly how to do that as an API
though, let me think about it...  your use case isn't one I'd come
across before.

> BTW, we should encourage people here to share more use cases related to
> regulator quality issues, especially in runtime, so we can evaluate the
> most suitable index to fit the requirements.

More common use cases are around manually doing adaptive mode switching
for regulators that are poor at automatically adjusting performance and
handling of very low standby current situations where the adaption can
consume enough power to register.
-------------- 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-mediatek/attachments/20160603/29a91d0d/attachment.sig>

From hongkun.cao at mediatek.com  Thu Jun  2 20:28:14 2016
From: hongkun.cao at mediatek.com (hongkun.cao at mediatek.com)
Date: Fri, 3 Jun 2016 11:28:14 +0800
Subject: [PATCH] pinctrl: mediatek: fix suspend/resume timing issue
Message-ID: <1464924495-20375-1-git-send-email-hongkun.cao@mediatek.com>

From: "hongkun.cao" <hongkun.cao at mediatek.com>

An irq which is a wake up source maybe masked unexpectedly if the wake
up source irq was triggered after pinctrl irqchip suspend and before
suspend_device_irqs finished.
Use *_noirq callbacks to guarantee pinctrl irqchip suspend would be
called after suspend_devices_irqs.

Signed-off-by: hongkun.cao <hongkun.cao at mediatek.com>
---
 drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
index a607655..ce554e0 100644
--- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
+++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c
@@ -1183,8 +1183,8 @@ static int mtk_eint_resume(struct device *device)
 }
 
 const struct dev_pm_ops mtk_eint_pm_ops = {
-	.suspend = mtk_eint_suspend,
-	.resume = mtk_eint_resume,
+	.suspend_noirq = mtk_eint_suspend,
+	.resume_noirq = mtk_eint_resume,
 };
 
 static void mtk_eint_ack(struct irq_data *d)
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:17 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:17 +0800
Subject: [alsa-devel] [PATCH v2 2/9] ASoC: mediatek: implement mediatek common
 structure
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-3-git-send-email-garlic.tseng@mediatek.com>

implement mediatek basic structure, include common private data,
afe fe dai operator and afe platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         | 387 +++++++++++++++++++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |  47 +++
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |  90 +++++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |  23 ++
 sound/soc/mediatek/common/mtk-base-afe.h           | 103 ++++++
 5 files changed, 650 insertions(+)
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h

diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 0000000..02d454d
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,387 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+			   unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	/*enable agent*/
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       0 << memif->data->agent_disable_shift);
+
+	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+	/*
+	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+	 * smaller than period_size due to AFE's internal buffer.
+	 * This easily leads to overrun when avail_min is period_size.
+	 * One more period can hold the possible unread buffer.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		int periods_max = mtk_afe_hardware->periods_max;
+
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+						   SNDRV_PCM_HW_PARAM_PERIODS,
+						   3, periods_max);
+		if (ret < 0) {
+			dev_err(afe->dev, "hw_constraint_minmax failed\n");
+			return ret;
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	/*dynamic allocate irq to memif */
+	if (memif->irq_usage < 0) {
+		int irq_id = mtk_dynamic_irq_acquire(afe);
+
+		if (irq_id != afe->irqs_size) {
+			/* link */
+			memif->irq_usage = irq_id;
+		} else {
+			dev_err(afe->dev, "%s() error: no more asys irq\n",
+				__func__);
+			ret = -EBUSY;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int irq_id;
+
+	irq_id = memif->irq_usage;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       1 << memif->data->agent_disable_shift);
+
+	if (!memif->const_irq) {
+		mtk_dynamic_irq_release(afe, irq_id);
+		memif->irq_usage = -1;
+		memif->substream = NULL;
+	}
+}
+EXPORT_SYMBOL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int msb_at_bit33 = 0;
+	int ret, fs = 0;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+			 memif->phys_buf_addr);
+	/* end */
+	mtk_regmap_write(afe->regmap,
+			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+			 memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set MSB to 33-bit */
+	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+			       1 << memif->data->msb_shift,
+			       msb_at_bit33 << memif->data->msb_shift);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+				       1 << memif->data->mono_shift,
+				       mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+			       memif->data->fs_maskbit << memif->data->fs_shift,
+			       fs << memif->data->fs_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+	unsigned int counter = runtime->period_size;
+	int fs;
+
+	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (memif->data->enable_shift >= 0)
+			mtk_regmap_update_bits(afe->regmap,
+					       memif->data->enable_reg,
+					       1 << memif->data->enable_shift,
+					       1 << memif->data->enable_shift);
+
+		/* set irq counter */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+				       irq_data->irq_cnt_maskbit
+				       << irq_data->irq_cnt_shift,
+				       counter << irq_data->irq_cnt_shift);
+
+		/* set irq fs */
+		fs = afe->irq_fs(substream, runtime->rate);
+
+		if (fs < 0)
+			return -EINVAL;
+
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+				       irq_data->irq_fs_maskbit
+				       << irq_data->irq_fs_shift,
+				       fs << irq_data->irq_fs_shift);
+
+		/* enable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       1 << irq_data->irq_en_shift);
+
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+				       1 << memif->data->enable_shift, 0);
+		/* disable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       0 << irq_data->irq_en_shift);
+		/* and clear pending IRQ */
+		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+				 1 << irq_data->irq_clr_shift);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int hd_audio = 0;
+
+	/*set hd mode*/
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hd_audio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hd_audio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		hd_audio = 1;
+		break;
+	default:
+		dev_err(afe->dev, "%s() error: unsupported format %d\n",
+			__func__, substream->runtime->format);
+		break;
+	}
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+			       1 << memif->data->hd_shift,
+			       hd_audio << memif->data->hd_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+	.startup	= mtk_afe_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mtk_afe_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+	int i;
+
+	mutex_lock(&irqs_lock);
+	for (i = 0; i < afe->irqs_size; ++i) {
+		if (afe->irqs[i].irq_occupyed == 0) {
+			afe->irqs[i].irq_occupyed = 1;
+			mutex_unlock(&irqs_lock);
+			return i;
+		}
+	}
+	mutex_unlock(&irqs_lock);
+	return afe->irqs_size;
+}
+EXPORT_SYMBOL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+	mutex_lock(&irqs_lock);
+	if (irq_id >= 0 && irq_id < afe->irqs_size) {
+		afe->irqs[irq_id].irq_occupyed = 0;
+		mutex_unlock(&irqs_lock);
+		return 0;
+	}
+	mutex_unlock(&irqs_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(mtk_dynamic_irq_release);
+
+void mtk_simple_isr(struct mtk_base_afe *afe, struct mtk_base_afe_memif *memif)
+{
+	snd_pcm_period_elapsed(memif->substream);
+}
+EXPORT_SYMBOL(mtk_simple_isr);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	dev_dbg(dev, "%s\n", __func__);
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	afe->suspended = true;
+	afe->runtime_suspend(dev);
+	return 0;
+}
+EXPORT_SYMBOL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	afe->runtime_resume(dev);
+
+	if (!afe->reg_back_up)
+		dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+				 afe->reg_back_up[i]);
+
+	afe->suspended = false;
+	return 0;
+}
+EXPORT_SYMBOL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 0000000..cb4df27
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,47 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+void mtk_simple_isr(struct mtk_base_afe *afe,
+		    struct mtk_base_afe_memif *memif);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 0000000..00fec10
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+			 (struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	const struct mtk_base_memif_data *memif_data = memif->data;
+	struct regmap *regmap = afe->regmap;
+	struct device *dev = afe->dev;
+	int reg_ofs_base = memif_data->reg_ofs_base;
+	int reg_ofs_cur = memif_data->reg_ofs_cur;
+	unsigned int hw_ptr = 0, hw_base = 0;
+	int ret, pcm_ptr_bytes;
+
+	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+	if (ret || hw_ptr == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+	if (ret || hw_base == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+	.ioctl = snd_pcm_lib_ioctl,
+	.pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	size_t size;
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+	size = afe->mtk_afe_hardware->buffer_bytes_max;
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+	.ops = &mtk_afe_pcm_ops,
+	.pcm_new = mtk_afe_pcm_new,
+	.pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 0000000..a973fc9
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 0000000..4169de0
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,103 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_reg;
+	int fs_shift;
+	int fs_maskbit;
+	int mono_reg;
+	int mono_shift;
+	int enable_reg;
+	int enable_shift;
+	int hd_reg;
+	int hd_shift;
+	int msb_reg;
+	int msb_shift;
+	int agent_disable_reg;
+	int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+	int id;
+	int irq_cnt_reg;
+	int irq_cnt_shift;
+	int irq_cnt_maskbit;
+	int irq_fs_reg;
+	int irq_fs_shift;
+	int irq_fs_maskbit;
+	int irq_en_reg;
+	int irq_en_shift;
+	int irq_clr_reg;
+	int irq_clr_shift;
+};
+
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct device;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+	void __iomem *base_addr;
+	struct device *dev;
+	struct regmap *regmap;
+
+	unsigned int const *reg_back_up_list;
+	unsigned int *reg_back_up;
+	unsigned int reg_back_up_list_num;
+
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	bool suspended;
+
+	struct mtk_base_afe_memif *memif;
+	int memif_size;
+	struct mtk_base_afe_irq *irqs;
+	int irqs_size;
+
+	const struct snd_pcm_hardware *mtk_afe_hardware;
+	int (*memif_fs)(struct snd_pcm_substream *substream,
+			unsigned int rate);
+	int (*irq_fs)(struct snd_pcm_substream *substream,
+		      unsigned int rate);
+
+	void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mtk_base_memif_data *data;
+	int irq_usage;
+	int const_irq;
+};
+
+struct mtk_base_afe_irq {
+	const struct mtk_base_irq_data *irq_data;
+	int irq_occupyed;
+};
+
+#endif
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:18 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:18 +0800
Subject: [alsa-devel] [PATCH v2 3/9] ASoC: mediatek: let mt8173 use mediatek
 common structure
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-4-git-send-email-garlic.tseng@mediatek.com>

Modify mt8173 driver implementation to use common structure.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                    |  11 +
 sound/soc/mediatek/Makefile                   |   3 +-
 sound/soc/mediatek/common/Makefile            |  17 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
 sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 681 +++++++++++---------------
 5 files changed, 328 insertions(+), 426 deletions(-)
 create mode 100644 sound/soc/mediatek/common/Makefile

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ae9f664..ff1a419 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,16 @@
+config SND_SOC_MEDIATEK
+	tristate "SND_SOC_MEDIATEK"
+	depends on ARCH_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
 	help
 	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
@@ -49,3 +59,4 @@ config SND_SOC_MT8173_RT5650_RT5676
 	  with the RT5650 and RT5676 codecs.
 	  Select Y if you have such device.
 	  If unsure select "N".
+
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 240dfc70..3d893be 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,3 @@
-# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
+
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 0000000..0fcd921
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
+
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
index 8f2936d..9a4837c 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -46,14 +46,13 @@ enum {
 };
 
 enum {
-	MT8173_AFE_IRQ_1,
-	MT8173_AFE_IRQ_2,
-	MT8173_AFE_IRQ_3,
-	MT8173_AFE_IRQ_4,
-	MT8173_AFE_IRQ_5,
-	MT8173_AFE_IRQ_6,
-	MT8173_AFE_IRQ_7,
-	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_DL1,
+	MT8173_AFE_IRQ_DL2,
+	MT8173_AFE_IRQ_VUL,
+	MT8173_AFE_IRQ_DAI,
+	MT8173_AFE_IRQ_AWB,
+	MT8173_AFE_IRQ_MOD_DAI,
+	MT8173_AFE_IRQ_HDMI,
 	MT8173_AFE_IRQ_NUM,
 };
 
@@ -71,31 +70,4 @@ enum {
 	MT8173_CLK_NUM
 };
 
-struct mt8173_afe;
-struct snd_pcm_substream;
-
-struct mt8173_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mt8173_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mt8173_afe_memif_data *data;
-	const struct mt8173_afe_irq_data *irqdata;
-};
-
 #endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 4fc52bc..d58bac4b 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -25,6 +25,9 @@
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1		0x0548
 #define AFE_TDM_CON2		0x054c
 
-#define AFE_BASE_END_OFFSET	8
 #define AFE_IRQ_STATUS_BITS	0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -152,15 +154,8 @@ static const unsigned int mt8173_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mt8173_afe {
-	/* address for ioremap audio hardware register */
-	void __iomem *base_addr;
-	struct device *dev;
-	struct regmap *regmap;
-	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+struct mt8173_afe_private {
 	struct clk *clocks[MT8173_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
-	bool suspended;
 };
 
 static const struct snd_pcm_hardware mt8173_afe_hardware = {
@@ -174,53 +169,6 @@ static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mt8173_afe_pcm_pointer
-			 (struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int hw_ptr;
-	int ret;
-
-	ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-	if (ret || hw_ptr == 0) {
-		dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-		hw_ptr = memif->phys_buf_addr;
-	}
-
-	return bytes_to_frames(substream->runtime,
-			       hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
-	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mt8173_afe_pcm_pointer,
-};
-
-static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-	size_t size;
-	struct snd_card *card = rtd->card->snd_card;
-	struct snd_pcm *pcm = rtd->pcm;
-
-	size = mt8173_afe_hardware.buffer_bytes_max;
-
-	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-						     card->dev, size, size);
-}
-
-static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
-{
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
-	.ops = &mt8173_afe_pcm_ops,
-	.pcm_new = mt8173_afe_pcm_new,
-	.pcm_free = mt8173_afe_pcm_free,
-};
-
 struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
@@ -253,7 +201,7 @@ static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 	return -EINVAL;
 }
 
-static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
 	unsigned int val;
 	int fs = mt8173_afe_i2s_fs(rate);
@@ -281,7 +229,7 @@ static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,7 +244,7 @@ static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
 				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
@@ -319,7 +267,7 @@ static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
 				    struct clk *m_ck, unsigned int mck_rate,
 				    struct clk *b_ck, unsigned int bck_rate)
 {
@@ -343,7 +291,7 @@ static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
 					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
@@ -356,7 +304,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
 				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -370,7 +318,7 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
@@ -386,12 +334,13 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
 				 runtime->rate * 256, NULL, 0);
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
 				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
 	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
@@ -407,13 +356,14 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return 0;
 
-	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				    afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
@@ -421,13 +371,14 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
 				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return;
 
-	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				     afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				     afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
 static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
@@ -435,12 +386,14 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
 	unsigned int val;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
 				 runtime->rate * 128,
-				 afe->clocks[MT8173_CLK_I2S3_B],
+				 afe_priv->clocks[MT8173_CLK_I2S3_B],
 				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
@@ -496,7 +449,7 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -508,10 +461,14 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
 		regmap_write(afe->regmap, AFE_HDMI_CONN0,
-			     AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-			     AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-			     AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-			     AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+				 AFE_HDMI_CONN0_O30_I30 |
+				 AFE_HDMI_CONN0_O31_I31 |
+				 AFE_HDMI_CONN0_O32_I34 |
+				 AFE_HDMI_CONN0_O33_I35 |
+				 AFE_HDMI_CONN0_O34_I32 |
+				 AFE_HDMI_CONN0_O35_I33 |
+				 AFE_HDMI_CONN0_O36_I36 |
+				 AFE_HDMI_CONN0_O37_I37);
 
 		/* enable Out control */
 		regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -531,224 +488,46 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int ret;
-
-	memif->substream = substream;
-
-	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
-
-	/*
-	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-	 * smaller than period_size due to AFE's internal buffer.
-	 * This easily leads to overrun when avail_min is period_size.
-	 * One more period can hold the possible unread buffer.
-	 */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		ret = snd_pcm_hw_constraint_minmax(runtime,
-						   SNDRV_PCM_HW_PARAM_PERIODS,
-						   3,
-						   mt8173_afe_hardware.periods_max);
-		if (ret < 0) {
-			dev_err(afe->dev, "hw_constraint_minmax failed\n");
-			return ret;
-		}
-	}
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-	return ret;
-}
-
-static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				     struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-	memif->substream = NULL;
-}
-
-static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				     struct snd_pcm_hw_params *params,
-				     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int msb_at_bit33 = 0;
-	int ret;
-
-	dev_dbg(afe->dev,
-		"%s period = %u, rate= %u, channels=%u\n",
-		__func__, params_period_size(params), params_rate(params),
-		params_channels(params));
-
-	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-	if (ret < 0)
-		return ret;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int fs;
 
-	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-	memif->buffer_size = substream->runtime->dma_bytes;
-
-	/* start */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base, memif->phys_buf_addr);
-	/* end */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-		     memif->phys_buf_addr + memif->buffer_size - 1);
-
-	/* set MSB to 33-bit */
-	regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-			   1 << memif->data->msb_shift,
-			   msb_at_bit33 << memif->data->msb_shift);
-
-	/* set channel */
-	if (memif->data->mono_shift >= 0) {
-		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   1 << memif->data->mono_shift,
-				   mono << memif->data->mono_shift);
-	}
-
-	/* set rate */
-	if (memif->data->fs_shift < 0)
-		return 0;
 	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
 	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
-		unsigned int val;
-
-		switch (params_rate(params)) {
+		switch (rate) {
 		case 8000:
-			val = 0;
+			fs = 0;
 			break;
 		case 16000:
-			val = 1;
+			fs = 1;
 			break;
 		case 32000:
-			val = 2;
+			fs = 2;
 			break;
 		default:
 			return -EINVAL;
 		}
-
-		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-		else
-			regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-
 	} else {
-		int fs = mt8173_afe_i2s_fs(params_rate(params));
-
-		if (fs < 0)
-			return -EINVAL;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   0xf << memif->data->fs_shift,
-				   fs << memif->data->fs_shift);
+		fs = mt8173_afe_i2s_fs(rate);
 	}
-
-	return 0;
+	return fs;
 }
 
-static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-	return snd_pcm_lib_free_pages(substream);
+	return mt8173_afe_i2s_fs(rate);
 }
 
-static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int counter = runtime->period_size;
-
-	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift,
-					   1 << memif->data->enable_shift);
-
-		/* set irq counter */
-		regmap_update_bits(afe->regmap,
-				   memif->data->irq_reg_cnt,
-				   0x3ffff << memif->data->irq_cnt_shift,
-				   counter << memif->data->irq_cnt_shift);
-
-		/* set irq fs */
-		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mt8173_afe_i2s_fs(runtime->rate);
-
-			if (fs < 0)
-				return -EINVAL;
-
-			regmap_update_bits(afe->regmap,
-					   AFE_IRQ_MCU_CON,
-					   0xf << memif->data->irq_fs_shift,
-					   fs << memif->data->irq_fs_shift);
-		}
-		/* enable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   1 << memif->data->irq_en_shift);
-
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift, 0);
-		/* disable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   0 << memif->data->irq_en_shift);
-		/* and clear pending IRQ */
-		regmap_write(afe->regmap, AFE_IRQ_CLR,
-			     1 << memif->data->irq_clr_shift);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
-	.startup	= mt8173_afe_dais_startup,
-	.shutdown	= mt8173_afe_dais_shutdown,
-	.hw_params	= mt8173_afe_dais_hw_params,
-	.hw_free	= mt8173_afe_dais_hw_free,
-	.trigger	= mt8173_afe_dais_trigger,
-};
-
 /* BE DAIs */
 static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
 	.startup	= mt8173_afe_i2s_startup,
@@ -761,56 +540,15 @@ static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
 	.shutdown	= mt8173_afe_hdmi_shutdown,
 	.prepare	= mt8173_afe_hdmi_prepare,
 	.trigger	= mt8173_afe_hdmi_trigger,
-
 };
 
-static int mt8173_afe_runtime_suspend(struct device *dev);
-static int mt8173_afe_runtime_resume(struct device *dev);
-
-static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
-			    &afe->backup_regs[i]);
-
-	afe->suspended = true;
-	mt8173_afe_runtime_suspend(afe->dev);
-	return 0;
-}
-
-static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i = 0;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-		return 0;
-
-	mt8173_afe_runtime_resume(afe->dev);
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
-			     afe->backup_regs[i]);
-
-	afe->suspended = false;
-	return 0;
-}
-
 static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
 		.id = MT8173_AFE_MEMIF_DL1,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -818,12 +556,12 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
 		.id = MT8173_AFE_MEMIF_VUL,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -831,7 +569,7 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
@@ -860,8 +598,8 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	{
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -872,7 +610,7 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
@@ -978,105 +716,222 @@ static const char *aud_clks[MT8173_CLK_NUM] = {
 	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
 		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 21,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 1,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 0,
-		.irq_fs_shift = 4,
-		.irq_clr_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 0,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DL2",
 		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 22,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 2,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 2,
-		.irq_fs_shift = 16,
-		.irq_clr_shift = 2,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "VUL",
 		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 27,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 3,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 1,
-		.irq_fs_shift = 8,
-		.irq_clr_shift = 1,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 6,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DAI",
 		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON0,
 		.fs_shift = 24,
+		.fs_maskbit = 0x3,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 4,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 5,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "AWB",
 		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 24,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 6,
-		.irq_reg_cnt = AFE_IRQ_CNT7,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 14,
-		.irq_fs_shift = 24,
-		.irq_clr_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 3,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "MOD_DAI",
 		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 30,
+		.fs_maskbit = 0x3,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 30,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 7,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 4,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
 		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = -1,
 		.enable_shift = -1,
-		.irq_reg_cnt = AFE_IRQ_CNT5,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 8,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+	{
+		.id = MT8173_AFE_IRQ_DL1,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8173_AFE_IRQ_DL2,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 2,
+
+	}, {
+		.id = MT8173_AFE_IRQ_VUL,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_AWB,
+		.irq_cnt_reg = AFE_IRQ_CNT7,
 		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_HDMI,
+		.irq_cnt_reg = AFE_IRQ_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
 		.irq_en_shift = 12,
+		.irq_fs_reg = -1,
 		.irq_fs_shift = -1,
+		.irq_fs_maskbit = -1,
+		.irq_clr_reg = AFE_IRQ_CLR,
 		.irq_clr_shift = 4,
-		.msb_shift = 8,
 	},
 };
 
@@ -1090,7 +945,7 @@ static const struct regmap_config mt8173_afe_regmap_config = {
 
 static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mt8173_afe *afe = dev_id;
+	struct mtk_base_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1102,24 +957,32 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 	}
 
 	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
-		struct mt8173_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *irq;
 
-		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+		if (memif->irq_usage < 0)
 			continue;
 
-		snd_pcm_period_elapsed(memif->substream);
+		irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
+			continue;
+
+		mtk_simple_isr(afe, memif);
 	}
 
 err_irq:
 	/* clear irq */
-	regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+	regmap_write(afe->regmap, AFE_IRQ_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
 
 	return IRQ_HANDLED;
 }
 
 static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1127,44 +990,46 @@ static int mt8173_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
 static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1181,35 +1046,37 @@ static int mt8173_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
+
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
 	size_t i;
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-		afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-		if (IS_ERR(afe->clocks[i])) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(afe_priv->clocks[i])) {
 			dev_err(afe->dev, "%s devm_clk_get %s fail\n",
 				__func__, aud_clks[i]);
-			return PTR_ERR(afe->clocks[i]);
+			return PTR_ERR(afe_priv->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
@@ -1217,7 +1084,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mt8173_afe *afe;
+	struct mtk_base_afe *afe;
+	struct mt8173_afe_private *afe_priv;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1228,6 +1096,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 	if (!afe)
 		return -ENOMEM;
 
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe_priv)
+		return -ENOMEM;
+
 	afe->dev = &pdev->dev;
 
 	irq_id = platform_get_irq(pdev, 0);
@@ -1259,8 +1133,30 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
+	/*memif % irq initialize*/
+	afe->memif_size = MT8173_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8173_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++) {
 		afe->memif[i].data = &memif_data[i];
+		afe->irqs[i].irq_data = &irq_data[i];
+		afe->irqs[i].irq_occupyed = true;
+		afe->memif[i].irq_usage = i;
+		afe->memif[i].const_irq = 1;
+	}
+
+	afe->mtk_afe_hardware = &mt8173_afe_hardware;
+	afe->memif_fs = mt8173_memif_fs;
+	afe->irq_fs = mt8173_irq_fs;
 
 	platform_set_drvdata(pdev, afe);
 
@@ -1271,7 +1167,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
+	afe->reg_back_up_list = mt8173_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+	afe->runtime_resume = mt8173_afe_runtime_resume;
+	afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:20 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:20 +0800
Subject: [alsa-devel] [PATCH v2 5/9] ASoC: mediatek: add structure define and
 clock control for 2701
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-6-git-send-email-garlic.tseng@mediatek.com>

add structure define and clock control function for 2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c | 357 ++++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h |  33 ++
 sound/soc/mediatek/mt2701/mt2701-afe-common.h     | 185 +++++++++++
 sound/soc/mediatek/mt2701/mt2701-reg.h            | 186 +++++++++++
 4 files changed, 761 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 0000000..479cd5a
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,357 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+	[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+	[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+	[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+	[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+	[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+	[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+	[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+	[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+	[MT2701_AUD_APLL_SEL] = "top_apll_sel",
+	[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+	[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+	[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+	[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+	[MT2701_AUD_AUDPLL] = "top_audpll",
+	[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+	[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+	[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+	[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+	[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+	[MT2701_AUD_CLK_26M] = "clk_26m",
+	[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+	[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+	[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+	[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+	[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+	[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+	[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+	[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+	[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+	[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+	[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+	[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+	[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+	[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+	[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+	[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+	[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+	[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+	[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+	[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+	[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+	[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+	[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+	[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+void mt2701_init_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i = 0;
+
+	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(aud_clks[i]))
+			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+				 __func__, aud_clks[i]);
+	}
+}
+
+void mt2701_afe_enable_clock(struct mtk_base_afe *afe, int en)
+{
+	if (en) {
+		mt2701_turn_on_a1sys_clock(afe);
+		mt2701_turn_on_a2sys_clock(afe);
+		mt2701_turn_on_afe_clock(afe);
+		regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+				   AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+				   AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+		regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+				   AFE_DAC_CON0_AFE_ON,
+				   AFE_DAC_CON0_AFE_ON);
+		regmap_write(afe->regmap, PWR2_TOP_CON,
+			     PWR2_TOP_CON_INIT_VAL);
+		regmap_write(afe->regmap, PWR1_ASM_CON1,
+			     PWR1_ASM_CON1_INIT_VAL);
+		regmap_write(afe->regmap, PWR2_ASM_CON1,
+			     PWR2_ASM_CON1_INIT_VAL);
+	} else {
+		mt2701_turn_off_afe_clock(afe);
+		mt2701_turn_off_a1sys_clock(afe);
+		mt2701_turn_off_a2sys_clock(afe);
+		regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+				   AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+		regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+				   AFE_DAC_CON0_AFE_ON, 0);
+	}
+}
+
+void mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+			aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			ret);
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+			   MT2701_AUD_AUD_MUX1_DIV_RATE);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+void mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			ret);
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			ret);
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+			   MT2701_AUD_AUD_MUX2_DIV_RATE);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+void mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	/*MT_CG_INFRA_AUDIO, INFRA_PDN_STA[5]*/
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+
+	/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUDINTBUS], ret);
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+			     afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUDINTBUS],
+			aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+
+	/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2*/
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+
+	/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4*/
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_M_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK,
+			   AUDIO_TOP_CON0_PDN_APLL_CK);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS,
+			   AUDIO_TOP_CON4_PDN_A1SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS,
+			   AUDIO_TOP_CON4_PDN_A2SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+	int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+	int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+	/* Set MCLK Kx_SRC_SEL(domain) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_clk_id], ret);
+
+	if (domain == 0) {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+	} else {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+	}
+	clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+	/* Set MCLK Kx_SRC_DIV(divider) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_div_id], ret);
+
+	ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+			aud_clks[aud_src_div_id], mclk, ret);
+	clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 0000000..0705f62
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,33 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+void mt2701_init_clock(struct mtk_base_afe *afe);
+void mt2701_afe_enable_clock(struct mtk_base_afe *afe, int en);
+void mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 0000000..af29367
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,185 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *             Koro Chen <koro.chen at mediatek.com>
+ *             Sascha Hauer <s.hauer at pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE	98304000
+#define MT2701_PLL_DOMAIN_1_RATE	90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+	MT2701_I2S_1,
+	MT2701_I2S_2,
+	MT2701_I2S_3,
+	MT2701_I2S_4,
+	MT2701_I2S_NUM,
+};
+
+enum {
+	MT2701_MEMIF_DL1,
+	MT2701_MEMIF_DL2,
+	MT2701_MEMIF_DL3,
+	MT2701_MEMIF_DL4,
+	MT2701_MEMIF_DL5,
+	MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_UL1,
+	MT2701_MEMIF_UL2,
+	MT2701_MEMIF_UL3,
+	MT2701_MEMIF_UL4,
+	MT2701_MEMIF_UL5,
+	MT2701_MEMIF_DLBT,
+	MT2701_MEMIF_ULBT,
+	MT2701_MEMIF_NUM,
+	MT2701_IO_I2S = MT2701_MEMIF_NUM,
+	MT2701_IO_2ND_I2S,
+	MT2701_IO_3RD_I2S,
+	MT2701_IO_4TH_I2S,
+	MT2701_IO_5TH_I2S,
+	MT2701_IO_6TH_I2S,
+	MT2701_IO_MRG,
+};
+
+enum {
+	MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ2,
+	MT2701_IRQ_ASYS_IRQ3,
+	MT2701_IRQ_ASYS_END,
+};
+
+enum {
+	DIV_ID_MCLK_TO_BCK,
+	DIV_ID_BCK_TO_LRCK,
+};
+
+/*2701 clock def*/
+enum audio_system_clock_type {
+	MT2701_AUD_INFRA_SYS_AUDIO,
+	MT2701_AUD_AUD_MUX1_SEL,
+	MT2701_AUD_AUD_MUX2_SEL,
+	MT2701_AUD_AUD_MUX1_DIV,
+	MT2701_AUD_AUD_MUX2_DIV,
+	MT2701_AUD_AUD_48K_TIMING,
+	MT2701_AUD_AUD_44K_TIMING,
+	MT2701_AUD_AUDPLL_MUX_SEL,
+	MT2701_AUD_APLL_SEL,
+	MT2701_AUD_AUD1PLL_98M,
+	MT2701_AUD_AUD2PLL_90M,
+	MT2701_AUD_HADDS2PLL_98M,
+	MT2701_AUD_HADDS2PLL_294M,
+	MT2701_AUD_AUDPLL,
+	MT2701_AUD_AUDPLL_D4,
+	MT2701_AUD_AUDPLL_D8,
+	MT2701_AUD_AUDPLL_D16,
+	MT2701_AUD_AUDPLL_D24,
+	MT2701_AUD_AUDINTBUS,
+	MT2701_AUD_CLK_26M,
+	MT2701_AUD_SYSPLL1_D4,
+	MT2701_AUD_AUD_K1_SRC_SEL,
+	MT2701_AUD_AUD_K2_SRC_SEL,
+	MT2701_AUD_AUD_K3_SRC_SEL,
+	MT2701_AUD_AUD_K4_SRC_SEL,
+	MT2701_AUD_AUD_K5_SRC_SEL,
+	MT2701_AUD_AUD_K6_SRC_SEL,
+	MT2701_AUD_AUD_K1_SRC_DIV,
+	MT2701_AUD_AUD_K2_SRC_DIV,
+	MT2701_AUD_AUD_K3_SRC_DIV,
+	MT2701_AUD_AUD_K4_SRC_DIV,
+	MT2701_AUD_AUD_K5_SRC_DIV,
+	MT2701_AUD_AUD_K6_SRC_DIV,
+	MT2701_AUD_AUD_I2S1_MCLK,
+	MT2701_AUD_AUD_I2S2_MCLK,
+	MT2701_AUD_AUD_I2S3_MCLK,
+	MT2701_AUD_AUD_I2S4_MCLK,
+	MT2701_AUD_AUD_I2S5_MCLK,
+	MT2701_AUD_AUD_I2S6_MCLK,
+	MT2701_AUD_ASM_M_SEL,
+	MT2701_AUD_ASM_H_SEL,
+	MT2701_AUD_UNIVPLL2_D4,
+	MT2701_AUD_UNIVPLL2_D2,
+	MT2701_AUD_SYSPLL_D5,
+	MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AUDIO_TOP_CON4,
+	AUDIO_TOP_CON5,
+	ASYS_TOP_CON,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN2,
+	AFE_CONN3,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN22,
+	AFE_DAC_CON0,
+	AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+	int i2s_ctrl_reg;
+	int i2s_pwn_shift;
+	int i2s_asrc_fs_shift;
+	int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+	I2S_OUT,
+	I2S_IN,
+	I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+	int dai_id;
+	int mclk_rate;
+	int div_mclk_to_bck;
+	int div_bck_to_lrck;
+	int format;
+	snd_pcm_format_t stream_fmt;
+	int on[I2S_DIR_NUM];
+	int occupied[I2S_DIR_NUM];
+	const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+	struct clk *clocks[MT2701_CLOCK_NUM];
+	struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+	bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 0000000..9db7d02
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/*AUDIO_TOP_CON0 (0x0000)*/
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON	(0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE		(0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK	(0x1 << 23)
+
+/*AUDIO_TOP_CON4 (0x0010)*/
+#define AUDIO_TOP_CON4_I2SO1_PWN	(0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS	(0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS	(0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN	(0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF	(0x1 << 25)
+
+/*AFE_DAIBT_CON0 (0x001c)*/
+#define AFE_DAIBT_CON0_DAIBT_EN		(0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN	(0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY	(0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN	(0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE		(0x1 << 12)
+
+/*PWR1_ASM_CON1 (0x0108)*/
+#define PWR1_ASM_CON1_INIT_VAL		(0x492)
+
+/*AFE_MRGIF_CON (0x003c)*/
+#define AFE_MRGIF_CON_MRG_EN		(0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN	(0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK	(0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K	(0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c)*/
+#define ASYS_I2SO1_CON_FS		(0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)	((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH		(0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN		(0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN		(0x1 << 0)
+/*0:EIAJ 1:I2S*/
+#define ASYS_I2SO1_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE	(0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)	((x) << 1)
+
+/*PWR2_TOP_CON (0x0634)*/
+#define PWR2_TOP_CON_INIT_VAL		(0xffe1ffff)
+
+/*ASYS_IRQ_CLR (0x07c0)*/
+#define ASYS_IRQ_CLR_ALL		(0xffffffff)
+
+/*PWR2_ASM_CON1 (0x1070)*/
+#define PWR2_ASM_CON1_INIT_VAL		(0x492492)
+
+/*AFE_DAC_CON0 (0x1200)*/
+#define AFE_DAC_CON0_AFE_ON		(0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238)*/
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK		(0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE	(0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE	(0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK			(0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK		(0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)		((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK	(0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES		(0x1 << 12)
+
+/*I2S in/out register bit control*/
+#define ASYS_I2S_CON_FS			(0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)		((x) << 8)
+#define ASYS_I2S_CON_RESET		(0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN		(0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE	(0x1 << 17)
+/*0:EIAJ 1:I2S*/
+#define ASYS_I2S_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE		(0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)	((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX		(0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:16 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:16 +0800
Subject: [alsa-devel] [PATCH v2 1/9] ASoC: mediatek: Refine mt8173 driver and
 change config option
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-2-git-send-email-garlic.tseng@mediatek.com>

move mt8173 driver to another folder and add prefix.
add config option SND_SOC_MT8173

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                         |  14 +-
 sound/soc/mediatek/Makefile                        |   9 +-
 sound/soc/mediatek/mt8173/Makefile                 |   7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      | 101 +++++
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     | 494 ++++++++++-----------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |   4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |   2 +-
 sound/soc/mediatek/mtk-afe-common.h                | 101 -----
 10 files changed, 367 insertions(+), 369 deletions(-)
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (66%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3abf51c..ae9f664 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,15 @@
-config SND_SOC_MEDIATEK
-	tristate "ASoC support for Mediatek chip"
+config SND_SOC_MT8173
+	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
 	help
-	  This adds ASoC platform driver support for Mediatek chip
+	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
 	  Select Y if you have such device.
 	  Ex: MT8173
 
 config SND_SOC_MT8173_MAX98090
 	tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_MAX98090
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -19,7 +19,7 @@ config SND_SOC_MT8173_MAX98090
 
 config SND_SOC_MT8173_RT5650
 	tristate "ASoC Audio driver for MT8173 with RT5650 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -29,7 +29,7 @@ config SND_SOC_MT8173_RT5650
 
 config SND_SOC_MT8173_RT5650_RT5514
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5514
 	help
@@ -40,7 +40,7 @@ config SND_SOC_MT8173_RT5650_RT5514
 
 config SND_SOC_MT8173_RT5650_RT5676
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5677
 	select SND_SOC_HDMI_CODEC
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index d486860..240dfc70 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,7 +1,2 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 0000000..0357b27
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,7 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644
index 0000000..8f2936d
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,101 @@
+/*
+ * mt8173_afe_common.h  --  Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen at mediatek.com>
+ *             Sascha Hauer <s.hauer at pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+	MT8173_AFE_MEMIF_DL1,
+	MT8173_AFE_MEMIF_DL2,
+	MT8173_AFE_MEMIF_VUL,
+	MT8173_AFE_MEMIF_DAI,
+	MT8173_AFE_MEMIF_AWB,
+	MT8173_AFE_MEMIF_MOD_DAI,
+	MT8173_AFE_MEMIF_HDMI,
+	MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM2,
+	MT8173_AFE_IO_PMIC,
+	MT8173_AFE_IO_I2S,
+	MT8173_AFE_IO_2ND_I2S,
+	MT8173_AFE_IO_HW_GAIN1,
+	MT8173_AFE_IO_HW_GAIN2,
+	MT8173_AFE_IO_MRG_O,
+	MT8173_AFE_IO_MRG_I,
+	MT8173_AFE_IO_DAIBT,
+	MT8173_AFE_IO_HDMI,
+};
+
+enum {
+	MT8173_AFE_IRQ_1,
+	MT8173_AFE_IRQ_2,
+	MT8173_AFE_IRQ_3,
+	MT8173_AFE_IRQ_4,
+	MT8173_AFE_IRQ_5,
+	MT8173_AFE_IRQ_6,
+	MT8173_AFE_IRQ_7,
+	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+	MT8173_CLK_INFRASYS_AUD,
+	MT8173_CLK_TOP_PDN_AUD,
+	MT8173_CLK_TOP_PDN_AUD_BUS,
+	MT8173_CLK_I2S0_M,
+	MT8173_CLK_I2S1_M,
+	MT8173_CLK_I2S2_M,
+	MT8173_CLK_I2S3_M,
+	MT8173_CLK_I2S3_B,
+	MT8173_CLK_BCK0,
+	MT8173_CLK_BCK1,
+	MT8173_CLK_NUM
+};
+
+struct mt8173_afe;
+struct snd_pcm_substream;
+
+struct mt8173_afe_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_shift;
+	int mono_shift;
+	int enable_shift;
+	int irq_reg_cnt;
+	int irq_cnt_shift;
+	int irq_en_shift;
+	int irq_fs_shift;
+	int irq_clr_shift;
+	int msb_shift;
+};
+
+struct mt8173_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mt8173_afe_memif_data *data;
+	const struct mt8173_afe_irq_data *irqdata;
+};
+
+#endif
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
similarity index 66%
rename from sound/soc/mediatek/mtk-afe-pcm.c
rename to sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 793d7e2..4fc52bc 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1,5 +1,5 @@
 /*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
  *
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Koro Chen <koro.chen at mediatek.com>
@@ -24,7 +24,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
-#include "mtk-afe-common.h"
+#include "mt8173-afe-common.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -135,7 +135,7 @@ enum afe_tdm_ch_start {
 	AFE_TDM_CH_ZERO,
 };
 
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
 	AUDIO_TOP_CON0,
 	AFE_CONN1,
 	AFE_CONN2,
@@ -152,18 +152,18 @@ static const unsigned int mtk_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mtk_afe {
+struct mt8173_afe {
 	/* address for ioremap audio hardware register */
 	void __iomem *base_addr;
 	struct device *dev;
 	struct regmap *regmap;
-	struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
-	struct clk *clocks[MTK_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
+	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+	struct clk *clocks[MT8173_CLK_NUM];
+	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
 	bool suspended;
 };
 
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 		 SNDRV_PCM_INFO_MMAP_VALID),
 	.buffer_bytes_max = 256 * 1024,
@@ -174,12 +174,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
+static snd_pcm_uframes_t mt8173_afe_pcm_pointer
 			 (struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int hw_ptr;
 	int ret;
 
@@ -193,40 +193,40 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
 			       hw_ptr - memif->phys_buf_addr);
 }
 
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
 	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mtk_afe_pcm_pointer,
+	.pointer = mt8173_afe_pcm_pointer,
 };
 
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	size_t size;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_pcm *pcm = rtd->pcm;
 
-	size = mtk_afe_hardware.buffer_bytes_max;
+	size = mt8173_afe_hardware.buffer_bytes_max;
 
 	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						     card->dev, size, size);
 }
 
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
 {
 	snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
-	.ops = &mtk_afe_pcm_ops,
-	.pcm_new = mtk_afe_pcm_new,
-	.pcm_free = mtk_afe_pcm_free,
+static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
+	.ops = &mt8173_afe_pcm_ops,
+	.pcm_new = mt8173_afe_pcm_new,
+	.pcm_free = mt8173_afe_pcm_free,
 };
 
-struct mtk_afe_rate {
+struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
 };
 
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
 	{ .rate = 8000, .regvalue = 0 },
 	{ .rate = 11025, .regvalue = 1 },
 	{ .rate = 12000, .regvalue = 2 },
@@ -242,21 +242,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
 	{ .rate = 192000, .regvalue = 14 },
 };
 
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
-		if (mtk_afe_i2s_rates[i].rate == sample_rate)
-			return mtk_afe_i2s_rates[i].regvalue;
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+		if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+			return mt8173_afe_i2s_rates[i].regvalue;
 
 	return -EINVAL;
 }
 
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 {
 	unsigned int val;
-	int fs = mtk_afe_i2s_fs(rate);
+	int fs = mt8173_afe_i2s_fs(rate);
 
 	if (fs < 0)
 		return -EINVAL;
@@ -281,7 +281,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,8 +296,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
-				    struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
 
@@ -319,9 +319,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
-				 struct clk *m_ck, unsigned int mck_rate,
-				 struct clk *b_ck, unsigned int bck_rate)
+static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+				    struct clk *m_ck, unsigned int mck_rate,
+				    struct clk *b_ck, unsigned int bck_rate)
 {
 	int ret;
 
@@ -343,8 +343,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
-				      struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
 		clk_disable_unprepare(m_ck);
@@ -352,11 +352,11 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
 		clk_disable_unprepare(b_ck);
 }
 
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -366,84 +366,82 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
-				 struct snd_soc_dai *dai)
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_set_i2s_enable(afe, false);
+	mt8173_afe_set_i2s_enable(afe, false);
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
 }
 
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	int ret;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
-			      NULL, 0);
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
-			      NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+				 runtime->rate * 256, NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
-	ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
 	if (ret)
 		return ret;
 
-	mtk_afe_set_i2s_enable(afe, true);
+	mt8173_afe_set_i2s_enable(afe, true);
 
 	return 0;
 }
 
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
 
-	mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				 afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				    afe->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				  afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				     afe->clocks[MT8173_CLK_I2S3_B]);
 }
 
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	unsigned int val;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
-			      afe->clocks[MTK_CLK_I2S3_B],
-			      runtime->rate * runtime->channels * 32);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				 runtime->rate * 128,
+				 afe->clocks[MT8173_CLK_I2S3_B],
+				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
 	      AFE_TDM_CON1_LRCK_INV |
@@ -494,11 +492,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -540,18 +538,18 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 	}
 }
 
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int ret;
 
 	memif->substream = substream;
 
-	snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
 
 	/*
 	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
@@ -563,7 +561,7 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 		ret = snd_pcm_hw_constraint_minmax(runtime,
 						   SNDRV_PCM_HW_PARAM_PERIODS,
 						   3,
-						   mtk_afe_hardware.periods_max);
+						   mt8173_afe_hardware.periods_max);
 		if (ret < 0) {
 			dev_err(afe->dev, "hw_constraint_minmax failed\n");
 			return ret;
@@ -576,23 +574,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 
 	memif->substream = NULL;
 }
 
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params,
-				  struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int msb_at_bit33 = 0;
 	int ret;
 
@@ -634,8 +632,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	/* set rate */
 	if (memif->data->fs_shift < 0)
 		return 0;
-	if (memif->data->id == MTK_AFE_MEMIF_DAI ||
-	    memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
+	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
 		unsigned int val;
 
 		switch (params_rate(params)) {
@@ -652,7 +650,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 			return -EINVAL;
 		}
 
-		if (memif->data->id == MTK_AFE_MEMIF_DAI)
+		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
 			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
 					   0x3 << memif->data->fs_shift,
 					   val << memif->data->fs_shift);
@@ -662,7 +660,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 					   val << memif->data->fs_shift);
 
 	} else {
-		int fs = mtk_afe_i2s_fs(params_rate(params));
+		int fs = mt8173_afe_i2s_fs(params_rate(params));
 
 		if (fs < 0)
 			return -EINVAL;
@@ -675,19 +673,19 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	return snd_pcm_lib_free_pages(substream);
 }
 
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int counter = runtime->period_size;
 
 	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
@@ -708,7 +706,7 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set irq fs */
 		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mtk_afe_i2s_fs(runtime->rate);
+			int fs = mt8173_afe_i2s_fs(runtime->rate);
 
 			if (fs < 0)
 				return -EINVAL;
@@ -743,76 +741,76 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 }
 
 /* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
-	.startup	= mtk_afe_dais_startup,
-	.shutdown	= mtk_afe_dais_shutdown,
-	.hw_params	= mtk_afe_dais_hw_params,
-	.hw_free	= mtk_afe_dais_hw_free,
-	.trigger	= mtk_afe_dais_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
+	.startup	= mt8173_afe_dais_startup,
+	.shutdown	= mt8173_afe_dais_shutdown,
+	.hw_params	= mt8173_afe_dais_hw_params,
+	.hw_free	= mt8173_afe_dais_hw_free,
+	.trigger	= mt8173_afe_dais_trigger,
 };
 
 /* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
-	.startup	= mtk_afe_i2s_startup,
-	.shutdown	= mtk_afe_i2s_shutdown,
-	.prepare	= mtk_afe_i2s_prepare,
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+	.startup	= mt8173_afe_i2s_startup,
+	.shutdown	= mt8173_afe_i2s_shutdown,
+	.prepare	= mt8173_afe_i2s_prepare,
 };
 
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
-	.startup	= mtk_afe_hdmi_startup,
-	.shutdown	= mtk_afe_hdmi_shutdown,
-	.prepare	= mtk_afe_hdmi_prepare,
-	.trigger	= mtk_afe_hdmi_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+	.startup	= mt8173_afe_hdmi_startup,
+	.shutdown	= mt8173_afe_hdmi_shutdown,
+	.prepare	= mt8173_afe_hdmi_prepare,
+	.trigger	= mt8173_afe_hdmi_trigger,
 
 };
 
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
+static int mt8173_afe_runtime_suspend(struct device *dev);
+static int mt8173_afe_runtime_resume(struct device *dev);
 
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_read(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
 			    &afe->backup_regs[i]);
 
 	afe->suspended = true;
-	mtk_afe_runtime_suspend(afe->dev);
+	mt8173_afe_runtime_suspend(afe->dev);
 	return 0;
 }
 
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i = 0;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
 		return 0;
 
-	mtk_afe_runtime_resume(afe->dev);
+	mt8173_afe_runtime_resume(afe->dev);
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_write(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
 			     afe->backup_regs[i]);
 
 	afe->suspended = false;
 	return 0;
 }
 
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
-		.id = MTK_AFE_MEMIF_DL1,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_DL1,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -820,12 +818,12 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
-		.id = MTK_AFE_MEMIF_VUL,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_VUL,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -833,11 +831,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
-		.id = MTK_AFE_IO_I2S,
+		.id = MT8173_AFE_IO_I2S,
 		.playback = {
 			.stream_name = "I2S Playback",
 			.channels_min = 1,
@@ -852,18 +850,18 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_i2s_ops,
+		.ops = &mt8173_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
 };
 
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	/* FE DAIs */
 	{
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_HDMI,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -874,11 +872,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
-		.id = MTK_AFE_IO_HDMI,
+		.id = MT8173_AFE_IO_HDMI,
 		.playback = {
 			.stream_name = "HDMIO Playback",
 			.channels_min = 2,
@@ -889,29 +887,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_hdmi_ops,
+		.ops = &mt8173_afe_hdmi_ops,
 	},
 };
 
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
 	/* inter-connections */
 	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -921,16 +919,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
 	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+			   mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
 	SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+			   mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
 	SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+			   mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
 	SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+			   mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
 };
 
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
 	{"I05", NULL, "DL1"},
 	{"I06", NULL, "DL1"},
 	{"I2S Playback", NULL, "O03"},
@@ -949,41 +947,41 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
 	{ "O10", "I04 Switch", "I04" },
 };
 
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
 	{"HDMIO Playback", NULL, "HDMI"},
 };
 
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
-	.name = "mtk-afe-pcm-dai",
-	.dapm_widgets = mtk_afe_pcm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
-	.dapm_routes = mtk_afe_pcm_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+	.name = "mt8173-afe-pcm-dai",
+	.dapm_widgets = mt8173_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+	.dapm_routes = mt8173_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
 };
 
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
-	.name = "mtk-afe-hdmi-dai",
-	.dapm_routes = mtk_afe_hdmi_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+	.name = "mt8173-afe-hdmi-dai",
+	.dapm_routes = mt8173_afe_hdmi_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
 };
 
-static const char *aud_clks[MTK_CLK_NUM] = {
-	[MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
-	[MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
-	[MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
-	[MTK_CLK_I2S0_M] =  "i2s0_m",
-	[MTK_CLK_I2S1_M] =  "i2s1_m",
-	[MTK_CLK_I2S2_M] =  "i2s2_m",
-	[MTK_CLK_I2S3_M] =  "i2s3_m",
-	[MTK_CLK_I2S3_B] =  "i2s3_b",
-	[MTK_CLK_BCK0] =  "bck0",
-	[MTK_CLK_BCK1] =  "bck1",
+static const char *aud_clks[MT8173_CLK_NUM] = {
+	[MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+	[MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+	[MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+	[MT8173_CLK_I2S0_M] =  "i2s0_m",
+	[MT8173_CLK_I2S1_M] =  "i2s1_m",
+	[MT8173_CLK_I2S2_M] =  "i2s2_m",
+	[MT8173_CLK_I2S3_M] =  "i2s3_m",
+	[MT8173_CLK_I2S3_B] =  "i2s3_b",
+	[MT8173_CLK_BCK0] =  "bck0",
+	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
-		.id = MTK_AFE_MEMIF_DL1,
+		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
 		.fs_shift = 0,
@@ -997,7 +995,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 0,
 	}, {
 		.name = "DL2",
-		.id = MTK_AFE_MEMIF_DL2,
+		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
 		.fs_shift = 4,
@@ -1011,7 +1009,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 1,
 	}, {
 		.name = "VUL",
-		.id = MTK_AFE_MEMIF_VUL,
+		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
 		.fs_shift = 16,
@@ -1025,7 +1023,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 6,
 	}, {
 		.name = "DAI",
-		.id = MTK_AFE_MEMIF_DAI,
+		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
 		.fs_shift = 24,
@@ -1039,7 +1037,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 5,
 	}, {
 		.name = "AWB",
-		.id = MTK_AFE_MEMIF_AWB,
+		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
 		.fs_shift = 12,
@@ -1053,7 +1051,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 3,
 	}, {
 		.name = "MOD_DAI",
-		.id = MTK_AFE_MEMIF_MOD_DAI,
+		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
 		.fs_shift = 30,
@@ -1067,7 +1065,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 4,
 	}, {
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
+		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
 		.fs_shift = -1,
@@ -1082,7 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 	},
 };
 
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -1090,9 +1088,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
 	.cache_type = REGCACHE_NONE,
 };
 
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mtk_afe *afe = dev_id;
+	struct mt8173_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1103,8 +1101,8 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
 		goto err_irq;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
-		struct mtk_afe_memif *memif = &afe->memif[i];
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+		struct mt8173_afe_memif *memif = &afe->memif[i];
 
 		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
 			continue;
@@ -1119,9 +1117,9 @@ err_irq:
 	return IRQ_HANDLED;
 }
 
-static int mtk_afe_runtime_suspend(struct device *dev)
+static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1129,45 +1127,44 @@ static int mtk_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
-static int mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1184,23 +1181,22 @@ static int mtk_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
-
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
 {
 	size_t i;
 
@@ -1212,16 +1208,16 @@ static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
 			return PTR_ERR(afe->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mtk_afe *afe;
+	struct mt8173_afe *afe;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1239,7 +1235,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
 		return -ENXIO;
 	}
-	ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+	ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
 			       0, "Afe_ISR_Handle", (void *)afe);
 	if (ret) {
 		dev_err(afe->dev, "could not request_irq\n");
@@ -1252,48 +1248,48 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		return PTR_ERR(afe->base_addr);
 
 	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
-		&mtk_afe_regmap_config);
+		&mt8173_afe_regmap_config);
 	if (IS_ERR(afe->regmap))
 		return PTR_ERR(afe->regmap);
 
 	/* initial audio related clock */
-	ret = mtk_afe_init_audio_clk(afe);
+	ret = mt8173_afe_init_audio_clk(afe);
 	if (ret) {
-		dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+		dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
 		return ret;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
 		afe->memif[i].data = &memif_data[i];
 
 	platform_set_drvdata(pdev, afe);
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = mtk_afe_runtime_resume(&pdev->dev);
+		ret = mt8173_afe_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_pcm_dai_component,
-					 mtk_afe_pcm_dais,
-					 ARRAY_SIZE(mtk_afe_pcm_dais));
+					 &mt8173_afe_pcm_dai_component,
+					 mt8173_afe_pcm_dais,
+					 ARRAY_SIZE(mt8173_afe_pcm_dais));
 	if (ret)
 		goto err_platform;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_hdmi_dai_component,
-					 mtk_afe_hdmi_dais,
-					 ARRAY_SIZE(mtk_afe_hdmi_dais));
+					 &mt8173_afe_hdmi_dai_component,
+					 mt8173_afe_hdmi_dais,
+					 ARRAY_SIZE(mt8173_afe_hdmi_dais));
 	if (ret)
 		goto err_comp;
 
-	dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+	dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
 	return 0;
 
 err_comp:
@@ -1305,38 +1301,38 @@ err_pm_disable:
 	return ret;
 }
 
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		mtk_afe_runtime_suspend(&pdev->dev);
+		mt8173_afe_runtime_suspend(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 	snd_soc_unregister_platform(&pdev->dev);
 	return 0;
 }
 
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
 	{ .compatible = "mediatek,mt8173-afe-pcm", },
 	{ }
 };
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
 
-static const struct dev_pm_ops mtk_afe_pm_ops = {
-	SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
-			   NULL)
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+			   mt8173_afe_runtime_resume, NULL)
 };
 
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
 	.driver = {
-		   .name = "mtk-afe-pcm",
-		   .of_match_table = mtk_afe_pcm_dt_match,
-		   .pm = &mtk_afe_pm_ops,
+		   .name = "mt8173-afe-pcm",
+		   .of_match_table = mt8173_afe_pcm_dt_match,
+		   .pm = &mt8173_afe_pm_ops,
 	},
-	.probe = mtk_afe_pcm_dev_probe,
-	.remove = mtk_afe_pcm_dev_remove,
+	.probe = mt8173_afe_pcm_dev_probe,
+	.remove = mt8173_afe_pcm_dev_remove,
 };
 
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
 
 MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
 MODULE_AUTHOR("Koro Chen <koro.chen at mediatek.com>");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-max98090.c
rename to sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35..5524a2c 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -18,7 +18,7 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 static struct snd_soc_jack mt8173_max98090_jack;
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e0836..467f704 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb59392..1b8b2a7 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -19,8 +19,8 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 072934b..d478976 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index f341f62..0000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * mtk_afe_common.h  --  Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen at mediatek.com>
- *             Sascha Hauer <s.hauer at pengutronix.de>
- *             Hidalgo Huang <hidalgo.huang at mediatek.com>
- *             Ir Lian <ir.lian at mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
-	MTK_AFE_MEMIF_DL1,
-	MTK_AFE_MEMIF_DL2,
-	MTK_AFE_MEMIF_VUL,
-	MTK_AFE_MEMIF_DAI,
-	MTK_AFE_MEMIF_AWB,
-	MTK_AFE_MEMIF_MOD_DAI,
-	MTK_AFE_MEMIF_HDMI,
-	MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM2,
-	MTK_AFE_IO_PMIC,
-	MTK_AFE_IO_I2S,
-	MTK_AFE_IO_2ND_I2S,
-	MTK_AFE_IO_HW_GAIN1,
-	MTK_AFE_IO_HW_GAIN2,
-	MTK_AFE_IO_MRG_O,
-	MTK_AFE_IO_MRG_I,
-	MTK_AFE_IO_DAIBT,
-	MTK_AFE_IO_HDMI,
-};
-
-enum {
-	MTK_AFE_IRQ_1,
-	MTK_AFE_IRQ_2,
-	MTK_AFE_IRQ_3,
-	MTK_AFE_IRQ_4,
-	MTK_AFE_IRQ_5,
-	MTK_AFE_IRQ_6,
-	MTK_AFE_IRQ_7,
-	MTK_AFE_IRQ_8,
-	MTK_AFE_IRQ_NUM,
-};
-
-enum {
-	MTK_CLK_INFRASYS_AUD,
-	MTK_CLK_TOP_PDN_AUD,
-	MTK_CLK_TOP_PDN_AUD_BUS,
-	MTK_CLK_I2S0_M,
-	MTK_CLK_I2S1_M,
-	MTK_CLK_I2S2_M,
-	MTK_CLK_I2S3_M,
-	MTK_CLK_I2S3_B,
-	MTK_CLK_BCK0,
-	MTK_CLK_BCK1,
-	MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mtk_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mtk_afe_memif_data *data;
-	const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:21 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:21 +0800
Subject: [alsa-devel] [PATCH v2 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-7-git-send-email-garlic.tseng@mediatek.com>

Add mt2701 platform driver implementation for playback and capture.
The implement follow DAPM structure (memory interface as FE and I2S
as BE).
Because of the hardware design, i2s out required to be enabled when
we need to enable i2s in. This patch includes the implementation.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 1523 ++++++++++++++++++++++++++++
 1 file changed, 1523 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644
index 0000000..4c22ca0
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1523 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *             Koro Chen <koro.chen at mediatek.com>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS	0xff
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+		| SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+		   | SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 1024 * 256,
+	.periods_min = 4,
+	.periods_max = 1024,
+	.buffer_bytes_max = 1024 * 1024 * 16,
+	.fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+	unsigned int rate;
+	unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+	{ .rate = 8000, .regvalue = 0 },
+	{ .rate = 12000, .regvalue = 1 },
+	{ .rate = 16000, .regvalue = 2 },
+	{ .rate = 24000, .regvalue = 3 },
+	{ .rate = 32000, .regvalue = 4 },
+	{ .rate = 48000, .regvalue = 5 },
+	{ .rate = 96000, .regvalue = 6 },
+	{ .rate = 192000, .regvalue = 7 },
+	{ .rate = 384000, .regvalue = 8 },
+	{ .rate = 7350, .regvalue = 16 },
+	{ .rate = 11025, .regvalue = 17 },
+	{ .rate = 14700, .regvalue = 18 },
+	{ .rate = 22050, .regvalue = 19 },
+	{ .rate = 29400, .regvalue = 20 },
+	{ .rate = 44100, .regvalue = 21 },
+	{ .rate = 88200, .regvalue = 22 },
+	{ .rate = 176400, .regvalue = 23 },
+	{ .rate = 352800, .regvalue = 24 },
+};
+
+int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+	int val = num - MT2701_IO_I2S;
+
+	if (val < 0 || val > MT2701_I2S_NUM) {
+		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+			__func__, num, val);
+		return -1;
+	}
+	return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+		if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+			return mt2701_afe_i2s_rates[i].regvalue;
+
+	return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+	int ret = 0;
+
+	/*enable mclk*/
+	ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+	if (ret)
+		dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+			i2s_num);
+
+	return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai,
+					int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
+	const struct mt2701_i2s_data *i2s_data;
+	int stream_dir = substream->stream;
+
+	if (dir_invert)	{
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	i2s_path->on[stream_dir]--;
+	if (i2s_path->on[stream_dir] < 0) {
+		dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+			 i2s_path->on[stream_dir], stream_dir);
+		i2s_path->on[stream_dir] = 0;
+	}
+	if (i2s_path->on[stream_dir])
+		return 0;
+
+	/*disable i2s*/
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   1 << i2s_data->i2s_pwn_shift);
+	return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+	if (i2s_path->occupied[substream->stream])
+		i2s_path->occupied[substream->stream] = 0;
+	else
+		goto I2S_UNSTART;
+
+	mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+	/*need to disable i2s-out path when disable i2s-in*/
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+	/*disable mclk*/
+	clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+					  struct snd_soc_dai *dai,
+					  int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
+	const struct mt2701_i2s_data *i2s_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	int reg, fs, w_len = 1;
+	int stream_dir = substream->stream;
+	unsigned int mask = 0, val = 0;
+
+	if (dir_invert) {
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	/*no need to enable if already done*/
+	i2s_path->on[stream_dir]++;
+
+	if (i2s_path->on[stream_dir] != 1)
+		return 0;
+
+	fs = mt2701_afe_i2s_fs(runtime->rate);
+
+	if (i2s_path->div_bck_to_lrck == 32)
+		w_len = 0;
+	else if (i2s_path->div_bck_to_lrck == 64)
+		w_len = 1;
+	else
+		dev_warn(dai->dev, "%s() bad bit count %d\n", __func__,
+			 afe_priv->i2s_path[i2s_num].div_bck_to_lrck);
+
+	mask = ASYS_I2S_CON_FS |
+	       ASYS_I2S_CON_I2S_COUPLE_MODE | /*0*/
+	       ASYS_I2S_CON_I2S_MODE |
+	       ASYS_I2S_CON_WIDE_MODE;
+
+	val = ASYS_I2S_CON_FS_SET(fs) |
+	      ASYS_I2S_CON_I2S_MODE |
+	      ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+	if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+		mask |= ASYS_I2S_IN_PHASE_FIX;
+		val |= ASYS_I2S_IN_PHASE_FIX;
+	}
+
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = ASMO_TIMING_CON1;
+	else
+		reg = ASMI_TIMING_CON1;
+
+	regmap_update_bits(afe->regmap, reg,
+			   i2s_data->i2s_asrc_fs_mask
+			   << i2s_data->i2s_asrc_fs_shift,
+			   fs << i2s_data->i2s_asrc_fs_shift);
+
+	/*enable i2s*/
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   0 << i2s_data->i2s_pwn_shift);
+
+	/*reset i2s hw status before enable*/
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, 0);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+	return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	int clk_domain;
+
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path = &afe_priv->i2s_path[i2s_num];
+	const int mclk_rate = i2s_path->mclk_rate;
+
+	if (i2s_path->occupied[substream->stream])
+		return -EBUSY;
+	i2s_path->occupied[substream->stream] = 1;
+
+	if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+		clk_domain = 0;
+	} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+		clk_domain = 1;
+	} else {
+		dev_err(dai->dev, "%s() bad mclk rate %d\n",
+			__func__, mclk_rate);
+		return -EINVAL;
+	}
+	mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	} else {
+		/*need to enable i2s-out path when enable i2s-in*/
+		/*prepare for another direction "out"*/
+		mt2701_i2s_path_prepare_enable(substream, dai, 1);
+		/*prepare for "in"*/
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	}
+
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				     unsigned int freq, int dir)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	/* mclk */
+	if (dir == SND_SOC_CLOCK_IN) {
+		dev_warn(dai->dev,
+			 "%s() warning: mt2701 doesn't support mclk input\n",
+			__func__);
+		return -EINVAL;
+	}
+	afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+				     int div)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	switch (div_id) {
+	case DIV_ID_MCLK_TO_BCK:
+		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
+		break;
+	case DIV_ID_BCK_TO_LRCK:
+		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	afe_priv->i2s_path[i2s_num].format = fmt;
+	return 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif_tmp;
+
+	/*can't run single DL & DLM at the same time*/
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+		if (memif_tmp->substream) {
+			dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+				 __func__, stream_dir, memif_num);
+			return -EBUSY;
+		}
+	}
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+
+	/*single DL use PAIR_INTERLEAVE*/
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_update_bits(afe->regmap,
+				   AFE_MEMIF_PBUF_SIZE,
+				   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+				   AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+	}
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp;
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_tmp = &afe->memif[i];
+		if (memif_tmp->substream)
+			return -EBUSY;
+	}
+
+	/*enable agent for all signal DL (due to hw design)*/
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   0 << memif_data->agent_disable_shift);
+	}
+
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   1 << memif_data->agent_disable_shift);
+	}
+	return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int channels = params_channels(params);
+
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+			   AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+				 int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift,
+				   1 << memif_tmp->data->enable_shift);
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift, 0);
+
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int fs;
+
+	if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT)
+		fs = mt2701_afe_i2s_fs(rate);
+	else
+		fs = (rate == 16000 ? 1 : 0);
+	return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+	return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+	.startup	= mt2701_simple_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mt2701_simple_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+	.startup	= mt2701_dlm_fe_startup,
+	.shutdown	= mt2701_dlm_fe_shutdown,
+	.hw_params	= mt2701_dlm_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+	.startup	= mt2701_afe_i2s_startup,
+	.shutdown	= mt2701_afe_i2s_shutdown,
+	.prepare	= mt2701_afe_i2s_prepare,
+	.set_sysclk	= mt2701_afe_i2s_set_sysclk,
+	.set_clkdiv	= mt2701_afe_i2s_set_clkdiv,
+	.set_fmt	= mt2701_afe_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+	/* FE DAIs: memory intefaces to CPU */
+	{
+		.name = "PCM_multi",
+		.id = MT2701_MEMIF_DLM,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLM",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_dlm_memif_dai_ops,
+	},
+	{
+		.name = "PCM0",
+		.id = MT2701_MEMIF_UL1,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM1",
+		.id = MT2701_MEMIF_UL2,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL2",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	/* BE DAIs */
+	{
+		.name = "I2S0",
+		.id = MT2701_IO_I2S,
+		.playback = {
+			.stream_name = "I2S0 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.capture = {
+			.stream_name = "I2S0 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S1",
+		.id = MT2701_IO_2ND_I2S,
+		.playback = {
+			.stream_name = "I2S1 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S1 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S2",
+		.id = MT2701_IO_3RD_I2S,
+		.playback = {
+			.stream_name = "I2S2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S3",
+		.id = MT2701_IO_4TH_I2S,
+		.playback = {
+			.stream_name = "I2S3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S3 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o23_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+	SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s0", ASYS_I2SO1_CON, 26, 1,
+				    0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s1", ASYS_I2SO2_CON, 26, 1,
+				    0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s2", PWR2_TOP_CON, 17, 1,
+				    0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s3", PWR2_TOP_CON, 18, 1,
+				    0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch Out I2s4", PWR2_TOP_CON, 19, 1,
+				    0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out0", AUDIO_TOP_CON4, 14, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out1", AUDIO_TOP_CON4, 15, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out2", PWR2_TOP_CON, 6, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out4", PWR2_TOP_CON, 8, 1,
+				    1),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+			   ARRAY_SIZE(mt2701_afe_i02_mix)),
+	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+			   ARRAY_SIZE(mt2701_afe_o00_mix)),
+	SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+			   ARRAY_SIZE(mt2701_afe_o01_mix)),
+	SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+			   ARRAY_SIZE(mt2701_afe_o02_mix)),
+	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+			   ARRAY_SIZE(mt2701_afe_o03_mix)),
+	SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+			   ARRAY_SIZE(mt2701_afe_o14_mix)),
+	SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+			   ARRAY_SIZE(mt2701_afe_o15_mix)),
+	SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+			   ARRAY_SIZE(mt2701_afe_o16_mix)),
+	SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+			   ARRAY_SIZE(mt2701_afe_o17_mix)),
+	SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+			   ARRAY_SIZE(mt2701_afe_o18_mix)),
+	SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+			   ARRAY_SIZE(mt2701_afe_o19_mix)),
+	SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+			   ARRAY_SIZE(mt2701_afe_o20_mix)),
+	SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+			   ARRAY_SIZE(mt2701_afe_o21_mix)),
+	SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+			   ARRAY_SIZE(mt2701_afe_o22_mix)),
+	SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+			   ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+	SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+	SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+	SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+	SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+
+	SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+	SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+	SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+	SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+	{"I12", NULL, "DL1"},
+	{"I13", NULL, "DL1"},
+	{"I35", NULL, "DLBT"},
+
+	{"I2S0 Playback", NULL, "O15"},
+	{"I2S0 Playback", NULL, "O16"},
+
+	{"I2S1 Playback", NULL, "O17"},
+	{"I2S1 Playback", NULL, "O18"},
+	{"I2S2 Playback", NULL, "O19"},
+	{"I2S2 Playback", NULL, "O20"},
+	{"I2S3 Playback", NULL, "O21"},
+	{"I2S3 Playback", NULL, "O22"},
+	{"BT Playback", NULL, "O31"},
+
+	{"UL1", NULL, "O00"},
+	{"UL1", NULL, "O01"},
+	{"UL2", NULL, "O02"},
+	{"UL2", NULL, "O03"},
+	{"ULBT", NULL, "O14"},
+
+	{"I00", NULL, "I2S0 Capture"},
+	{"I01", NULL, "I2S0 Capture"},
+
+	{"I02", NULL, "I2S1 Capture"},
+	{"I03", NULL, "I2S1 Capture"},
+	/*I02,03 link to UL2, also need to open I2S0*/
+	{"I02", "I2S0 Switch", "I2S0 Capture"},
+
+	{"I26", NULL, "BT Capture"},
+
+	{"ASRC_O0", "Multi ch asrc out0", "DLM"},
+	{"ASRC_O1", "Multi ch asrc out1", "DLM"},
+	{"ASRC_O2", "Multi ch asrc out2", "DLM"},
+	{"ASRC_O3", "Multi ch asrc out3", "DLM"},
+
+	{"I12I13", "Multi ch Out I2s0", "ASRC_O0"},
+	{"I14I15", "Multi ch Out I2s1", "ASRC_O1"},
+	{"I16I17", "Multi ch Out I2s2", "ASRC_O2"},
+	{"I18I19", "Multi ch Out I2s3", "ASRC_O3"},
+
+	{ "I12", NULL, "I12I13" },
+	{ "I13", NULL, "I12I13" },
+	{ "I14", NULL, "I14I15" },
+	{ "I15", NULL, "I14I15" },
+	{ "I16", NULL, "I16I17" },
+	{ "I17", NULL, "I16I17" },
+	{ "I18", NULL, "I18I19" },
+	{ "I19", NULL, "I18I19" },
+
+	{ "O00", "I00 Switch", "I00" },
+	{ "O01", "I01 Switch", "I01" },
+	{ "O02", "I02 Switch", "I02" },
+	{ "O03", "I03 Switch", "I03" },
+	{ "O14", "I26 Switch", "I26" },
+	{ "O15", "I12 Switch", "I12" },
+	{ "O16", "I13 Switch", "I13" },
+	{ "O17", "I14 Switch", "I14" },
+	{ "O18", "I15 Switch", "I15" },
+	{ "O19", "I16 Switch", "I16" },
+	{ "O20", "I17 Switch", "I17" },
+	{ "O21", "I18 Switch", "I18" },
+	{ "O22", "I19 Switch", "I19" },
+	{ "O31", "I35 Switch", "I35" },
+
+};
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+	.name = "mt2701-afe-pcm-dai",
+	.dapm_widgets = mt2701_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+	.dapm_routes = mt2701_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+};
+
+static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
+	{
+		.name = "DL1",
+		.id = MT2701_MEMIF_DL1,
+		.reg_ofs_base = AFE_DL1_BASE,
+		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 16,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 1,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 6,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL2",
+		.id = MT2701_MEMIF_DL2,
+		.reg_ofs_base = AFE_DL2_BASE,
+		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 17,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 2,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 7,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL3",
+		.id = MT2701_MEMIF_DL3,
+		.reg_ofs_base = AFE_DL3_BASE,
+		.reg_ofs_cur = AFE_DL3_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 18,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 3,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 4,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 8,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL4",
+		.id = MT2701_MEMIF_DL4,
+		.reg_ofs_base = AFE_DL4_BASE,
+		.reg_ofs_cur = AFE_DL4_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 19,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 4,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 9,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL5",
+		.id = MT2701_MEMIF_DL5,
+		.reg_ofs_base = AFE_DL5_BASE,
+		.reg_ofs_cur = AFE_DL5_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 20,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 20,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 5,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 10,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLM",
+		.id = MT2701_MEMIF_DLM,
+		.reg_ofs_base = AFE_DLMCH_BASE,
+		.reg_ofs_cur = AFE_DLMCH_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 7,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 28,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 12,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL1",
+		.id = MT2701_MEMIF_UL1,
+		.reg_ofs_base = AFE_VUL_BASE,
+		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 0,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 10,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 0,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL2",
+		.id = MT2701_MEMIF_UL2,
+		.reg_ofs_base = AFE_UL2_BASE,
+		.reg_ofs_cur = AFE_UL2_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 2,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 11,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 1,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL3",
+		.id = MT2701_MEMIF_UL3,
+		.reg_ofs_base = AFE_UL3_BASE,
+		.reg_ofs_cur = AFE_UL3_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 4,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 12,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 2,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL4",
+		.id = MT2701_MEMIF_UL4,
+		.reg_ofs_base = AFE_UL4_BASE,
+		.reg_ofs_cur = AFE_UL4_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 6,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 13,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 3,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL5",
+		.id = MT2701_MEMIF_UL5,
+		.reg_ofs_base = AFE_UL5_BASE,
+		.reg_ofs_cur = AFE_UL5_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 20,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 8,
+		.fs_maskbit = 0x1f,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 14,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 4,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLBT",
+		.id = MT2701_MEMIF_DLBT,
+		.reg_ofs_base = AFE_ARB1_BASE,
+		.reg_ofs_cur = AFE_ARB1_CUR,
+		.fs_reg = AFE_DAC_CON3,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 22,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 8,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 14,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 13,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "ULBT",
+		.id = MT2701_MEMIF_ULBT,
+		.reg_ofs_base = AFE_DAI_BASE,
+		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 30,
+		.fs_maskbit = 0x1,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 17,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 20,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 16,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+	{
+		.id = MT2701_IRQ_ASYS_IRQ1,
+		.irq_cnt_reg = ASYS_IRQ1_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ1_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ1_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 0,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ2,
+		.irq_cnt_reg = ASYS_IRQ2_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ2_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ2_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 1,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ3,
+		.irq_cnt_reg = ASYS_IRQ3_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ3_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ3_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 2,
+	}
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = {
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO1_CON,
+			.i2s_pwn_shift = 6,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN1_CON,
+			.i2s_pwn_shift = 0,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO2_CON,
+			.i2s_pwn_shift = 7,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN2_CON,
+			.i2s_pwn_shift = 1,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO3_CON,
+			.i2s_pwn_shift = 8,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN3_CON,
+			.i2s_pwn_shift = 2,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO4_CON,
+			.i2s_pwn_shift = 9,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN4_CON,
+			.i2s_pwn_shift = 3,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AFE_END_ADDR,
+	.cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+	int id;
+	struct mtk_base_afe *afe = dev;
+	struct mtk_base_afe_memif *memif;
+	struct mtk_base_afe_irq *irq;
+	u32 status;
+
+	regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+	regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+	for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+		memif = &afe->memif[id];
+		if (memif->irq_usage < 0)
+			continue;
+		irq = &afe->irqs[memif->irq_usage];
+		if (status & 1 << (irq->irq_data->irq_clr_shift))
+			mtk_simple_isr(afe, memif);
+	}
+	return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	mt2701_afe_enable_clock(afe, 0);
+	return 0;
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	mt2701_afe_enable_clock(afe, 1);
+	return 0;
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+	int ret, i;
+	unsigned int irq_id;
+	struct mtk_base_afe *afe;
+	struct mt2701_afe_private *afe_priv;
+	struct resource *res;
+	struct device *dev;
+
+	ret = 0;
+	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe)
+		return -ENOMEM;
+
+	afe->dev = &pdev->dev;
+	dev = afe->dev;
+
+	irq_id = platform_get_irq(pdev, 0);
+	if (!irq_id) {
+		dev_err(dev, "%s no irq found\n", dev->of_node->name);
+		return -ENXIO;
+	}
+	ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+			       IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+	if (ret) {
+		dev_err(dev, "could not request_irq for asys-isr\n");
+		return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+	if (IS_ERR(afe->base_addr))
+		return PTR_ERR(afe->base_addr);
+
+	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+		&mt2701_afe_regmap_config);
+	if (IS_ERR(afe->regmap))
+		return PTR_ERR(afe->regmap);
+
+	/*memif initialize*/
+	afe->memif_size = MT2701_MEMIF_NUM;
+	afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+				  GFP_KERNEL);
+
+	if (!afe->memif)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->memif_size; i++) {
+		afe->memif[i].data = &memif_data[i];
+		afe->memif[i].irq_usage = -1;
+	}
+
+	/*irq initialize*/
+	afe->irqs_size = MT2701_IRQ_ASYS_END;
+	afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+				 GFP_KERNEL);
+
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++)
+		afe->irqs[i].irq_data = &irq_data[i];
+
+	/*I2S initialize*/
+	for (i = 0; i < MT2701_I2S_NUM; i++) {
+		afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+			= &mt2701_i2s_data[i][I2S_OUT];
+		afe_priv->i2s_path[i].i2s_data[I2S_IN]
+			= &mt2701_i2s_data[i][I2S_IN];
+	}
+
+	afe->mtk_afe_hardware = &mt2701_afe_hardware;
+	afe->memif_fs = mt2701_memif_fs;
+	afe->irq_fs = mt2701_irq_fs;
+
+	afe->reg_back_up_list = mt2701_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+	afe->runtime_resume = mt2701_afe_runtime_resume;
+	afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+	/* initial audio related clock */
+	mt2701_init_clock(afe);
+
+	platform_set_drvdata(pdev, afe);
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev))
+		goto err_pm_disable;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	if (ret) {
+		dev_warn(dev, "err_platform\n");
+		goto err_platform;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev,
+					 &mt2701_afe_pcm_dai_component,
+					 mt2701_afe_pcm_dais,
+					 ARRAY_SIZE(mt2701_afe_pcm_dais));
+	if (ret) {
+		dev_warn(dev, "err_dai_component\n");
+		goto err_dai_component;
+	}
+
+	mt2701_afe_runtime_resume(&pdev->dev);
+
+	return 0;
+
+err_dai_component:
+	snd_soc_unregister_component(&pdev->dev);
+
+err_platform:
+	snd_soc_unregister_platform(&pdev->dev);
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		mt2701_afe_runtime_suspend(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_unregister_platform(&pdev->dev);
+	/*disable afe clock*/
+	mt2701_afe_enable_clock(afe, 0);
+	return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+			   mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+	.driver = {
+		   .name = "mt2701-audio",
+		   .of_match_table = mt2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+		   .pm = &mt2701_afe_pm_ops,
+#endif
+	},
+	.probe = mt2701_afe_pcm_dev_probe,
+	.remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:22 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:22 +0800
Subject: [alsa-devel] [PATCH v2 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-8-git-send-email-garlic.tseng@mediatek.com>

Add supports for 16k (wideband BT) and add a general compatible
string "linux,bt-sco"

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 Documentation/devicetree/bindings/sound/bt-sco.txt | 2 +-
 sound/soc/codecs/bt-sco.c                          | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt
index 29b8e5d..641edf7 100644
--- a/Documentation/devicetree/bindings/sound/bt-sco.txt
+++ b/Documentation/devicetree/bindings/sound/bt-sco.txt
@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad1..101b384 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -31,14 +31,14 @@ static struct snd_soc_dai_driver bt_sco_dai = {
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	.capture = {
 		 .stream_name = "Capture",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 };
@@ -77,6 +77,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
 	{ .compatible = "delta,dfbmcs320", },
+	{ .compatible = "linux,bt-sco", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:15 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:15 +0800
Subject: [alsa-devel] [PATCH v2 0/9] ASoC: Mediatek: Add support for MT2701 SOC
Message-ID: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>

This patch adds basic support for Mediatek AFE for MT2701 SoC.
The patch is based on broonie tree "for-next" branch.

Change since v1:

implement common private structure, fe dai and platform driver.
let mt8173 and mt2701 use the common structure.

Garlic Tseng (9):
  ASoC: mediatek: Refine mt8173 driver and change config option
  ASoC: mediatek: implement mediatek common structure
  ASoC: mediatek: let mt8173 use mediatek common structure
  ASoC: mediatek: add documents for mt2701
  ASoC: mediatek: add structure define and clock control for 2701
  ASoC: mediatek: add mt2701 platform driver implementation.
  ASoC: bt-sco: extend rate and add a general compatible string
  ASoC: mediatek: add BT implementation
  ASoC: mediatek: Add mt2701-cs42448 driver and config option.

 Documentation/devicetree/bindings/sound/bt-sco.txt |    2 +-
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   |  150 ++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |   43 +
 sound/soc/codecs/bt-sco.c                          |    5 +-
 sound/soc/mediatek/Kconfig                         |   44 +-
 sound/soc/mediatek/Makefile                        |   11 +-
 sound/soc/mediatek/common/Makefile                 |   17 +
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         |  387 +++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |   47 +
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |   90 ++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |   23 +
 sound/soc/mediatek/common/mtk-base-afe.h           |  103 ++
 sound/soc/mediatek/mt2701/Makefile                 |   19 +
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c  |  357 +++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h  |   33 +
 sound/soc/mediatek/mt2701/mt2701-afe-common.h      |  185 +++
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c         | 1664 ++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c         |  422 +++++
 sound/soc/mediatek/mt2701/mt2701-reg.h             |  186 +++
 sound/soc/mediatek/mt8173/Makefile                 |    7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      |   73 +
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     |  923 +++++------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |    4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |    2 +-
 sound/soc/mediatek/mtk-afe-common.h                |  101 --
 27 files changed, 4267 insertions(+), 635 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
 create mode 100644 sound/soc/mediatek/common/Makefile
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (51%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:19 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:19 +0800
Subject: [alsa-devel] [PATCH v2 4/9] ASoC: mediatek: add documents for mt2701
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-5-git-send-email-garlic.tseng@mediatek.com>

add mt2701-afe-pcm.txt and mt2701-cs42448.txt for mt2701

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   | 150 +++++++++++++++++++++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |  43 ++++++
 2 files changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 0000000..3e623a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm at 11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 0000000..0557444
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:23 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:23 +0800
Subject: [alsa-devel] [PATCH v2 8/9] ASoC: mediatek: add BT implementation
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-9-git-send-email-garlic.tseng@mediatek.com>

Add BT implementation for mt2701 platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 141 +++++++++++++++++++++++++++++
 1 file changed, 141 insertions(+)

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index 4c22ca0..9215b71 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -346,6 +346,88 @@ static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+	afe_priv->mrg_enable[substream->stream] = 1;
+	return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_fs;
+	u32 val, msk;
+
+	pr_debug("%s() cpu_dai id %d\n", __func__, dai->id);
+	stream_fs = params_rate(params);
+
+	if ((stream_fs != 8000) && (stream_fs != 16000)) {
+		pr_err("%s() btmgr not supprt this stream_fs %d\n",
+		       __func__, stream_fs);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_I2S_MODE_MASK,
+			   AFE_MRGIF_CON_I2S_MODE_32K);
+
+	val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+	      | AFE_DAIBT_CON0_MRG_USE;
+	msk = val;
+
+	if (stream_fs == 16000)
+		val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+			   AFE_DAIBT_CON0_DAIBT_EN,
+			   AFE_DAIBT_CON0_DAIBT_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_I2S_EN,
+			   AFE_MRGIF_CON_MRG_I2S_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_EN,
+			   AFE_MRGIF_CON_MRG_EN);
+	return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	pr_debug("%s() cpu_dai id %d\n", __func__, dai->id);
+	/* if the other direction stream is not occupied */
+	if (!afe_priv->mrg_enable[!substream->stream]) {
+		regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+				   AFE_DAIBT_CON0_DAIBT_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_I2S_EN, 0);
+		regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+				   AUDIO_TOP_CON4_PDN_MRGIF,
+				   AUDIO_TOP_CON4_PDN_MRGIF);
+	}
+	afe_priv->mrg_enable[substream->stream] = 0;
+}
+
 static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
@@ -529,6 +611,13 @@ static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
 	.set_fmt	= mt2701_afe_i2s_set_fmt,
 };
 
+/* MRG BE DAIs*/
+static struct snd_soc_dai_ops mt2701_btmrg_ops = {
+	.startup = mt2701_btmrg_startup,
+	.shutdown = mt2701_btmrg_shutdown,
+	.hw_params = mt2701_btmrg_hw_params,
+};
+
 static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
@@ -579,6 +668,36 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		},
 		.ops = &mt2701_single_memif_dai_ops,
 	},
+	{
+		.name = "PCM_BT_DL",
+		.id = MT2701_MEMIF_DLBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM_BT_UL",
+		.id = MT2701_MEMIF_ULBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "ULBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
 	/* BE DAIs */
 	{
 		.name = "I2S0",
@@ -678,6 +797,28 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		.ops = &mt2701_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
+	{
+		.name = "MRG BT",
+		.id = MT2701_IO_MRG,
+		.playback = {
+			.stream_name = "BT Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.stream_name = "BT Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_btmrg_ops,
+		.symmetric_rates = 1,
+	}
 };
 
 static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
-- 
1.9.1



From garlic.tseng at mediatek.com  Thu Jun  2 21:56:24 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 3 Jun 2016 12:56:24 +0800
Subject: [alsa-devel] [PATCH v2 9/9] ASoC: mediatek: Add mt2701-cs42448 driver
 and config option.
In-Reply-To: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1464929784-5489-10-git-send-email-garlic.tseng@mediatek.com>

Add machine driver and config option for MT2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                 |  21 ++
 sound/soc/mediatek/Makefile                |   1 +
 sound/soc/mediatek/mt2701/Makefile         |  19 ++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c | 422 +++++++++++++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ff1a419..0ecb785 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -7,6 +7,27 @@ config SND_SOC_MEDIATEK
 	  Select Y if you have such device.
 	  If unsure select "N".
 
+config SND_SOC_MT2701
+	tristate "ASoC support for Mediatek MT2701 chip"
+	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+	tristate "SND_SOC_MT2701_CS42448"
+	depends on SND_SOC_MT2701
+	select SND_SOC_CS42XX8_I2C
+	select SND_SOC_BT_SCO
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  with the CS42448 codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 3d893be..5133215 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
 
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 0000000..31c3d04
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644
index 0000000..fce159f
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,422 @@
+/*
+ * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian at mediatek.com>
+ *              Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+	int i2s1_in_mux;
+	int i2s1_in_mux_gpio_sel_1;
+	int i2s1_in_mux_gpio_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+	"ADC_SDOUT2",
+	"ADC_SDOUT3",
+	"I2S_IN_1",
+	"I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+	SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+	return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+		return 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 1:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 2:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	case 3:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	default:
+		dev_warn(card->dev, "%s invalid setting\n", __func__);
+	}
+
+	priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget
+			mt2701_cs42448_asoc_card_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+	SND_SOC_DAPM_MIC("AMIC", NULL),
+	SND_SOC_DAPM_LINE("Tuner In", NULL),
+	SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+	SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+	SOC_DAPM_PIN_SWITCH("AMIC"),
+	SOC_DAPM_PIN_SWITCH("Tuner In"),
+	SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+	SOC_DAPM_PIN_SWITCH("AUX In"),
+	SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+		     mt2701_cs42448_i2sin1_mux_get,
+		     mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+		.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+		.list = mt2701_cs42448_sampling_rates,
+		.mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+	int err;
+
+	err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 &mt2701_cs42448_constraints_rates);
+	if (err < 0) {
+		dev_err(substream->pcm->card->dev, /*better way to get device?*/
+			"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+			__func__, err);
+		return err;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+	.startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	/* codec slave, mt2701 master */
+	unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			    | SND_SOC_DAIFMT_CONT;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+	unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+	unsigned int div_bck_to_lrck = 64;
+
+	mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+
+	/* mt2701 mclk */
+	snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+	/* mt2701 bck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+	/* mt2701 lrck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+	/* mt2701 master */
+	snd_soc_dai_set_fmt(cpu_dai, fmt);
+
+	/* codec mclk */
+	snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+	/* codec slave */
+	snd_soc_dai_set_fmt(codec_dai, fmt);
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_be_ops = {
+	.hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+	DAI_LINK_FE_MULTI_CH_OUT,
+	DAI_LINK_FE_PCM0_IN,
+	DAI_LINK_FE_PCM1_IN,
+	DAI_LINK_FE_BT_OUT,
+	DAI_LINK_FE_BT_IN,
+	DAI_LINK_BE_I2S0,
+	DAI_LINK_BE_I2S1,
+	DAI_LINK_BE_I2S2,
+	DAI_LINK_BE_I2S3,
+	DAI_LINK_BE_MRG_BT,
+};
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+	/*FE*/
+	[DAI_LINK_FE_MULTI_CH_OUT] = {
+		.name = "mt2701-cs42448-multi-ch-out",
+		.stream_name = "mt2701-cs42448-multi-ch-out",
+		.cpu_dai_name = "PCM_multi",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_PCM0_IN] = {
+		.name = "mt2701-cs42448-pcm0",
+		.stream_name = "mt2701-cs42448-pcm0-data-UL",
+		.cpu_dai_name = "PCM0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_PCM1_IN] = {
+		.name = "mt2701-cs42448-pcm1-data-UL",
+		.stream_name = "mt2701-cs42448-pcm1-data-UL",
+		.cpu_dai_name = "PCM1",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_BT_OUT] = {
+		.name = "mt2701-cs42448-pcm-BT-out",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_DL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_BT_IN] = {
+		.name = "mt2701-cs42448-pcm-BT-in",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_UL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	/*BE*/
+	[DAI_LINK_BE_I2S0] = {
+		.name = "mt2701-cs42448-I2S0",
+		.cpu_dai_name = "I2S0",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S1] = {
+		.name = "mt2701-cs42448-I2S1",
+		.cpu_dai_name = "I2S1",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S2] = {
+		.name = "mt2701-cs42448-I2S2",
+		.cpu_dai_name = "I2S2",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S3] = {
+		.name = "mt2701-cs42448-I2S3",
+		.cpu_dai_name = "I2S3",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_MRG_BT] = {
+		.name = "mt2701-cs42448-MRG-BT",
+		.cpu_dai_name = "MRG BT",
+		.no_pcm = 1,
+		.codec_dai_name = "bt-sco-pcm",
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+	.name = "mt2701-cs42448",
+	.owner = THIS_MODULE,
+	.dai_link = mt2701_cs42448_dai_links,
+	.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+	.controls = mt2701_cs42448_controls,
+	.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+	.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+	int ret;
+	int i;
+	struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+	struct mt2701_cs42448_private *priv =
+		devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+			     GFP_KERNEL);
+	struct device *dev = &pdev->dev;
+
+	if (!priv)
+		return -ENOMEM;
+
+	platform_node = of_parse_phandle(pdev->dev.of_node,
+					 "mediatek,platform", 0);
+	if (!platform_node) {
+		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].platform_name)
+			continue;
+		mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
+	}
+
+	card->dev = dev;
+
+	codec_node = of_parse_phandle(pdev->dev.of_node,
+				      "mediatek,audio-codec", 0);
+	if (!codec_node) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].codec_name)
+			continue;
+		mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
+	}
+
+	codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+					     "mediatek,audio-codec-bt-mrg", 0);
+	if (!codec_node_bt_mrg) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec-bt-mrg' missing or invalid\n");
+		return -EINVAL;
+	}
+	mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+							= codec_node_bt_mrg;
+
+	ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+		return ret;
+	}
+
+	priv->i2s1_in_mux_gpio_sel_1 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+					"i2s1_in_mux_gpio_sel_1");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+	}
+
+	priv->i2s1_in_mux_gpio_sel_2 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+					"i2s1_in_mux_gpio_sel_2");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+	}
+	snd_soc_card_set_drvdata(card, priv);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret)
+		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+			__func__, ret);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+	{.compatible = "mediatek,mt2701-cs42448-machine",},
+	{}
+};
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+	.driver = {
+		.name = "mt2701-cs42448",
+		   #ifdef CONFIG_OF
+		   .of_match_table = mt2701_cs42448_machine_dt_match,
+		   #endif
+	},
+	.probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
-- 
1.9.1



From fan.chen at mediatek.com  Thu Jun  2 22:42:08 2016
From: fan.chen at mediatek.com (Fan Chen)
Date: Fri, 3 Jun 2016 13:42:08 +0800
Subject: [PATCH v2 1/3] regulator: DT: Add DT property for operation
 mode configuration
In-Reply-To: <20160603001600.GX2282@sirena.org.uk>
References: <1463987611-32139-1-git-send-email-henryc.chen@mediatek.com>
 <1463987611-32139-2-git-send-email-henryc.chen@mediatek.com>
 <20160523104703.GB26992@leverpostej> <20160523112856.GR8206@sirena.org.uk>
 <1464682835.25847.3.camel@mtksdaap41> <20160603001600.GX2282@sirena.org.uk>
Message-ID: <1464932528.18889.12.camel@mtksdaap41>

Hi Mark,

On Fri, 2016-06-03 at 01:16 +0100, Mark Brown wrote:
> On Tue, May 31, 2016 at 04:20:35PM +0800, Fan Chen wrote:
> > On Mon, 2016-05-23 at 12:28 +0100, Mark Brown wrote:

> > In the case of svs[1], which Henry mentioned in cover letter, it can be
> > regarded as a special consumer who requires very accurate voltage for
> > calibration the hardware in its initialization stage. So, this kinds of
> > consumers know their regulator very well and only need to switch to the
> > modes they want in the particular conditions.
> 
> So what you're trying to do here is not so much set a specific mode as
> set maximum regulation accuracy for a period of time.

exactly.

> > However, IIUC, you want a proposal to provide a sort of QoS framework
> > which can cover most of use cases who care about the regular quality in
> > runtime, is that correct?
> 
> Well, we want a coherent general use case that doesn't require a user to
> know the specific details of the regulator they're working with since we
> need to hide that knowledge from the user.

Agreed, it is hard to control once expose too many details. But I think
maybe there still be some parameter user has to aware to decide the
performance/quality in the common use cases you said below.

> 
> > IMHO, some quality index can be considered, for example:
> > Minimum Current Requirement (mA): If a user specified this constraint in
> > runtime, it means that he cares more about the supplying quality like
> > transient voltage drop, ripple above certain load.
> > Maximum Current Requirement (mA): If a user specified this constraint in
> > runtime, it means that he cares more about the power consumption under
> > certain load.
> > It could be a flexible way instead to tie the operation modes directly.
> 
> I'm not sure I really understand these distinctions to be honest,
> and specifying minimum loads seems very tricky from a robustness point
> of view.
> 
> If all you need right now is a way to maximize regulation quality that's
> probably a lot easier than anything based on absolute loads or on
> multiple "normal operation" modes - it takes a lot of the complexity out
> of things as there's no need to consider things like the distinctions
> between modes.  We just need a standard operating mode and to know the
> highest available mode.  I'm not sure exactly how to do that as an API
> though, let me think about it...  your use case isn't one I'd come
> across before.
Thanks. Please kindly give us suggestion for this case.
> 
> > BTW, we should encourage people here to share more use cases related to
> > regulator quality issues, especially in runtime, so we can evaluate the
> > most suitable index to fit the requirements.
> 
> More common use cases are around manually doing adaptive mode switching
> for regulators that are poor at automatically adjusting performance and
> handling of very low standby current situations where the adaption can
> consume enough power to register.

Best regards,
Fan



From hs.liao at mediatek.com  Thu Jun  2 23:12:36 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Fri, 3 Jun 2016 14:12:36 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <574FF264.7050209@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com>
Message-ID: <1464934356.15175.31.camel@mtksdaap41>

Hi Mathias,

Please see my inline reply.

On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
> 
> On 01/06/16 11:57, Horng-Shyang Liao wrote:
> > Hi Mathias,
> >
> > Please see my inline reply.
> >
> > On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
> >>
> >> On 31/05/16 10:36, Horng-Shyang Liao wrote:
> >>> Hi Mathias,
> >>>
> >>> Please see my inline reply.
> >>>
> >>> On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
> >>>>
> >>>> On 30/05/16 05:19, HS Liao wrote:
> >>>>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> >>>>> CMDQ is used to help read/write registers with critical time limitation,
> >>>>> such as updating display configuration during the vblank. It controls
> >>>>> Global Command Engine (GCE) hardware to achieve this requirement.
> >>>>> Currently, CMDQ only supports display related hardwares, but we expect
> >>>>> it can be extended to other hardwares for future requirements.
> >>>>>
> >>>>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> >>>>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> >>>>> ---
> >>>>>     drivers/soc/mediatek/Kconfig    |  10 +
> >>>>>     drivers/soc/mediatek/Makefile   |   1 +
> >>>>>     drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> >>>>>     include/soc/mediatek/cmdq.h     | 197 +++++++++
> >>>>>     4 files changed, 1151 insertions(+)
> >>>>>     create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> >>>>>     create mode 100644 include/soc/mediatek/cmdq.h
> >>>>>
> >>>>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> >>>>> index 0a4ea80..c4ad75c 100644
> >>>>> --- a/drivers/soc/mediatek/Kconfig
> >>>>> +++ b/drivers/soc/mediatek/Kconfig
> >>>>> @@ -1,6 +1,16 @@
> >>>>>     #
> >>>>>     # MediaTek SoC drivers
> >>>>>     #
> >>>>> +config MTK_CMDQ
> >>>>> +	bool "MediaTek CMDQ Support"
> >>>>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> >>
> >> depends on ARM64 ?
> >
> > Will add ARM64.
> >
> >>>>> +	select MTK_INFRACFG
> >>>>> +	help
> >>>>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> >>>>> +	  driver. The CMDQ is used to help read/write registers with critical
> >>>>> +	  time limitation, such as updating display configuration during the
> >>>>> +	  vblank.
> >>>>> +
> >>>>>     config MTK_INFRACFG
> >>>>>     	bool "MediaTek INFRACFG Support"
> >>>>>     	depends on ARCH_MEDIATEK || COMPILE_TEST
> >>>>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> >>>>> index 12998b0..f7397ef 100644
> >>>>> --- a/drivers/soc/mediatek/Makefile
> >>>>> +++ b/drivers/soc/mediatek/Makefile
> >>>>> @@ -1,3 +1,4 @@
> >>>>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
> >>>>>     obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> >>>>>     obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
> >>>>>     obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
> >>>>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
> >>>>> new file mode 100644
> >>>>> index 0000000..e9d6e1c
> >>>>> --- /dev/null
> >>>>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
> >>>>> @@ -0,0 +1,943 @@
> >>>>> +/*
> >>>>> + * Copyright (c) 2015 MediaTek Inc.
> >>>>> + *
> >>>>> + * This program is free software; you can redistribute it and/or modify
> >>>>> + * it under the terms of the GNU General Public License version 2 as
> >>>>> + * published by the Free Software Foundation.
> >>>>> + *
> >>>>> + * This program is distributed in the hope that it will be useful,
> >>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>>> + * GNU General Public License for more details.
> >>>>> + */
> >>>>> +
> >>>>> +#include <linux/clk.h>
> >>>>> +#include <linux/clk-provider.h>
> >>>>> +#include <linux/completion.h>
> >>>>> +#include <linux/dma-mapping.h>
> >>>>> +#include <linux/errno.h>
> >>>>> +#include <linux/interrupt.h>
> >>>>> +#include <linux/iopoll.h>
> >>>>> +#include <linux/kernel.h>
> >>>>> +#include <linux/kthread.h>
> >>>>> +#include <linux/module.h>
> >>>>> +#include <linux/mutex.h>
> >>>>> +#include <linux/of_address.h>
> >>>>> +#include <linux/of_irq.h>
> >>>>> +#include <linux/platform_device.h>
> >>>>> +#include <linux/slab.h>
> >>>>> +#include <linux/spinlock.h>
> >>>>> +#include <linux/suspend.h>
> >>>>> +#include <linux/workqueue.h>
> >>>>> +#include <soc/mediatek/cmdq.h>
> >>>>> +
> >>>>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
> >>>>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
> >>>>> +#define CMDQ_TIMEOUT_MS			1000
> >>>>> +#define CMDQ_IRQ_MASK			0xffff
> >>>>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
> >>>>> +#define CMDQ_CLK_NAME			"gce"
> >>>>
> >>>> We can put the names in directly to un-bloat the defines.
> >>>
> >>> I will use the names directly and remove defines.
> >>>
> >>>>> +
> >>>>> +#define CMDQ_CURR_IRQ_STATUS		0x010
> >>>>> +#define CMDQ_CURR_LOADED_THR		0x018
> >>>>> +#define CMDQ_THR_SLOT_CYCLES		0x030
> >>>>> +
> >>>>> +#define CMDQ_THR_BASE			0x100
> >>>>> +#define CMDQ_THR_SHIFT			0x080
> >>>>
> >>>> Wouldn't be CMDQ_THR_SIZE more accurate?
> >>>
> >>> Will rename it.
> >>>
> >>>>> +#define CMDQ_THR_WARM_RESET		0x00
> >>>>> +#define CMDQ_THR_ENABLE_TASK		0x04
> >>>>> +#define CMDQ_THR_SUSPEND_TASK		0x08
> >>>>> +#define CMDQ_THR_CURR_STATUS		0x0c
> >>>>> +#define CMDQ_THR_IRQ_STATUS		0x10
> >>>>> +#define CMDQ_THR_IRQ_ENABLE		0x14
> >>>>> +#define CMDQ_THR_CURR_ADDR		0x20
> >>>>> +#define CMDQ_THR_END_ADDR		0x24
> >>>>> +#define CMDQ_THR_CFG			0x40
> >>>>> +
> >>>>> +#define CMDQ_THR_ENABLED		0x1
> >>>>> +#define CMDQ_THR_DISABLED		0x0
> >>>>> +#define CMDQ_THR_SUSPEND		0x1
> >>>>> +#define CMDQ_THR_RESUME			0x0
> >>>>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
> >>>>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
> >>>>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
> >>>>> +#define CMDQ_THR_PRIORITY		3
> >>>>> +#define CMDQ_THR_IRQ_DONE		0x1
> >>>>> +#define CMDQ_THR_IRQ_ERROR		0x12
> >>>>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
> >>>>
> >>>> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
> >>>
> >>> Will do.
> >>>
> >>>>> +#define CMDQ_THR_IRQ_MASK		0x13
> >>>>
> >>>> never used.
> >>>
> >>> Will remove.
> >>>
> >>>>> +#define CMDQ_THR_EXECUTING		BIT(31)
> >>>>> +
> >>>>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
> >>>>> +#define CMDQ_SUBSYS_MASK		0x1f
> >>>>> +#define CMDQ_OP_CODE_MASK		0xff000000
> >>>>> +
> >>>>> +#define CMDQ_OP_CODE_SHIFT		24
> >>>>
> >>>> Couldn't we connect the mask with the shift, or aren't they related?
> >>>>
> >>>> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
> >>>
> >>> Will do.
> >>>
> >>>>> +#define CMDQ_SUBSYS_SHIFT		16
> >>>>> +
> >>>>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
> >>>>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
> >>>>> +#define CMDQ_JUMP_BY_PA			0x10000001
> >>>>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
> >>>>> +#define CMDQ_WFE_UPDATE			BIT(31)
> >>>>> +#define CMDQ_WFE_WAIT			BIT(15)
> >>>>> +#define CMDQ_WFE_WAIT_VALUE		0x1
> >>>>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
> >>>>> +
> >>>>> +enum cmdq_thread_index {
> >>>>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
> >>>>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
> >>>>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
> >>>>> +	CMDQ_THR_MAX_COUNT,	/* max */
> >>>>> +};
> >>>>> +
> >>>>> +/*
> >>>>> + * CMDQ_CODE_MOVE:
> >>>>> + *   move value into internal register as mask
> >>>>> + *   format: op mask
> >>>>> + * CMDQ_CODE_WRITE:
> >>>>> + *   write value into target register
> >>>>> + *   format: op subsys address value
> >>>>> + * CMDQ_CODE_JUMP:
> >>>>> + *   jump by offset
> >>>>> + *   format: op offset
> >>>>> + * CMDQ_CODE_WFE:
> >>>>> + *   wait for event and clear
> >>>>> + *   it is just clear if no wait
> >>>>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
> >>>>> + *           [clear] op event update:1 to_wait:0 wait:0
> >>>>> + * CMDQ_CODE_EOC:
> >>>>> + *   end of command
> >>>>> + *   format: op irq_flag
> >>>>> + */
> >>>>
> >>>> I think we need more documentation of how this command queue engine is
> >>>> working. If not, I think it will be really complicated to understand how
> >>>> to use this.
> >>>>
> >>>>> +enum cmdq_code {
> >>>>> +	CMDQ_CODE_MOVE = 0x02,
> >>>>> +	CMDQ_CODE_WRITE = 0x04,
> >>>>> +	CMDQ_CODE_JUMP = 0x10,
> >>>>> +	CMDQ_CODE_WFE = 0x20,
> >>>>> +	CMDQ_CODE_EOC = 0x40,
> >>>>> +};
> >>>>> +
> >>>>> +enum cmdq_task_state {
> >>>>> +	TASK_STATE_BUSY, /* running on a GCE thread */
> >>>>> +	TASK_STATE_ERROR,
> >>>>> +	TASK_STATE_DONE,
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq_task_cb {
> >>>>> +	cmdq_async_flush_cb	cb;
> >>>>> +	void			*data;
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq_thread {
> >>>>> +	void __iomem		*base;
> >>>>> +	struct list_head	task_busy_list;
> >>>>> +	wait_queue_head_t	wait_task_done;
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq_task {
> >>>>> +	struct cmdq		*cmdq;
> >>>>> +	struct list_head	list_entry;
> >>>>> +	enum cmdq_task_state	task_state;
> >>>>> +	void			*va_base;
> >>>>> +	dma_addr_t		pa_base;
> >>>>> +	u64			engine_flag;
> >>>>> +	size_t			command_size;
> >>>>> +	u32			num_cmd;
> >>>>
> >>>> num_cmd is directly connected to command_size. I prefer to just keep
> >>>> num_cmd and calculate the command_size where necessary.
> >>>
> >>> After I trace code, I prefer to keep command_size and calculate num_cmd
> >>> where necessary. What do you think?
> >>>
> >>
> >> I suppose you prefer this, as you are writing to the GCE depending on
> >> the command_size. I think it is worth to create a macro for the
> >> calculation of the number of commands, to make the code more readable.
> >> Would be nice if you would just pass cmdq_task to it and it would return
> >> the number. Just as an idea.
> >
> > Will add macro.
> >
> >>>>> +	struct cmdq_thread	*thread;
> >>>>> +	struct cmdq_task_cb	cb;
> >>>>> +	struct work_struct	release_work;
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq {
> >>>>> +	struct device		*dev;
> >>>>> +	void __iomem		*base;
> >>>>> +	u32			irq;
> >>>>> +	struct workqueue_struct	*task_release_wq;
> >>>>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
> >>>>> +	struct mutex		task_mutex;	/* for task */
> >>>>> +	spinlock_t		exec_lock;	/* for exec */
> >>>>> +	struct clk		*clock;
> >>>>> +	bool			suspended;
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq_subsys {
> >>>>> +	u32	base;
> >>>>> +	int	id;
> >>>>> +};
> >>>>> +
> >>>>> +static const struct cmdq_subsys gce_subsys[] = {
> >>>>> +	{0x1400, 1},
> >>>>> +	{0x1401, 2},
> >>>>> +	{0x1402, 3},
> >>>>> +};
> >>>>> +
> >>>>> +static int cmdq_subsys_base_to_id(u32 base)
> >>>>> +{
> >>>>> +	int i;
> >>>>> +
> >>>>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
> >>>>> +		if (gce_subsys[i].base == base)
> >>>>> +			return gce_subsys[i].id;
> >>>>> +	return -EFAULT;
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_eng_get_thread(u64 flag)
> >>>>> +{
> >>>>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> >>>>> +		return CMDQ_THR_DISP_MAIN_IDX;
> >>>>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> >>>>> +		return CMDQ_THR_DISP_SUB_IDX;
> >>>>> +	else
> >>>>> +		return CMDQ_THR_DISP_MISC_IDX;
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_task_release(struct cmdq_task *task)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>> +
> >>>>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
> >>>>> +			  task->pa_base);
> >>>>> +	kfree(task);
> >>>>> +}
> >>>>> +
> >>>>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
> >>>>> +					   struct cmdq_task_cb cb)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = rec->cmdq;
> >>>>> +	struct device *dev = cmdq->dev;
> >>>>> +	struct cmdq_task *task;
> >>>>> +
> >>>>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
> >>>>> +	INIT_LIST_HEAD(&task->list_entry);
> >>>>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
> >>>>> +					   &task->pa_base, GFP_KERNEL);
> >>>>> +	if (!task->va_base) {
> >>>>> +		dev_err(dev, "allocate command buffer failed\n");
> >>>>> +		kfree(task);
> >>>>> +		return NULL;
> >>>>> +	}
> >>>>> +
> >>>>> +	task->cmdq = cmdq;
> >>>>> +	task->command_size = rec->command_size;
> >>>>> +	task->engine_flag = rec->engine_flag;
> >>>>> +	task->task_state = TASK_STATE_BUSY;
> >>>>> +	task->cb = cb;
> >>>>> +	memcpy(task->va_base, rec->buf, rec->command_size);
> >>>>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
> >>>>> +	return task;
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
> >>>>> +			       u32 offset)
> >>>>> +{
> >>>>> +	writel(value, thread->base + offset);
> >>>>> +}
> >>>>> +
> >>>>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
> >>>>> +{
> >>>>> +	return readl(thread->base + offset);
> >>>>> +}
> >>>>
> >>>> We can get rid of cmdq_thread_readl/writel.
> >>>
> >>> Will do.
> >>>
> >>>>> +
> >>>>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	u32 status;
> >>>>> +
> >>>>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
> >>>>> +
> >>>>> +	/* If already disabled, treat as suspended successful. */
> >>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>> +	      CMDQ_THR_ENABLED))
> >>>>> +		return 0;
> >>>>> +
> >>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
> >>>>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
> >>>>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
> >>>>> +			(u32)(thread->base - cmdq->base));
> >>>>> +		return -EFAULT;
> >>>>> +	}
> >>>>> +
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	u32 warm_reset;
> >>>>> +
> >>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
> >>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
> >>>>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
> >>>>> +			0, 10)) {
> >>>>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
> >>>>> +			(u32)(thread->base - cmdq->base));
> >>>>> +		return -EFAULT;
> >>>>> +	}
> >>>>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	cmdq_thread_reset(cmdq, thread);
> >>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
> >>>>> +}
> >>>>> +
> >>>>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
> >>>>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	cmdq_thread_writel(thread,
> >>>>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
> >>>>> +			   CMDQ_THR_CURR_ADDR);
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
> >>>>> +{
> >>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>> +	struct cmdq_task *prev_task = list_last_entry(
> >>>>> +			&thread->task_busy_list, typeof(*task), list_entry);
> >>>>> +	u64 *prev_task_base = prev_task->va_base;
> >>>>> +
> >>>>> +	/* let previous task jump to this task */
> >>>>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
> >>>>> +						 task->pa_base;
> >>>>> +
> >>>>> +	cmdq_thread_invalidate_fetched_data(thread);
> >>>>> +}
> >>>>> +
> >>>>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
> >>>>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
> >>>>> +{
> >>>>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>>>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
> >>>>> +	u32 *base = task->va_base;
> >>>>> +	u32 num_cmd = task->num_cmd << 1;
> >>>>> +	int i;
> >>>>> +
> >>>>> +	for (i = 0; i < num_cmd; i += 2)
> >>>>> +		if (base[i] == wfe_option &&
> >>>>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
> >>>>> +			base[i] = CMDQ_JUMP_PASS;
> >>>>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
> >>>>> +		}
> >>>>
> >>>> After using the command buffer as a void pointer a u64 pointer, we now
> >>>> reference to it as u32. I would prefer to explain here, how the command
> >>>> looks like we are searching for and use a for loop passing task->num_cmd
> >>>> instead.
> >>>
> >>> Will use u64* to rewrite the above code.
> >>>
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>> +	unsigned long flags;
> >>>>> +	unsigned long curr_pa, end_pa;
> >>>>> +
> >>>>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
> >>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>
> >>>> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
> >>>> the spin_lock here? Can't we just use one of the two?
> >>>
> >>> We can drop task_mutex, but we will get some side effects.
> >>> 1. exec_lock needs to include more code, but I think it is not good for
> >>>      spinlock.
> >>> 2. In cmdq_rec_flush_async(), task_mutex needs to protect
> >>>      (1) cmdq->suspended, (2) cmdq_task_exec(), and
> >>>      (3) cmdq_task_wait_release_schedule().
> >>>      If we drop task_mutex, we have to put cmdq->suspended if condition
> >>>      just before cmdq_task_exec() and inside exec_lock, and we have to
> >>>      release task and its command buffer if error. This will let flow
> >>>      become more complex and enlarge code size.
> >>>
> >>> What do you think?
> >>
> >> Why do you need to protect cmdq_task_wait_release_schedule? We don't
> >> care about the order of the workqueue elements, do we?
> >
> > According to CK's comment, we have to ensure order to remove previous
> > task.
> > http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> >
> >> As far as I understand you would need to protect cmdq_task_acquire as
> >> well, to "ensure" continously growing pa_base. More on that below.
> >
> > We need to ensure continuous physical addresses in a task, but we don't
> > need to ensure continuous physical addresses between tasks.
> > So, I think it is unnecessary to protect by mutex or spinlock.
> >
> 
> True, I didn't get that.
> 
> >>>
> >>>>> +	task->thread = thread;
> >>>>> +	task->task_state = TASK_STATE_BUSY;
> >>>>
> >>>> That was already set in cmdq_task_acquire, why do we need to set it here
> >>>> again?
> >>>
> >>> Will drop it.
> >>>
> >>
> >> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire
> >> instead.
> >
> > Will drop it in cmdq_task_acquire instead.
> >
> >>>>> +	if (list_empty(&thread->task_busy_list)) {
> >>>>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
> >>>>> +
> >>>>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
> >>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>>>> +				   CMDQ_THR_END_ADDR);
> >>>>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
> >>>>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
> >>>>> +				   CMDQ_THR_IRQ_ENABLE);
> >>>>> +
> >>>>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
> >>>>> +				   CMDQ_THR_ENABLE_TASK);
> >>>>> +	} else {
> >>>>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>>>> +
> >>>>> +		/*
> >>>>> +		 * check boundary condition
> >>>>> +		 * PC = END - 8, EOC is executed
> >>>>> +		 * PC = END, all CMDs are executed
> >>>>> +		 */
> >>>>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
> >>>>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
> >>>>
> >>>> 8 refers to CMDQ_INST_SIZE, right?
> >>>
> >>> Yes, I will use CMDQ_INST_SIZE.
> >>>
> >>>>> +			/* set to this task directly */
> >>>>> +			cmdq_thread_writel(thread, task->pa_base,
> >>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>> +		} else {
> >>>>> +			cmdq_task_insert_into_thread(task);
> >>>>> +
> >>>>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
> >>>>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
> >>>>> +				cmdq_task_remove_wfe(task);
> >>>>
> >>>> We could do this check using the task->engine_flag, I suppose that's
> >>>> easier to undestand then.
> >>>
> >>> Will use task->engine_flag.
> >>>
> >>>>> +
> >>>>> +			smp_mb(); /* modify jump before enable thread */
> >>>>> +		}
> >>>>> +
> >>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>>>> +				   CMDQ_THR_END_ADDR);
> >>>>> +		cmdq_thread_resume(thread);
> >>>>> +	}
> >>>>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>> +				   struct cmdq_thread *thread, u32 irq_flag)
> >>>>> +{
> >>>>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>> +	u32 curr_pa;
> >>>>> +	struct cmdq_cb_data cmdq_cb_data;
> >>>>> +	bool err;
> >>>>> +
> >>>>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>> +		err = true;
> >>>>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>> +		err = false;
> >>>>> +	else
> >>>>> +		return;
> >>>>> +
> >>>>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>> +
> >>>>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>> +				 list_entry) {
> >>>>> +		if (curr_pa >= task->pa_base &&
> >>>>> +		    curr_pa < (task->pa_base + task->command_size))
> >>>>
> >>>> What are you checking here? It seems as if you make some implcit
> >>>> assumptions about pa_base and the order of execution of commands in the
> >>>> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
> >>>> about dma_handle?
> >>>
> >>> 1. Check what is the current running task in this GCE thread.
> >>> 2. Yes.
> >>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>
> >>
> >> Yes, physical addresses might be continous, but AFAIK there is no
> >> guarantee that the dma_handle address is steadily growing, when calling
> >> dma_alloc_coherent. And if I understand the code correctly, you use this
> >> assumption to decide if the task picked from task_busy_list is currently
> >> executing. So I think this mecanism is not working.
> >
> > I don't use dma_handle address, and just use physical addresses.
> >  From CPU's point of view, tasks are linked by the busy list.
> >  From GCE's point of view, tasks are linked by the JUMP command.
> >
> >> In which cases does the HW thread raise an interrupt.
> >> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >
> > GCE will raise interrupt if any task is done or error.
> > However, GCE is fast, so CPU may get multiple done tasks
> > when it is running ISR.
> >
> > In case of error, that GCE thread will pause and raise interrupt.
> > So, CPU may get multiple done tasks and one error task.
> >
> 
> I think we should reimplement the ISR mechanism. Can't we just read 
> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave 
> cmdq_handle_error_done to the thread_fn? You will need to pass 
> information from the handler to thread_fn, but that shouldn't be an 
> issue. AFAIK interrupts are disabled in the handler, so we should stay 
> there as short as possible. Traversing task_busy_list is expensive, so 
> we need to do it in a thread context.

Actually, our initial implementation is similar to your suggestion,
but display needs CMDQ to return callback function very precisely,
else display will drop frame.
For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
and CMDQ needs to call callback function in ISR.
If we defer callback to workqueue, the time interval may be larger than
32 ms.sometimes.

> I keep thinking about how to get rid of the two data structures, 
> task_busy_list and the task_release_wq. We need the latter for the only 
> sake of getting a timeout.
> 
> Did you have a look on how the mailbox framework handles this?
> By the way, what is the reason to not implement the whole driver as a 
> mailbox controller? For me, this driver looks like a good fit.

CMDQ needs to encode commands for GCE hardware. We think this behavior
should be put in CMDQ driver, and client just call CMDQ functions.
Therefore, if we want to use mailbox framework, cmdq_rec must be
mailbox client, and the others must be mailbox controller.

However, if we use mailbox controller, CMDQ driver still needs to
control busy list for each GCE thread, and use workqueue to handle
timeout tasks.

The only thing that we can borrow from mailbox framework is the send
(CMDQ flush) and receive (CMDQ callback) interface, However, we don't
think we can gain many benefits from it, and we have some overheads to
conform to mailbox interface.


> 
> >>>>> +			curr_task = task;
> >>>>> +		if (task->cb.cb) {
> >>>>> +			cmdq_cb_data.err = curr_task ? err : false;
> >>>>> +			cmdq_cb_data.data = task->cb.data;
> >>>>> +			task->cb.cb(cmdq_cb_data);
> >>>>> +		}
> >>>>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
> >>>>> +				TASK_STATE_DONE;
> >>>>> +		list_del(&task->list_entry);
> >>>>> +		if (curr_task)
> >>>>> +			break;
> >>>>> +	}
> >>>>> +
> >>>>> +	wake_up(&thread->wait_task_done);
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
> >>>>> +{
> >>>>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
> >>>>> +	unsigned long flags = 0L;
> >>>>> +	u32 irq_flag;
> >>>>> +
> >>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>> +
> >>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * Another CPU core could run "release task" right before we acquire
> >>>>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
> >>>>> +	 * need to check the enable bit of this GCE thread.
> >>>>> +	 */
> >>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>> +	      CMDQ_THR_ENABLED))
> >>>>> +		irq_flag = 0;
> >>>>
> >>>> cmdq_handle_error_done just retuns in this case. Programming this way
> >>>> just makes things confusing. What about:
> >>>>
> >>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>> 	      CMDQ_THR_ENABLED)
> >>>> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>> else
> >>>> 		irq_flag = 0;
> >>>>
> >>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>
> >>> We still need to clear irq_flag if GCE thread is disabled.
> >>> So, I think we can just return here.
> >>>
> >>> 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>> 	      CMDQ_THR_ENABLED))
> >>> 		return;
> >>>
> >>> What do you think?
> >>>
> >>
> >> No, you can't just return, you need to unlock the spinlock.
> >> Anyway I would prefer it the other way round, as I put it in my last
> >> mail. Just delete the else branch, we don't need to set irq_flag to zero.
> >
> > Sorry for my previous wrong reply since I merge your comment
> > and CK's comment.
> > http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> > So, I will put this if condition into cmdq_task_handle_error_result()
> > and then just return it if GCE thread is disabled.
> >
> 
> You mean in cmdq_task_handle_done
> We should rename this functions to not create confusion.

Sorry again. I mean in cmdq_handle_error_done().
This function handles both done and error.

I agree the function name looks confusion.
I think it can be renamed to cmdq_thread_irq_handler()
since it is used to handle irq for GCE thread.

> Regards,
> Matthias

Thanks,
HS

> >>>>> +
> >>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>> +}
> >>>>> +
> >>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = dev;
> >>>>> +	u32 irq_status;
> >>>>> +	int i;
> >>>>> +
> >>>>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
> >>>>> +	irq_status &= CMDQ_IRQ_MASK;
> >>>>> +	irq_status ^= CMDQ_IRQ_MASK;
> >>>>
> >>>> irq_status can be much bigger then 3, which is the number of threads in
> >>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
> >>>> clear to me.
> >>>
> >>> Our GCE hardware has 16 threads, but we only use 3 threads currently.
> >>>
> >>
> >> Ok, but please use bitops here.
> >
> > Will use bitops.
> >
> >>>>> +
> >>>>> +	if (!irq_status)
> >>>>> +		return IRQ_NONE;
> >>>>> +
> >>>>> +	while (irq_status) {
> >>>>> +		i = ffs(irq_status) - 1;
> >>>>> +		irq_status &= ~BIT(i);
> >>>>> +		cmdq_thread_irq_handler(cmdq, i);
> >>>>> +	}
> >>>>
> >>>> Can you explain how the irq status register looks like, that would it
> >>>> make much easier to understand what happens here.
> >>>
> >>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
> >>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
> >>>
> >>
> >> Thanks, that helped. :)
> >>
> >>>>> +
> >>>>> +	return IRQ_HANDLED;	
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
> >>>>
> >>>> We never check the return values, why do we have them?
> >>>
> >>> Will drop return value.
> >>>
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>> +	struct device *dev = cmdq->dev;
> >>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>> +	struct cmdq_task *next_task, *prev_task;
> >>>>> +	u32 irq_flag;
> >>>>> +
> >>>>> +	/* suspend GCE thread to ensure consistency */
> >>>>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>>>> +
> >>>>> +	/* ISR has handled this error task */
> >>>>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>>>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
> >>>>> +				struct cmdq_task, list_entry);
> >>>>> +		if (next_task) /* move to next task */
> >>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>
> >>>> We have to do this, as we suppose that the thread did not reach the jump
> >>>> instruction we put into it's command queue, right?
> >>>
> >>> Yes.
> >>>
> >>
> >> So this should then go into it's own function. In wait_release_work,
> >> something like this:
> >>
> >> if(task->task_state == TASK_STATE_ERROR)
> >>        cmdq_task_handle_error(task)
> >
> > OK.
> > I will write new function cmdq_task_handle_error() to handle error case.
> >
> >>>>> +		cmdq_thread_resume(thread);
> >>>>> +		return -ECANCELED;
> >>>>> +	}
> >>>>> +
> >>>>
> >>>> if task_state != ERROR and != DONE. This means that the timeout of
> >>>> task_release_wq has timed out, right?
> >>>
> >>> Yes.
> >>>
> >>>>> +	/*
> >>>>> +	 * Save next_task and prev_task in advance
> >>>>> +	 * since cmdq_handle_error_done will remove list_entry.
> >>>>> +	 */
> >>>>> +	next_task = prev_task = NULL;
> >>>>> +	if (task->list_entry.next != &thread->task_busy_list)
> >>>>> +		next_task = list_next_entry(task, list_entry);
> >>>>> +	if (task->list_entry.prev != &thread->task_busy_list)
> >>>>> +		prev_task = list_prev_entry(task, list_entry);
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * Although IRQ is disabled, GCE continues to execute.
> >>>>> +	 * It may have pending IRQ before GCE thread is suspended,
> >>>>> +	 * so check this condition again.
> >>>>> +	 */
> >>>>
> >>>> The first thing we did in this function was suspending the thread. Why
> >>>> do we need this then?
> >>>
> >>> Because timeout is CPU timeout not GCE timeout, GCE could just finish
> >>> this task before the GCE thread is suspended.
> >>>
> >>
> >> What are the reasons for a timeout? An error has happend, or the task is
> >> still executing.
> >
> >  From GCE's point of view, this task is still executing.
> > But, it could be an error of client.
> > For example, task may never get event if display turn off hardware
> > before waiting for task to finish its work.
> >
> >>>> To be honest this whole functions looks really like a design error. We
> >>>> have to sperate the states much clearer so that it is possible to
> >>>> understand what is happening in the GCE. Isn't it for example posible to
> >>>> have worker queues for timed out tasks and tasks with an error? I'm not
> >>>> sure how to do this, actually I'm not sure if I really understood how
> >>>> this is supposed to work.
> >>>
> >>> GCE doesn't have timeout. The timeout is decided and controlled by CPU,
> >>> so we check timeout in release work.
> >>> For error and done, they are easy to check by register, and we have
> >>> already created release work for timeout. So, I don't think we need to
> >>> create work queue for each case.
> >>>
> >>> What do you think?
> >>>
> >>
> >> I think, if we find in here, that the irq_flag is set, then the the
> >> interrupt handler was triggered and is spinning the spinlock. If this is
> >> not the case, we have a timeout and we handle this.
> >
> > I will write another function to handle error, and handle timeout here.
> >
> >>>> How much do we win, when we patch the thread command queue for every
> >>>> task we add, instead of just taking one task after another from the
> >>>> task_busy_list?
> >>>
> >>> GCE is used to help read/write registers with critical time limitation.
> >>> Sometimes, client may ask to process multiple tasks in a short period
> >>> of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
> >>> shouldn't limit to process one task after another from the
> >>> task_busy_list. Currently, when interrupt or timeout, we will check
> >>> how many tasks are done, and which one is error or timeout.
> >>>
> >>
> >> So I suppose the device driver who use this are interested in throughput
> >> and not in latency. The callback of every
> >>
> >>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>> +
> >>>>> +	if (task->task_state == TASK_STATE_DONE) {
> >>>>> +		cmdq_thread_resume(thread);
> >>>>> +		return 0;
> >>>>> +	}
> >>>>> +
> >>>>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>>>> +		dev_err(dev, "task 0x%p error\n", task);
> >>>>> +		if (next_task) /* move to next task */
> >>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>> +		cmdq_thread_resume(thread);
> >>>>> +		return -ECANCELED;
> >>>>> +	}
> >>>>> +
> >>>>> +	/* Task is running, so we force to remove it. */
> >>>>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
> >>>>> +	task->task_state = TASK_STATE_ERROR;
> >>>>> +
> >>>>> +	if (prev_task) {
> >>>>> +		u64 *prev_va = prev_task->va_base;
> >>>>> +		u64 *curr_va = task->va_base;
> >>>>> +
> >>>>> +		/* copy JUMP instruction */
> >>>>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
> >>>>> +
> >>>>> +		cmdq_thread_invalidate_fetched_data(thread);
> >>>>> +	} else if (next_task) { /* move to next task */
> >>>>> +		cmdq_thread_writel(thread, next_task->pa_base,
> >>>>> +				   CMDQ_THR_CURR_ADDR);
> >>>>> +	}
> >>>>> +
> >>>>> +	list_del(&task->list_entry);
> >>>>> +	cmdq_thread_resume(thread);
> >>>>> +
> >>>>> +	/* call cb here to prevent lock */
> >>>>> +	if (task->cb.cb) {
> >>>>> +		struct cmdq_cb_data cmdq_cb_data;
> >>>>> +
> >>>>> +		cmdq_cb_data.err = true;
> >>>>> +		cmdq_cb_data.data = task->cb.data;
> >>>>> +		task->cb.cb(cmdq_cb_data);
> >>>>> +	}
> >>>>> +
> >>>>> +	return -ECANCELED;
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
> >>>>> +{
> >>>>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
> >>>>> +					      release_work);
> >>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>> +	unsigned long flags;
> >>>>> +
> >>>>> +	wait_event_timeout(thread->wait_task_done,
> >>>>> +			   task->task_state != TASK_STATE_BUSY,
> >>>>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
> >>>>> +
> >>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>> +	if (task->task_state != TASK_STATE_DONE)
> >>>>> +		cmdq_task_handle_error_result(task);
> >>>>> +	if (list_empty(&thread->task_busy_list))
> >>>>> +		cmdq_thread_disable(cmdq, thread);
> >>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>> +
> >>>>> +	/* release regardless of success or not */
> >>>>> +	clk_disable_unprepare(cmdq->clock);
> >>>>> +	cmdq_task_release(task);
> >>>>> +}
> >>>>> +
> >>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>> +
> >>>>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
> >>>>> +	queue_work(cmdq->task_release_wq, &task->release_work);
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
> >>>>> +{
> >>>>> +	void *new_buf;
> >>>>> +
> >>>>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
> >>>>> +	if (!new_buf)
> >>>>> +		return -ENOMEM;
> >>>>> +	rec->buf = new_buf;
> >>>>> +	rec->buf_size = size;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
> >>>>> +{
> >>>>> +	struct cmdq_base *cmdq_base;
> >>>>> +	struct resource res;
> >>>>> +	int subsys;
> >>>>> +	u32 base;
> >>>>> +
> >>>>> +	if (of_address_to_resource(dev->of_node, 0, &res))
> >>>>> +		return NULL;
> >>>>> +	base = (u32)res.start;
> >>>>> +
> >>>>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
> >>>>> +	if (subsys < 0)
> >>>>> +		return NULL;
> >>>>> +
> >>>>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
> >>>>> +	if (!cmdq_base)
> >>>>> +		return NULL;
> >>>>> +	cmdq_base->subsys = subsys;
> >>>>> +	cmdq_base->base = base;
> >>>>> +
> >>>>> +	return cmdq_base;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_register_device);
> >>>>> +
> >>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>> +		    struct cmdq_rec **rec_ptr)
> >>>>> +{
> >>>>> +	struct cmdq_rec *rec;
> >>>>> +	int err;
> >>>>> +
> >>>>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
> >>>>> +	if (!rec)
> >>>>> +		return -ENOMEM;
> >>>>> +	rec->cmdq = dev_get_drvdata(dev);
> >>>>> +	rec->engine_flag = engine_flag;
> >>>>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
> >>>>
> >>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
> >>>
> >>> Will do.
> >>>
> >>>>> +	if (err < 0) {
> >>>>> +		kfree(rec);
> >>>>> +		return err;
> >>>>> +	}
> >>>>> +	*rec_ptr = rec;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_create);
> >>>>> +
> >>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
> >>>>> +				   u32 arg_a, u32 arg_b)
> >>>>> +{
> >>>>> +	u64 *cmd_ptr;
> >>>>> +	int err;
> >>>>> +
> >>>>> +	if (WARN_ON(rec->finalized))
> >>>>> +		return -EBUSY;
> >>>>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
> >>>>> +		return -EINVAL;
> >>>>
> >>>> cmdq_rec_append_command is just called from inside this driver and code
> >>>> is a enum. We can expect it to be correct, no need for this check.
> >>>
> >>> Will drop this check.
> >>>
> >>>>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
> >>>>
> >>>> command_size is the offset into the buffer to which a new command is
> >>>> written, so this name is highly confusing. I wonder if this would be
> >>>> easier to understand if we redefine command_size to something like the
> >>>> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
> >>>
> >>> I can rename command_size to cmd_buf_size and calculate num_cmd by
> >>> dividing CMDQ_INST_SIZE.
> >>> What do you think?
> >>>
> >>>>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
> >>>>> +		if (err < 0)
> >>>>> +			return err;
> >>>>> +	}
> >>>>> +	cmd_ptr = rec->buf + rec->command_size;
> >>>>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
> >>>>> +	rec->command_size += CMDQ_INST_SIZE;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
> >>>>> +		   u32 offset)
> >>>>> +{
> >>>>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
> >>>>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
> >>>>
> >>>> base->subsys is the id from gce_sybsys, so we can expect it to be
> >>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
> >>>
> >>> Will drop it.
> >>>
> >>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_write);
> >>>>> +
> >>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>> +			struct cmdq_base *base, u32 offset, u32 mask)
> >>>>> +{
> >>>>> +	u32 offset_mask = offset;
> >>>>> +	int err;
> >>>>> +
> >>>>> +	if (mask != 0xffffffff) {
> >>>>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
> >>>>> +		if (err < 0)
> >>>>> +			return err;
> >>>>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> >>>>> +	}
> >>>>> +	return cmdq_rec_write(rec, value, base, offset_mask);
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
> >>>>> +
> >>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> >>>>> +{
> >>>>> +	u32 arg_b;
> >>>>> +
> >>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>> +		return -EINVAL;
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * bit 0-11: wait value
> >>>>> +	 * bit 15: 1 - wait, 0 - no wait
> >>>>> +	 * bit 16-27: update value
> >>>>> +	 * bit 31: 1 - update, 0 - no update
> >>>>> +	 */
> >>>>
> >>>> I don't understand this comments. What are they for?
> >>>
> >>> This is for WFE command. I will comment it.
> >>>
> >>>>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
> >>>>> +
> >>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
> >>>>> +{
> >>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>> +		return -EINVAL;
> >>>>> +
> >>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
> >>>>> +				       CMDQ_WFE_UPDATE);
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
> >>>>> +
> >>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
> >>>>> +{
> >>>>> +	int err;
> >>>>> +
> >>>>> +	if (rec->finalized)
> >>>>> +		return 0;
> >>>>> +
> >>>>> +	/* insert EOC and generate IRQ for each command iteration */
> >>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
> >>>>> +	if (err < 0)
> >>>>> +		return err;
> >>>>> +
> >>>>> +	/* JUMP to end */
> >>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
> >>>>> +	if (err < 0)
> >>>>> +		return err;
> >>>>> +
> >>>>
> >>>> Does this need to be atomic?
> >>>> What happens if after CODE_EOC and before CODE_JUMP some
> >>>> write/read/event gets added?
> >>>> What happens if more commands get added to the queue after CODE_JUMP,
> >>>> but before finalized is set to true. Why don't you use atomic functions
> >>>> to access finalized?
> >>>
> >>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
> >>> client uses cmdq_rec.
> >>>
> >>
> >> Well I think that rec->finalized tries to implement this, but might
> >> fail, if two kernel threads work on the same cmdq_rec.
> >>
> >>>>> +	rec->finalized = true;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>>>> +			 void *data)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = rec->cmdq;
> >>>>> +	struct cmdq_task *task;
> >>>>> +	struct cmdq_task_cb task_cb;
> >>>>> +	struct cmdq_thread *thread;
> >>>>> +	int err;
> >>>>> +
> >>>>> +	mutex_lock(&cmdq->task_mutex);
> >>>>> +	if (cmdq->suspended) {
> >>>>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
> >>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>> +		return -EPERM;
> >>>>> +	}
> >>>>> +
> >>>>> +	err = cmdq_rec_finalize(rec);
> >>>>> +	if (err < 0) {
> >>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>> +		return err;
> >>>>> +	}
> >>>>> +
> >>>>> +	task_cb.cb = cb;
> >>>>> +	task_cb.data = data;
> >>>>> +	task = cmdq_task_acquire(rec, task_cb);
> >>>>> +	if (!task) {
> >>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>> +		return -EFAULT;
> >>>>> +	}
> >>>>> +
> >>>>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
> >>>>> +	cmdq_task_exec(task, thread);
> >>>>> +	cmdq_task_wait_release_schedule(task);
> >>>>> +	mutex_unlock(&cmdq->task_mutex);
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
> >>>>> +
> >>>>> +struct cmdq_flush_completion {
> >>>>> +	struct completion cmplt;
> >>>>> +	bool err;
> >>>>> +};
> >>>>> +
> >>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
> >>>>> +{
> >>>>> +	struct cmdq_flush_completion *cmplt = data.data;
> >>>>> +
> >>>>> +	cmplt->err = data.err;
> >>>>> +	complete(&cmplt->cmplt);
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
> >>>>> +{
> >>>>> +	struct cmdq_flush_completion cmplt;
> >>>>> +	int err;
> >>>>> +
> >>>>> +	init_completion(&cmplt.cmplt);
> >>>>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
> >>>>> +	if (err < 0)
> >>>>> +		return err;
> >>>>> +	wait_for_completion(&cmplt.cmplt);
> >>>>> +	return cmplt.err ? -EFAULT : 0;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
> >>>>> +
> >>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
> >>>>> +{
> >>>>> +	kfree(rec->buf);
> >>>>> +	kfree(rec);
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
> >>>>> +
> >>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
> >>>>> +{
> >>>>> +	struct cmdq_thread *thread;
> >>>>> +	int i;
> >>>>> +
> >>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>> +		thread = &cmdq->thread[i];
> >>>>> +		if (!list_empty(&thread->task_busy_list))
> >>>>> +			return false;
> >>>>> +	}
> >>>>> +	return true;
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_suspend(struct device *dev)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>> +	u32 exec_threads;
> >>>>> +
> >>>>> +	mutex_lock(&cmdq->task_mutex);
> >>>>> +	cmdq->suspended = true;
> >>>>> +	mutex_unlock(&cmdq->task_mutex);
> >>>>> +
> >>>>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
> >>>>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
> >>>>> +		dev_err(dev, "wait active tasks timeout.\n");
> >>>>> +		flush_workqueue(cmdq->task_release_wq);
> >>>>> +	}
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_resume(struct device *dev)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>> +
> >>>>> +	cmdq->suspended = false;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_remove(struct platform_device *pdev)
> >>>>> +{
> >>>>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
> >>>>> +
> >>>>> +	destroy_workqueue(cmdq->task_release_wq);
> >>>>> +	cmdq->task_release_wq = NULL;
> >>>>> +	return 0;
> >>>>> +}
> >>>>> +
> >>>>> +static int cmdq_probe(struct platform_device *pdev)
> >>>>> +{
> >>>>> +	struct device *dev = &pdev->dev;
> >>>>> +	struct device_node *node = dev->of_node;
> >>>>> +	struct resource *res;
> >>>>> +	struct cmdq *cmdq;
> >>>>> +	int err, i;
> >>>>> +
> >>>>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
> >>>>> +	if (!cmdq)
> >>>>> +		return -ENOMEM;
> >>>>> +	cmdq->dev = dev;
> >>>>> +
> >>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>>>> +	cmdq->base = devm_ioremap_resource(dev, res);
> >>>>> +	if (IS_ERR(cmdq->base)) {
> >>>>> +		dev_err(dev, "failed to ioremap gce\n");
> >>>>> +		return PTR_ERR(cmdq->base);
> >>>>> +	}
> >>>>> +
> >>>>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
> >>>>> +	if (!cmdq->irq) {
> >>>>> +		dev_err(dev, "failed to get irq\n");
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +
> >>>>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
> >>>>> +		dev, cmdq->base, cmdq->irq);
> >>>>> +
> >>>>> +	mutex_init(&cmdq->task_mutex);
> >>>>> +	spin_lock_init(&cmdq->exec_lock);
> >>>>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
> >>>>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
> >>>>> +			"cmdq_task_wait_release");
> >>>>> +
> >>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
> >>>>> +				CMDQ_THR_SHIFT * i;
> >>>>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
> >>>>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
> >>>>> +	}
> >>>>> +
> >>>>> +	platform_set_drvdata(pdev, cmdq);
> >>>>> +
> >>>>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
> >>>>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
> >>>>> +	if (err < 0) {
> >>>>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
> >>>>> +		goto fail;
> >>>>> +	}
> >>>>> +
> >>>>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
> >>>>> +	if (IS_ERR(cmdq->clock)) {
> >>>>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
> >>>>> +		err = PTR_ERR(cmdq->clock);
> >>>>> +		goto fail;
> >>>>> +	}
> >>>>> +	return 0;
> >>>>> +
> >>>>> +fail:
> >>>>> +	cmdq_remove(pdev);
> >>>>> +	return err;
> >>>>> +}
> >>>>> +
> >>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
> >>>>> +	.suspend = cmdq_suspend,
> >>>>> +	.resume = cmdq_resume,
> >>>>> +};
> >>>>> +
> >>>>> +static const struct of_device_id cmdq_of_ids[] = {
> >>>>> +	{.compatible = "mediatek,mt8173-gce",},
> >>>>> +	{}
> >>>>> +};
> >>>>> +
> >>>>> +static struct platform_driver cmdq_drv = {
> >>>>> +	.probe = cmdq_probe,
> >>>>> +	.remove = cmdq_remove,
> >>>>> +	.driver = {
> >>>>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
> >>>>> +		.owner = THIS_MODULE,
> >>>>> +		.pm = &cmdq_pm_ops,
> >>>>> +		.of_match_table = cmdq_of_ids,
> >>>>> +	}
> >>>>> +};
> >>>>> +
> >>>>> +builtin_platform_driver(cmdq_drv);
> >>>>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
> >>>>> new file mode 100644
> >>>>> index 0000000..60eef3d
> >>>>> --- /dev/null
> >>>>> +++ b/include/soc/mediatek/cmdq.h
> >>>>> @@ -0,0 +1,197 @@
> >>>>> +/*
> >>>>> + * Copyright (c) 2015 MediaTek Inc.
> >>>>> + *
> >>>>> + * This program is free software; you can redistribute it and/or modify
> >>>>> + * it under the terms of the GNU General Public License version 2 as
> >>>>> + * published by the Free Software Foundation.
> >>>>> + *
> >>>>> + * This program is distributed in the hope that it will be useful,
> >>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>>> + * GNU General Public License for more details.
> >>>>> + */
> >>>>> +
> >>>>> +#ifndef __MTK_CMDQ_H__
> >>>>> +#define __MTK_CMDQ_H__
> >>>>> +
> >>>>> +#include <linux/platform_device.h>
> >>>>> +#include <linux/types.h>
> >>>>> +
> >>>>> +enum cmdq_eng {
> >>>>> +	CMDQ_ENG_DISP_AAL,
> >>>>> +	CMDQ_ENG_DISP_COLOR0,
> >>>>> +	CMDQ_ENG_DISP_COLOR1,
> >>>>> +	CMDQ_ENG_DISP_DPI0,
> >>>>> +	CMDQ_ENG_DISP_DSI0,
> >>>>> +	CMDQ_ENG_DISP_DSI1,
> >>>>> +	CMDQ_ENG_DISP_GAMMA,
> >>>>> +	CMDQ_ENG_DISP_OD,
> >>>>> +	CMDQ_ENG_DISP_OVL0,
> >>>>> +	CMDQ_ENG_DISP_OVL1,
> >>>>> +	CMDQ_ENG_DISP_PWM0,
> >>>>> +	CMDQ_ENG_DISP_PWM1,
> >>>>> +	CMDQ_ENG_DISP_RDMA0,
> >>>>> +	CMDQ_ENG_DISP_RDMA1,
> >>>>> +	CMDQ_ENG_DISP_RDMA2,
> >>>>> +	CMDQ_ENG_DISP_UFOE,
> >>>>> +	CMDQ_ENG_DISP_WDMA0,
> >>>>> +	CMDQ_ENG_DISP_WDMA1,
> >>>>> +	CMDQ_ENG_MAX,
> >>>>> +};
> >>>>> +
> >>>>> +/* events for CMDQ and display */
> >>>>> +enum cmdq_event {
> >>>>> +	/* Display start of frame(SOF) events */
> >>>>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> >>>>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> >>>>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> >>>>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> >>>>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> >>>>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> >>>>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> >>>>> +	/* Display end of frame(EOF) events */
> >>>>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> >>>>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> >>>>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> >>>>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> >>>>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> >>>>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> >>>>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> >>>>> +	/* Mutex end of frame(EOF) events */
> >>>>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> >>>>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> >>>>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> >>>>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> >>>>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> >>>>> +	/* Display underrun events */
> >>>>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> >>>>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> >>>>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> >>>>> +	/* Keep this at the end of HW events */
> >>>>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> >>>>> +};
> >>>>> +
> >>>>> +struct cmdq_cb_data {
> >>>>> +	bool	err;
> >>>>> +	void	*data;
> >>>>> +};
> >>>>> +
> >>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
> >>>>> +
> >>>>> +struct cmdq_task;
> >>>>> +struct cmdq;
> >>>>> +
> >>>>> +struct cmdq_rec {
> >>>>> +	struct cmdq		*cmdq;
> >>>>> +	u64			engine_flag;
> >>>>> +	size_t			command_size;
> >>>>> +	void			*buf;
> >>>>> +	size_t			buf_size;
> >>>>> +	bool			finalized;
> >>>>> +};
> >>
> >> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that
> >> and this way make the driver less complex?
> >
> > There are two main reasons for cmdq_rec.
> > 1. It is slow to access dma too frequently.
> >     So, we append commands to cacheable memory, and then flush to dma.
> > 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
> >     If we merge them, we need to take care of some synchronization
> >     issues.
> >
> >>>>> +
> >>>>> +struct cmdq_base {
> >>>>> +	int	subsys;
> >>>>> +	u32	base;
> >>>>
> >>>> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
> >>>> so we can get rid of the struct, right?
> >>>
> >>> Current subsys method is based on previous comment from Daniel Kurtz.
> >>> Please take a look of our previous discussion.
> >>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >>> Thanks.
> >>>
> >>
> >> I have to look deeper into this, but from what I read, the proposal from
> >> Daniel [1] seems good to me.
> >>
> >> [1] https://patchwork.kernel.org/patch/8068311/
> >
> > The initial proposal has some problem, so please see the follow-up
> > discussions. Thanks.
> > http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
> > http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >
> >
> >>>>> +};
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_register_device() - register device which needs CMDQ
> >>>>> + * @dev:		device
> >>>>> + *
> >>>>> + * Return: cmdq_base pointer or NULL for failed
> >>>>> + */
> >>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_create() - create command queue record
> >>>>> + * @dev:		device
> >>>>> + * @engine_flag:	command queue engine flag
> >>>>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + */
> >>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>> +		    struct cmdq_rec **rec_ptr);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_write() - append write command to the command queue record
> >>>>> + * @rec:	the command queue record
> >>>>> + * @value:	the specified target register value
> >>>>> + * @base:	the command queue base
> >>>>> + * @offset:	register offset from module base
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + */
> >>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
> >>>>> +		   struct cmdq_base *base, u32 offset);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_write_mask() - append write command with mask to the command
> >>>>> + *			   queue record
> >>>>> + * @rec:	the command queue record
> >>>>> + * @value:	the specified target register value
> >>>>> + * @base:	the command queue base
> >>>>> + * @offset:	register offset from module base
> >>>>> + * @mask:	the specified target register mask
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + */
> >>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>> +			struct cmdq_base *base, u32 offset, u32 mask);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
> >>>>
> >>>> reco rd -> record
> >>>
> >>> Will fix it.
> >>>
> >>>> Regards,
> >>>> Matthias
> >>>>
> >>>>> + * @rec:	the command queue record
> >>>>> + * @event:	the desired event type to "wait and CLEAR"
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + */
> >>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_clear_event() - append clear event command to the command queue
> >>>>> + *			    record
> >>>>> + * @rec:	the command queue record
> >>>>> + * @event:	the desired event to be cleared
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + */
> >>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
> >>>>> + * @rec:	the command queue record
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + *
> >>>>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
> >>>>> + * synchronous flush function. When the function returned, the recorded
> >>>>> + * commands have been done.
> >>>>> + */
> >>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
> >>>>> + *			    commands and call back after ISR is finished
> >>>>> + * @rec:	the command queue record
> >>>>> + * @cb:		called in the end of CMDQ ISR
> >>>>> + * @data:	this data will pass back to cb
> >>>>> + *
> >>>>> + * Return: 0 for success; else the error code is returned
> >>>>> + *
> >>>>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
> >>>>> + * after ISR is finished. Note that this is an ASYNC function. When the function
> >>>>> + * returned, it may or may not be finished. The ISR callback function is called
> >>>>> + * in the end of ISR.
> >>
> >> "The callback is called from the ISR."
> >>
> >> Regards,
> >> Matthias
> >>
> >>>>> + */
> >>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>>>> +			 void *data);
> >>>>> +
> >>>>> +/**
> >>>>> + * cmdq_rec_destroy() - destroy command queue record
> >>>>> + * @rec:	the command queue record
> >>>>> + */
> >>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
> >>>>> +
> >>>>> +#endif	/* __MTK_CMDQ_H__ */
> >>>>>
> >>>
> >>> Thanks,
> >>> HS
> >>>
> >
> > Thanks,
> > HS
> >




From weiqing.kong at mediatek.com  Thu Jun  2 23:45:49 2016
From: weiqing.kong at mediatek.com (weiqing kong)
Date: Fri, 3 Jun 2016 14:45:49 +0800
Subject: [PATCH 1/3] dt-bindings: pwm: Add MediaTek display PWM bindings
In-Reply-To: <20160602224550.GA19256@rob-hp-laptop>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-2-git-send-email-weiqing.kong@mediatek.com>
 <20160602224550.GA19256@rob-hp-laptop>
Message-ID: <1464936349.5209.9.camel@mhfsdcap03>

On Thu, 2016-06-02 at 17:45 -0500, Rob Herring wrote:
> On Mon, May 30, 2016 at 04:41:50PM +0800, Weiqing Kong wrote:
> > Add MT2701 compatible string.
> > 
> > Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> > ---
> >  Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt | 5 +++--
> >  1 file changed, 3 insertions(+), 2 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> > index f8f59ba..f2f2fa9 100644
> > --- a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> > +++ b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> > @@ -1,9 +1,10 @@
> >  MediaTek display PWM controller
> >  
> >  Required properties:
> > - - compatible: should be "mediatek,<name>-disp-pwm":
> > -   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
> > + - compatible: should be like these:
> > +   - "mediatek,mt2701-disp-bls": found on mt2701 SoC.
> 
> Why bls instead of pwm?

In mt6595/mt8173, we have a hardware module named pwm.
In mt2701, we have bls hardware module which includes disp pwm function,
so we named it mt2701-disp-bls rather than mt2701-disp-pwm in order to
match with hardware spec.
thanks.

> 
> >     - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
> > +   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
> >   - reg: physical base address and length of the controller's registers.
> >   - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
> >     the cell format.
> > -- 
> > 1.8.1.1.dirty
> > 




From geert at linux-m68k.org  Fri Jun  3 00:17:00 2016
From: geert at linux-m68k.org (Geert Uytterhoeven)
Date: Fri, 3 Jun 2016 09:17:00 +0200
Subject: [RFC v3 02/45] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-3-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-3-git-send-email-k.kozlowski@samsung.com>
Message-ID: <CAMuHMdXWMf7Dt77wSUj8NytQqb99jzDiAz46kJkAEz+6BX3Uvw@mail.gmail.com>

Hi Krzysztof,

On Thu, Jun 2, 2016 at 5:39 PM, Krzysztof Kozlowski
<k.kozlowski at samsung.com> wrote:
> --- a/include/linux/dma-mapping.h
> +++ b/include/linux/dma-mapping.h
> @@ -5,13 +5,25 @@

> +/**
> + * List of possible attributes associated with a DMA mapping. The semantics
> + * of each attribute should be defined in Documentation/DMA-attributes.txt.
> + */
> +#define DMA_ATTR_WRITE_BARRIER         (1UL << 1)

Any particular reason they start at 2, not 1?

> +#define DMA_ATTR_WEAK_ORDERING         (1UL << 2)
> +#define DMA_ATTR_WRITE_COMBINE         (1UL << 3)
> +#define DMA_ATTR_NON_CONSISTENT                (1UL << 4)
> +#define DMA_ATTR_NO_KERNEL_MAPPING     (1UL << 5)
> +#define DMA_ATTR_SKIP_CPU_SYNC         (1UL << 6)
> +#define DMA_ATTR_FORCE_CONTIGUOUS      (1UL << 7)
> +#define DMA_ATTR_ALLOC_SINGLE_PAGES    (1UL << 8)

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


From geert at linux-m68k.org  Fri Jun  3 00:18:44 2016
From: geert at linux-m68k.org (Geert Uytterhoeven)
Date: Fri, 3 Jun 2016 09:18:44 +0200
Subject: [RFC v3 29/45] m68k: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-30-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-30-git-send-email-k.kozlowski@samsung.com>
Message-ID: <CAMuHMdXpMu6iuSYRJwyqP8mx_A9cAnsgkTh-8nU+JMpD4f2-SA@mail.gmail.com>

On Thu, Jun 2, 2016 at 5:39 PM, Krzysztof Kozlowski
<k.kozlowski at samsung.com> wrote:
> Split out subsystem specific changes for easier reviews. This will be
> squashed with main commit.
>
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

Looks good.

Acked-by: Geert Uytterhoeven <geert at linux-m68k.org>

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert at linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds


From boris.brezillon at free-electrons.com  Fri Jun  3 00:36:15 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Fri, 3 Jun 2016 09:36:15 +0200
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <8671722.LHaiaBNqfE@avalon>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
 <8671722.LHaiaBNqfE@avalon>
Message-ID: <20160603093615.14ba5fb8@bbrezillon>

On Fri, 03 Jun 2016 00:05:46 +0300
Laurent Pinchart <laurent.pinchart at ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Thursday 02 Jun 2016 16:31:28 Boris Brezillon wrote:
> > Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
> > drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
> > that DRM drivers can leave this hook unassigned if they know they want
> > to use drm_atomic_helper_best_encoder().  
> 
> Could you please update include/drm/drm_modeset_helper_vtables.h to document 
> this new behaviour ?

Sure.

> 
> The only drawback I see in this patch is the additional object lookup 
> performed by drm_atomic_helper_best_encoder() at runtime. I wonder if we could 
> somehow cache the information, as the assignment can't change when there's a 
> 1:1 correspondence between encoders and connectors.
> 
> > Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> > ---
> >  drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
> >  drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
> >  2 files changed, 15 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> > b/drivers/gpu/drm/drm_atomic_helper.c index f6a3350..849d029 100644
> > --- a/drivers/gpu/drm/drm_atomic_helper.c
> > +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state
> > *state, if (funcs->atomic_best_encoder)
> >  		new_encoder = funcs->atomic_best_encoder(connector,
> >  							 connector_state);
> > -	else
> > +	else if (funcs->best_encoder)
> >  		new_encoder = funcs->best_encoder(connector);
> > +	else
> > +		new_encoder = drm_atomic_helper_best_encoder(connector);
> > 
> >  	if (!new_encoder) {
> >  		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
> > diff --git a/drivers/gpu/drm/drm_fb_helper.c
> > b/drivers/gpu/drm/drm_fb_helper.c index 7c2eb75..d44389a 100644
> > --- a/drivers/gpu/drm/drm_fb_helper.c
> > +++ b/drivers/gpu/drm/drm_fb_helper.c
> > @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper
> > *fb_helper, my_score++;
> > 
> >  	connector_funcs = connector->helper_private;
> > -	encoder = connector_funcs->best_encoder(connector);
> > +
> > +	/*
> > +	 * If the DRM device implements atomic hooks and ->best_encoder() is
> > +	 * NULL we fallback to the default drm_atomic_helper_best_encoder()
> > +	 * helper.
> > +	 */
> > +	if (fb_helper->dev->mode_config.funcs->atomic_commit &&
> > +	    !connector_funcs->best_encoder)
> > +		encoder = drm_atomic_helper_best_encoder(connector);
> > +	else
> > +		encoder = connector_funcs->best_encoder(connector);
> > +
> >  	if (!encoder)
> >  		goto out;  
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


From boris.brezillon at free-electrons.com  Fri Jun  3 00:37:43 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Fri, 3 Jun 2016 09:37:43 +0200
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
 <8671722.LHaiaBNqfE@avalon>
 <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>
Message-ID: <20160603093743.000ef95b@bbrezillon>

On Thu, 2 Jun 2016 23:57:02 +0200
Daniel Vetter <daniel at ffwll.ch> wrote:

> On Thu, Jun 2, 2016 at 11:05 PM, Laurent Pinchart
> <laurent.pinchart at ideasonboard.com> wrote:
> > Hi Boris,
> >
> > Thank you for the patch.
> >
> > On Thursday 02 Jun 2016 16:31:28 Boris Brezillon wrote:  
> >> Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
> >> drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
> >> that DRM drivers can leave this hook unassigned if they know they want
> >> to use drm_atomic_helper_best_encoder().  
> >
> > Could you please update include/drm/drm_modeset_helper_vtables.h to document
> > this new behaviour ?  
> 
> Thanks for reminding me. Please update hooks for both
> atomic_best_encoder and best_encoder. Also mention that it's only
> optional for atomic drivers. There's lots of examples in that file for
> the wording usually used.

Hm, I haven't changed anything for the ->atomic_best_encoder() hook, or
am I missing something?

> 
> > The only drawback I see in this patch is the additional object lookup
> > performed by drm_atomic_helper_best_encoder() at runtime. I wonder if we could
> > somehow cache the information, as the assignment can't change when there's a
> > 1:1 correspondence between encoders and connectors.  
> 
> drm_encoder_find is an idr lookup. That should be plenty fast,
> especially for modeset code. Usually what's too expensive even for
> modeset code is linear list walks. But Chris just submitted patches to
> convert most of them into simple lookups.
> -Daniel
> 
> >>> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>  
> >> ---
> >>  drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
> >>  drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
> >>  2 files changed, 15 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> >> b/drivers/gpu/drm/drm_atomic_helper.c index f6a3350..849d029 100644
> >> --- a/drivers/gpu/drm/drm_atomic_helper.c
> >> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> >> @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state
> >> *state, if (funcs->atomic_best_encoder)
> >>               new_encoder = funcs->atomic_best_encoder(connector,
> >>                                                        connector_state);
> >> -     else
> >> +     else if (funcs->best_encoder)
> >>               new_encoder = funcs->best_encoder(connector);
> >> +     else
> >> +             new_encoder = drm_atomic_helper_best_encoder(connector);
> >>
> >>       if (!new_encoder) {
> >>               DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
> >> diff --git a/drivers/gpu/drm/drm_fb_helper.c
> >> b/drivers/gpu/drm/drm_fb_helper.c index 7c2eb75..d44389a 100644
> >> --- a/drivers/gpu/drm/drm_fb_helper.c
> >> +++ b/drivers/gpu/drm/drm_fb_helper.c
> >> @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper
> >> *fb_helper, my_score++;
> >>
> >>       connector_funcs = connector->helper_private;
> >> -     encoder = connector_funcs->best_encoder(connector);
> >> +
> >> +     /*
> >> +      * If the DRM device implements atomic hooks and ->best_encoder() is
> >> +      * NULL we fallback to the default drm_atomic_helper_best_encoder()
> >> +      * helper.
> >> +      */
> >> +     if (fb_helper->dev->mode_config.funcs->atomic_commit &&
> >> +         !connector_funcs->best_encoder)
> >> +             encoder = drm_atomic_helper_best_encoder(connector);
> >> +     else
> >> +             encoder = connector_funcs->best_encoder(connector);
> >> +
> >>       if (!encoder)
> >>               goto out;  
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
> >  
> 
> 
> 



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


From boris.brezillon at free-electrons.com  Fri Jun  3 00:38:49 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Fri, 3 Jun 2016 09:38:49 +0200
Subject: [PATCH 09/20] drm: rcar-du: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <5863270.jtrd0Kruzj@avalon>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-10-git-send-email-boris.brezillon@free-electrons.com>
 <5863270.jtrd0Kruzj@avalon>
Message-ID: <20160603093849.136858be@bbrezillon>

On Thu, 02 Jun 2016 23:57:14 +0300
Laurent Pinchart <laurent.pinchart at ideasonboard.com> wrote:

> Hi Boris,
> 
> Thank you for the patch.
> 
> On Thursday 02 Jun 2016 16:31:36 Boris Brezillon wrote:
> > All outputs have a 1:1 relationship between connectors and encoders,
> > and the driver is relying on the atomic helpers: we can drop the custom  
> > ->best_encoder() implementations and let the core call  
> > drm_atomic_helper_best_encoder() for us.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> > ---
> >  drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ------------
> >  drivers/gpu/drm/rcar-du/rcar_du_encoder.h |  3 ---
> >  drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c |  1 -
> >  drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c |  1 -
> >  drivers/gpu/drm/rcar-du/rcar_du_vgacon.c  |  1 -
> >  5 files changed, 18 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 4e939e4..55149e9 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
> > @@ -27,18 +27,6 @@
> >  #include "rcar_du_vgacon.h"
> > 
> >  /*
> > ---------------------------------------------------------------------------
> > -- - * Common connector functions
> > - */
> > -
> > -struct drm_encoder *
> > -rcar_du_connector_best_encoder(struct drm_connector *connector)
> > -{
> > -	struct rcar_du_connector *rcon = to_rcar_connector(connector);
> > -
> > -	return rcar_encoder_to_drm_encoder(rcon->encoder);
> > -}
> > -
> > -/*
> > ---------------------------------------------------------------------------
> > -- * Encoder
> >   */
> > 
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> > b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 719b6f2a..a8669c3 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
> > @@ -49,9 +49,6 @@ struct rcar_du_connector {
> >  #define to_rcar_connector(c) \
> >  	container_of(c, struct rcar_du_connector, connector)
> > 
> > -struct drm_encoder *
> > -rcar_du_connector_best_encoder(struct drm_connector *connector);
> > -
> >  int rcar_du_encoder_init(struct rcar_du_device *rcdu,
> >  			 enum rcar_du_encoder_type type,
> >  			 enum rcar_du_output output,
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> > b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c index 6c92714..612b4d5 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
> > @@ -52,7 +52,6 @@ static int rcar_du_hdmi_connector_mode_valid(struct
> > drm_connector *connector, static const struct drm_connector_helper_funcs
> > connector_helper_funcs = { .get_modes = rcar_du_hdmi_connector_get_modes,
> >  	.mode_valid = rcar_du_hdmi_connector_mode_valid,
> > -	.best_encoder = rcar_du_connector_best_encoder,
> >  };
> > 
> >  static enum drm_connector_status
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> > b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c index e905f5d..6afd0af 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
> > @@ -59,7 +59,6 @@ static int rcar_du_lvds_connector_get_modes(struct
> > drm_connector *connector)
> > 
> >  static const struct drm_connector_helper_funcs connector_helper_funcs = {
> >  	.get_modes = rcar_du_lvds_connector_get_modes,
> > -	.best_encoder = rcar_du_connector_best_encoder,
> >  };
> > 
> >  static enum drm_connector_status
> > diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> > b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c index 9d7e5c9..68f7ffa 100644
> > --- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> > +++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
> > @@ -28,7 +28,6 @@ static int rcar_du_vga_connector_get_modes(struct
> > drm_connector *connector)
> > 
> >  static const struct drm_connector_helper_funcs connector_helper_funcs = {
> >  	.get_modes = rcar_du_vga_connector_get_modes,
> > -	.best_encoder = rcar_du_connector_best_encoder,
> >  };
> > 
> >  static enum drm_connector_status  
> 
> You can also remove
> 
>         rcon->encoder = renc;
> 
> from rcar_du_vga_connector_init(), it's not needed anymore. The same code in 
> rcar_du_hdmi_connector_init() has to stay for now though, as it's used to 
> locate the slave encoder in the HDMI support code. That should change when the 
> driver will be converted to use drm_bridge.
> 
> I can also fix this during the conversion to drm_bridge if you don't want to 
> resubmit. In any case,

There will be a v2 anyway, so I'll squash this change in the patch.

> 
> Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> 

Thanks,

Boris

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


From k.kozlowski at samsung.com  Fri Jun  3 00:50:40 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Fri, 03 Jun 2016 09:50:40 +0200
Subject: [RFC v3 02/45] dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <CAMuHMdXWMf7Dt77wSUj8NytQqb99jzDiAz46kJkAEz+6BX3Uvw@mail.gmail.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-3-git-send-email-k.kozlowski@samsung.com>
 <CAMuHMdXWMf7Dt77wSUj8NytQqb99jzDiAz46kJkAEz+6BX3Uvw@mail.gmail.com>
Message-ID: <575136D0.6000101@samsung.com>

On 06/03/2016 09:17 AM, Geert Uytterhoeven wrote:
> Hi Krzysztof,
> 
> On Thu, Jun 2, 2016 at 5:39 PM, Krzysztof Kozlowski
> <k.kozlowski at samsung.com> wrote:
>> --- a/include/linux/dma-mapping.h
>> +++ b/include/linux/dma-mapping.h
>> @@ -5,13 +5,25 @@
> 
>> +/**
>> + * List of possible attributes associated with a DMA mapping. The semantics
>> + * of each attribute should be defined in Documentation/DMA-attributes.txt.
>> + */
>> +#define DMA_ATTR_WRITE_BARRIER         (1UL << 1)
> 
> Any particular reason they start at 2, not 1?

No reason. I'll fix this in next version (and trim Cc-list). Anyway the
values of constants won't match old ones but that should not be problem
(unless they are hard-coded somewhere).

Best regards,
Krzysztof




From jesper.nilsson at axis.com  Fri Jun  3 00:52:19 2016
From: jesper.nilsson at axis.com (Jesper Nilsson)
Date: Fri, 3 Jun 2016 09:52:19 +0200
Subject: [RFC v3 10/45] cris: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-11-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-11-git-send-email-k.kozlowski@samsung.com>
Message-ID: <20160603075219.GF3183@axis.com>

On Thu, Jun 02, 2016 at 05:39:12PM +0200, Krzysztof Kozlowski wrote:
> Split out subsystem specific changes for easier reviews. This will be
> squashed with main commit.

Acked-by: Jesper Nilsson <jesper.nilsson at axis.com>

> Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

/^JN - Jesper Nilsson
-- 
               Jesper Nilsson -- jesper.nilsson at axis.com


From k.kozlowski at samsung.com  Fri Jun  3 00:55:48 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Fri, 03 Jun 2016 09:55:48 +0200
Subject: [RFC v3 01/45] powerpc: dma-mapping: Don't hard-code the value of
 DMA_ATTR_WEAK_ORDERING
In-Reply-To: <1464881987-13203-2-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-2-git-send-email-k.kozlowski@samsung.com>
Message-ID: <57513804.9020309@samsung.com>

On 06/02/2016 05:39 PM, Krzysztof Kozlowski wrote:
> Hard-coded value of DMA_ATTR_WEAK_ORDERING is then compared with the
> symbol.  This will stop matching if the value of symbol is changed (when
> switching DMA attributes to unsigned long).
> 
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
> ---
>  arch/powerpc/platforms/cell/iommu.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/platforms/cell/iommu.c b/arch/powerpc/platforms/cell/iommu.c
> index 14a582b21274..0c2794d2b6c0 100644
> --- a/arch/powerpc/platforms/cell/iommu.c
> +++ b/arch/powerpc/platforms/cell/iommu.c
> @@ -1162,7 +1162,7 @@ static int __init setup_iommu_fixed(char *str)
>  	pciep = of_find_node_by_type(NULL, "pcie-endpoint");
>  
>  	if (strcmp(str, "weak") == 0 || (pciep && strcmp(str, "strong") != 0))
> -		iommu_fixed_is_weak = 1;
> +		iommu_fixed_is_weak = DMA_ATTR_WEAK_ORDERING;

After some more thoughts given to this, I think my fix is not correct.
The 'iommu_fixed_is_weak' stores the bool and it is used to compare with
result of test_bit().

Please ignore this patch.

Best regards,
Krzysztof


From rmk+kernel at armlinux.org.uk  Fri Jun  3 00:58:15 2016
From: rmk+kernel at armlinux.org.uk (Russell King)
Date: Fri, 03 Jun 2016 08:58:15 +0100
Subject: [PATCH] iommu: convert DT component matching to
 component_match_add_release()
Message-ID: <E1b8jzj-00046r-Pd@rmk-PC.armlinux.org.uk>

Convert DT component matching to use component_match_add_release().

Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
---
 drivers/iommu/mtk_iommu.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index c3043d8754e3..c036df1c49ca 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -557,6 +557,11 @@ static int compare_of(struct device *dev, void *data)
 	return dev->of_node == data;
 }
 
+static void release_of(struct device *dev, void *data)
+{
+	of_node_put(data);
+}
+
 static int mtk_iommu_bind(struct device *dev)
 {
 	struct mtk_iommu_data *data = dev_get_drvdata(dev);
@@ -630,17 +635,19 @@ static int mtk_iommu_probe(struct platform_device *pdev)
 			continue;
 
 		plarbdev = of_find_device_by_node(larbnode);
-		of_node_put(larbnode);
 		if (!plarbdev) {
 			plarbdev = of_platform_device_create(
 						larbnode, NULL,
 						platform_bus_type.dev_root);
-			if (!plarbdev)
+			if (!plarbdev) {
+				of_node_put(larbnode);
 				return -EPROBE_DEFER;
+			}
 		}
 		data->smi_imu.larb_imu[i].dev = &plarbdev->dev;
 
-		component_match_add(dev, &match, compare_of, larbnode);
+		component_match_add_release(dev, &match, release_of,
+					    compare_of, larbnode);
 	}
 
 	platform_set_drvdata(pdev, data);
-- 
2.1.0



From john at phrozen.org  Fri Jun  3 01:17:05 2016
From: john at phrozen.org (John Crispin)
Date: Fri,  3 Jun 2016 10:17:05 +0200
Subject: [PATCH V4 0/4]  net-next: mediatek: improve phy support
Message-ID: <1464941829-38712-1-git-send-email-john@phrozen.org>

The current driver did not handle the RGMII delay modes and asymmetric flow
control properly. The mii_bus is not freed properly. Also add support for
fixed-phy allowing the driver to work on SoCs that have an internal gigabit
switch.

John Crispin (4):
  net-next: mediatek: use mdiobus_free() in favour of kfree()
  net-next: mediatek: fix gigabit and flow control advertisement
  net-next: mediatek: add fixed-phy support
  net-next: mediatek: properly handle RGMII modes

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   40 +++++++++++++++++++++++----
 1 file changed, 34 insertions(+), 6 deletions(-)

-- 
1.7.10.4



From john at phrozen.org  Fri Jun  3 01:17:08 2016
From: john at phrozen.org (John Crispin)
Date: Fri,  3 Jun 2016 10:17:08 +0200
Subject: [PATCH V4 3/4] net-next: mediatek: add fixed-phy support
In-Reply-To: <1464941829-38712-1-git-send-email-john@phrozen.org>
References: <1464941829-38712-1-git-send-email-john@phrozen.org>
Message-ID: <1464941829-38712-4-git-send-email-john@phrozen.org>

The MT7623 SoC has a builtin gigabit switch. If we want to use it, GMAC1
needs to be configured using a fixed link speed and flow control settings.
The easiest way to do this is to used the fixed-phy driver, allowing us to
reuse the existing mdio polling code to setup the MAC.

Reviewed-by: Andrew Lunn <andrew at lunn.ch>
Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index b0652f6..231d284 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -229,6 +229,9 @@ static int mtk_phy_connect(struct mtk_mac *mac)
 	u32 val, ge_mode;
 
 	np = of_parse_phandle(mac->of_node, "phy-handle", 0);
+	if (!np && of_phy_is_fixed_link(mac->of_node))
+		if (!of_phy_register_fixed_link(mac->of_node))
+			np = of_node_get(mac->of_node);
 	if (!np)
 		return -ENODEV;
 
-- 
1.7.10.4



From john at phrozen.org  Fri Jun  3 01:17:06 2016
From: john at phrozen.org (John Crispin)
Date: Fri,  3 Jun 2016 10:17:06 +0200
Subject: [PATCH V4 1/4] net-next: mediatek: use mdiobus_free() in favour of
 kfree()
In-Reply-To: <1464941829-38712-1-git-send-email-john@phrozen.org>
References: <1464941829-38712-1-git-send-email-john@phrozen.org>
Message-ID: <1464941829-38712-2-git-send-email-john@phrozen.org>

The driver currently uses kfree() to clear the mii_bus. This is not the
correct way to clear the memory and mdiobus_free() should be used instead.
This patch fixes the two instances where this happens in the driver.

Reviewed-by: Andrew Lunn <andrew at lunn.ch>
Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index c984462..28f169f 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -280,7 +280,7 @@ static int mtk_mdio_init(struct mtk_eth *eth)
 	return 0;
 
 err_free_bus:
-	kfree(eth->mii_bus);
+	mdiobus_free(eth->mii_bus);
 
 err_put_node:
 	of_node_put(mii_np);
@@ -295,7 +295,7 @@ static void mtk_mdio_cleanup(struct mtk_eth *eth)
 
 	mdiobus_unregister(eth->mii_bus);
 	of_node_put(eth->mii_bus->dev.of_node);
-	kfree(eth->mii_bus);
+	mdiobus_free(eth->mii_bus);
 }
 
 static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
-- 
1.7.10.4



From john at phrozen.org  Fri Jun  3 01:17:09 2016
From: john at phrozen.org (John Crispin)
Date: Fri,  3 Jun 2016 10:17:09 +0200
Subject: [PATCH V4 4/4] net-next: mediatek: properly handle RGMII modes
In-Reply-To: <1464941829-38712-1-git-send-email-john@phrozen.org>
References: <1464941829-38712-1-git-send-email-john@phrozen.org>
Message-ID: <1464941829-38712-5-git-send-email-john@phrozen.org>

If an external Gigabit PHY is connected to either of the MACs we need to
be able to tell the PHY to use a delay. Not doing so will result in heavy
packet loss and/or data corruption when using PHYs such as the IC+ IP1001.
We tell the PHY which MII delay mode to use via the devictree.

The ethernet driver needs to be adapted to handle all 3 rgmii-*id modes
in the same way as normal rgmii when setting up the MAC.

Reviewed-by: Andrew Lunn <andrew at lunn.ch>
Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 231d284..4763252 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -236,6 +236,9 @@ static int mtk_phy_connect(struct mtk_mac *mac)
 		return -ENODEV;
 
 	switch (of_get_phy_mode(np)) {
+	case PHY_INTERFACE_MODE_RGMII_TXID:
+	case PHY_INTERFACE_MODE_RGMII_RXID:
+	case PHY_INTERFACE_MODE_RGMII_ID:
 	case PHY_INTERFACE_MODE_RGMII:
 		ge_mode = 0;
 		break;
-- 
1.7.10.4



From john at phrozen.org  Fri Jun  3 01:17:07 2016
From: john at phrozen.org (John Crispin)
Date: Fri,  3 Jun 2016 10:17:07 +0200
Subject: [PATCH V4 2/4] net-next: mediatek: fix gigabit and flow control
 advertisement
In-Reply-To: <1464941829-38712-1-git-send-email-john@phrozen.org>
References: <1464941829-38712-1-git-send-email-john@phrozen.org>
Message-ID: <1464941829-38712-3-git-send-email-john@phrozen.org>

The current code will not setup the PHYs advertisement features correctly.
Fix this and properly advertise Gigabit features and properly handle
asymmetric pause frames.

Signed-off-by: Sean Wang <keyhaede at gmail.com>
Signed-off-by: John Crispin <john at phrozen.org>
---
Changes in V4
* set SUPPORTED_Asym_Pause rather than clearing it
* add debug code
* only setup asym if we are in full duplex

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   30 +++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 28f169f..b0652f6 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -133,6 +133,8 @@ static int mtk_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg)
 static void mtk_phy_link_adjust(struct net_device *dev)
 {
 	struct mtk_mac *mac = netdev_priv(dev);
+	u16 lcl_adv = 0, rmt_adv = 0;
+	u8 flowctrl;
 	u32 mcr = MAC_MCR_MAX_RX_1536 | MAC_MCR_IPG_CFG |
 		  MAC_MCR_FORCE_MODE | MAC_MCR_TX_EN |
 		  MAC_MCR_RX_EN | MAC_MCR_BACKOFF_EN |
@@ -150,11 +152,30 @@ static void mtk_phy_link_adjust(struct net_device *dev)
 	if (mac->phy_dev->link)
 		mcr |= MAC_MCR_FORCE_LINK;
 
-	if (mac->phy_dev->duplex)
+	if (mac->phy_dev->duplex) {
 		mcr |= MAC_MCR_FORCE_DPX;
 
-	if (mac->phy_dev->pause)
-		mcr |= MAC_MCR_FORCE_RX_FC | MAC_MCR_FORCE_TX_FC;
+		if (mac->phy_dev->pause)
+			rmt_adv = LPA_PAUSE_CAP;
+		if (mac->phy_dev->asym_pause)
+			rmt_adv |= LPA_PAUSE_ASYM;
+
+		if (mac->phy_dev->advertising & ADVERTISED_Pause)
+			lcl_adv |= ADVERTISE_PAUSE_CAP;
+		if (mac->phy_dev->advertising & ADVERTISED_Asym_Pause)
+			lcl_adv |= ADVERTISE_PAUSE_ASYM;
+
+		flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv);
+
+		if (flowctrl & FLOW_CTRL_TX)
+			mcr |= MAC_MCR_FORCE_TX_FC;
+		if (flowctrl & FLOW_CTRL_RX)
+			mcr |= MAC_MCR_FORCE_RX_FC;
+
+		netif_dbg(mac->hw, link, dev, "rx pause %s, tx pause %s\n",
+			  flowctrl & FLOW_CTRL_RX ? "enabled" : "disabled",
+			  flowctrl & FLOW_CTRL_TX ? "enabled" : "disabled");
+	}
 
 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id));
 
@@ -236,7 +257,8 @@ static int mtk_phy_connect(struct mtk_mac *mac)
 	mac->phy_dev->autoneg = AUTONEG_ENABLE;
 	mac->phy_dev->speed = 0;
 	mac->phy_dev->duplex = 0;
-	mac->phy_dev->supported &= PHY_BASIC_FEATURES;
+	mac->phy_dev->supported &= PHY_GBIT_FEATURES | SUPPORTED_Pause |
+				   SUPPORTED_Asym_Pause;
 	mac->phy_dev->advertising = mac->phy_dev->supported |
 				    ADVERTISED_Autoneg;
 	phy_start_aneg(mac->phy_dev);
-- 
1.7.10.4



From matthias.bgg at gmail.com  Fri Jun  3 04:18:12 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 13:18:12 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464934356.15175.31.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
Message-ID: <57516774.5080008@gmail.com>



On 03/06/16 08:12, Horng-Shyang Liao wrote:
> Hi Mathias,
>
> Please see my inline reply.
>
> On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
>>
>> On 01/06/16 11:57, Horng-Shyang Liao wrote:
>>> Hi Mathias,
>>>
>>> Please see my inline reply.
>>>
>>> On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
>>>>
>>>> On 31/05/16 10:36, Horng-Shyang Liao wrote:
>>>>> Hi Mathias,
>>>>>
>>>>> Please see my inline reply.
>>>>>
>>>>> On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
>>>>>>
>>>>>> On 30/05/16 05:19, HS Liao wrote:
>>>>>>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
>>>>>>> CMDQ is used to help read/write registers with critical time limitation,
>>>>>>> such as updating display configuration during the vblank. It controls
>>>>>>> Global Command Engine (GCE) hardware to achieve this requirement.
>>>>>>> Currently, CMDQ only supports display related hardwares, but we expect
>>>>>>> it can be extended to other hardwares for future requirements.
>>>>>>>
>>>>>>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
>>>>>>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
>>>>>>> ---
>>>>>>>      drivers/soc/mediatek/Kconfig    |  10 +
>>>>>>>      drivers/soc/mediatek/Makefile   |   1 +
>>>>>>>      drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
>>>>>>>      include/soc/mediatek/cmdq.h     | 197 +++++++++
>>>>>>>      4 files changed, 1151 insertions(+)
>>>>>>>      create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
>>>>>>>      create mode 100644 include/soc/mediatek/cmdq.h
>>>>>>>
>>>>>>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
>>>>>>> index 0a4ea80..c4ad75c 100644
>>>>>>> --- a/drivers/soc/mediatek/Kconfig
>>>>>>> +++ b/drivers/soc/mediatek/Kconfig
>>>>>>> @@ -1,6 +1,16 @@
>>>>>>>      #
>>>>>>>      # MediaTek SoC drivers
>>>>>>>      #
>>>>>>> +config MTK_CMDQ
>>>>>>> +	bool "MediaTek CMDQ Support"
>>>>>>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
>>>>
>>>> depends on ARM64 ?
>>>
>>> Will add ARM64.
>>>
>>>>>>> +	select MTK_INFRACFG
>>>>>>> +	help
>>>>>>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
>>>>>>> +	  driver. The CMDQ is used to help read/write registers with critical
>>>>>>> +	  time limitation, such as updating display configuration during the
>>>>>>> +	  vblank.
>>>>>>> +
>>>>>>>      config MTK_INFRACFG
>>>>>>>      	bool "MediaTek INFRACFG Support"
>>>>>>>      	depends on ARCH_MEDIATEK || COMPILE_TEST
>>>>>>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
>>>>>>> index 12998b0..f7397ef 100644
>>>>>>> --- a/drivers/soc/mediatek/Makefile
>>>>>>> +++ b/drivers/soc/mediatek/Makefile
>>>>>>> @@ -1,3 +1,4 @@
>>>>>>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
>>>>>>>      obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
>>>>>>>      obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
>>>>>>>      obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
>>>>>>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..e9d6e1c
>>>>>>> --- /dev/null
>>>>>>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
>>>>>>> @@ -0,0 +1,943 @@
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>>>> + *
>>>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>>>> + * published by the Free Software Foundation.
>>>>>>> + *
>>>>>>> + * This program is distributed in the hope that it will be useful,
>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>>>> + * GNU General Public License for more details.
>>>>>>> + */
>>>>>>> +
>>>>>>> +#include <linux/clk.h>
>>>>>>> +#include <linux/clk-provider.h>
>>>>>>> +#include <linux/completion.h>
>>>>>>> +#include <linux/dma-mapping.h>
>>>>>>> +#include <linux/errno.h>
>>>>>>> +#include <linux/interrupt.h>
>>>>>>> +#include <linux/iopoll.h>
>>>>>>> +#include <linux/kernel.h>
>>>>>>> +#include <linux/kthread.h>
>>>>>>> +#include <linux/module.h>
>>>>>>> +#include <linux/mutex.h>
>>>>>>> +#include <linux/of_address.h>
>>>>>>> +#include <linux/of_irq.h>
>>>>>>> +#include <linux/platform_device.h>
>>>>>>> +#include <linux/slab.h>
>>>>>>> +#include <linux/spinlock.h>
>>>>>>> +#include <linux/suspend.h>
>>>>>>> +#include <linux/workqueue.h>
>>>>>>> +#include <soc/mediatek/cmdq.h>
>>>>>>> +
>>>>>>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
>>>>>>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
>>>>>>> +#define CMDQ_TIMEOUT_MS			1000
>>>>>>> +#define CMDQ_IRQ_MASK			0xffff
>>>>>>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
>>>>>>> +#define CMDQ_CLK_NAME			"gce"
>>>>>>
>>>>>> We can put the names in directly to un-bloat the defines.
>>>>>
>>>>> I will use the names directly and remove defines.
>>>>>
>>>>>>> +
>>>>>>> +#define CMDQ_CURR_IRQ_STATUS		0x010
>>>>>>> +#define CMDQ_CURR_LOADED_THR		0x018
>>>>>>> +#define CMDQ_THR_SLOT_CYCLES		0x030
>>>>>>> +
>>>>>>> +#define CMDQ_THR_BASE			0x100
>>>>>>> +#define CMDQ_THR_SHIFT			0x080
>>>>>>
>>>>>> Wouldn't be CMDQ_THR_SIZE more accurate?
>>>>>
>>>>> Will rename it.
>>>>>
>>>>>>> +#define CMDQ_THR_WARM_RESET		0x00
>>>>>>> +#define CMDQ_THR_ENABLE_TASK		0x04
>>>>>>> +#define CMDQ_THR_SUSPEND_TASK		0x08
>>>>>>> +#define CMDQ_THR_CURR_STATUS		0x0c
>>>>>>> +#define CMDQ_THR_IRQ_STATUS		0x10
>>>>>>> +#define CMDQ_THR_IRQ_ENABLE		0x14
>>>>>>> +#define CMDQ_THR_CURR_ADDR		0x20
>>>>>>> +#define CMDQ_THR_END_ADDR		0x24
>>>>>>> +#define CMDQ_THR_CFG			0x40
>>>>>>> +
>>>>>>> +#define CMDQ_THR_ENABLED		0x1
>>>>>>> +#define CMDQ_THR_DISABLED		0x0
>>>>>>> +#define CMDQ_THR_SUSPEND		0x1
>>>>>>> +#define CMDQ_THR_RESUME			0x0
>>>>>>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
>>>>>>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
>>>>>>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
>>>>>>> +#define CMDQ_THR_PRIORITY		3
>>>>>>> +#define CMDQ_THR_IRQ_DONE		0x1
>>>>>>> +#define CMDQ_THR_IRQ_ERROR		0x12
>>>>>>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
>>>>>>
>>>>>> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
>>>>>
>>>>> Will do.
>>>>>
>>>>>>> +#define CMDQ_THR_IRQ_MASK		0x13
>>>>>>
>>>>>> never used.
>>>>>
>>>>> Will remove.
>>>>>
>>>>>>> +#define CMDQ_THR_EXECUTING		BIT(31)
>>>>>>> +
>>>>>>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
>>>>>>> +#define CMDQ_SUBSYS_MASK		0x1f
>>>>>>> +#define CMDQ_OP_CODE_MASK		0xff000000
>>>>>>> +
>>>>>>> +#define CMDQ_OP_CODE_SHIFT		24
>>>>>>
>>>>>> Couldn't we connect the mask with the shift, or aren't they related?
>>>>>>
>>>>>> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
>>>>>
>>>>> Will do.
>>>>>
>>>>>>> +#define CMDQ_SUBSYS_SHIFT		16
>>>>>>> +
>>>>>>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
>>>>>>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
>>>>>>> +#define CMDQ_JUMP_BY_PA			0x10000001
>>>>>>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
>>>>>>> +#define CMDQ_WFE_UPDATE			BIT(31)
>>>>>>> +#define CMDQ_WFE_WAIT			BIT(15)
>>>>>>> +#define CMDQ_WFE_WAIT_VALUE		0x1
>>>>>>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
>>>>>>> +
>>>>>>> +enum cmdq_thread_index {
>>>>>>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
>>>>>>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
>>>>>>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
>>>>>>> +	CMDQ_THR_MAX_COUNT,	/* max */
>>>>>>> +};
>>>>>>> +
>>>>>>> +/*
>>>>>>> + * CMDQ_CODE_MOVE:
>>>>>>> + *   move value into internal register as mask
>>>>>>> + *   format: op mask
>>>>>>> + * CMDQ_CODE_WRITE:
>>>>>>> + *   write value into target register
>>>>>>> + *   format: op subsys address value
>>>>>>> + * CMDQ_CODE_JUMP:
>>>>>>> + *   jump by offset
>>>>>>> + *   format: op offset
>>>>>>> + * CMDQ_CODE_WFE:
>>>>>>> + *   wait for event and clear
>>>>>>> + *   it is just clear if no wait
>>>>>>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
>>>>>>> + *           [clear] op event update:1 to_wait:0 wait:0
>>>>>>> + * CMDQ_CODE_EOC:
>>>>>>> + *   end of command
>>>>>>> + *   format: op irq_flag
>>>>>>> + */
>>>>>>
>>>>>> I think we need more documentation of how this command queue engine is
>>>>>> working. If not, I think it will be really complicated to understand how
>>>>>> to use this.
>>>>>>
>>>>>>> +enum cmdq_code {
>>>>>>> +	CMDQ_CODE_MOVE = 0x02,
>>>>>>> +	CMDQ_CODE_WRITE = 0x04,
>>>>>>> +	CMDQ_CODE_JUMP = 0x10,
>>>>>>> +	CMDQ_CODE_WFE = 0x20,
>>>>>>> +	CMDQ_CODE_EOC = 0x40,
>>>>>>> +};
>>>>>>> +
>>>>>>> +enum cmdq_task_state {
>>>>>>> +	TASK_STATE_BUSY, /* running on a GCE thread */
>>>>>>> +	TASK_STATE_ERROR,
>>>>>>> +	TASK_STATE_DONE,
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq_task_cb {
>>>>>>> +	cmdq_async_flush_cb	cb;
>>>>>>> +	void			*data;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq_thread {
>>>>>>> +	void __iomem		*base;
>>>>>>> +	struct list_head	task_busy_list;
>>>>>>> +	wait_queue_head_t	wait_task_done;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq_task {
>>>>>>> +	struct cmdq		*cmdq;
>>>>>>> +	struct list_head	list_entry;
>>>>>>> +	enum cmdq_task_state	task_state;
>>>>>>> +	void			*va_base;
>>>>>>> +	dma_addr_t		pa_base;
>>>>>>> +	u64			engine_flag;
>>>>>>> +	size_t			command_size;
>>>>>>> +	u32			num_cmd;
>>>>>>
>>>>>> num_cmd is directly connected to command_size. I prefer to just keep
>>>>>> num_cmd and calculate the command_size where necessary.
>>>>>
>>>>> After I trace code, I prefer to keep command_size and calculate num_cmd
>>>>> where necessary. What do you think?
>>>>>
>>>>
>>>> I suppose you prefer this, as you are writing to the GCE depending on
>>>> the command_size. I think it is worth to create a macro for the
>>>> calculation of the number of commands, to make the code more readable.
>>>> Would be nice if you would just pass cmdq_task to it and it would return
>>>> the number. Just as an idea.
>>>
>>> Will add macro.
>>>
>>>>>>> +	struct cmdq_thread	*thread;
>>>>>>> +	struct cmdq_task_cb	cb;
>>>>>>> +	struct work_struct	release_work;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq {
>>>>>>> +	struct device		*dev;
>>>>>>> +	void __iomem		*base;
>>>>>>> +	u32			irq;
>>>>>>> +	struct workqueue_struct	*task_release_wq;
>>>>>>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
>>>>>>> +	struct mutex		task_mutex;	/* for task */
>>>>>>> +	spinlock_t		exec_lock;	/* for exec */
>>>>>>> +	struct clk		*clock;
>>>>>>> +	bool			suspended;
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq_subsys {
>>>>>>> +	u32	base;
>>>>>>> +	int	id;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static const struct cmdq_subsys gce_subsys[] = {
>>>>>>> +	{0x1400, 1},
>>>>>>> +	{0x1401, 2},
>>>>>>> +	{0x1402, 3},
>>>>>>> +};
>>>>>>> +
>>>>>>> +static int cmdq_subsys_base_to_id(u32 base)
>>>>>>> +{
>>>>>>> +	int i;
>>>>>>> +
>>>>>>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
>>>>>>> +		if (gce_subsys[i].base == base)
>>>>>>> +			return gce_subsys[i].id;
>>>>>>> +	return -EFAULT;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_eng_get_thread(u64 flag)
>>>>>>> +{
>>>>>>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
>>>>>>> +		return CMDQ_THR_DISP_MAIN_IDX;
>>>>>>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
>>>>>>> +		return CMDQ_THR_DISP_SUB_IDX;
>>>>>>> +	else
>>>>>>> +		return CMDQ_THR_DISP_MISC_IDX;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_task_release(struct cmdq_task *task)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>> +
>>>>>>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
>>>>>>> +			  task->pa_base);
>>>>>>> +	kfree(task);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
>>>>>>> +					   struct cmdq_task_cb cb)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>>>> +	struct device *dev = cmdq->dev;
>>>>>>> +	struct cmdq_task *task;
>>>>>>> +
>>>>>>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
>>>>>>> +	INIT_LIST_HEAD(&task->list_entry);
>>>>>>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
>>>>>>> +					   &task->pa_base, GFP_KERNEL);
>>>>>>> +	if (!task->va_base) {
>>>>>>> +		dev_err(dev, "allocate command buffer failed\n");
>>>>>>> +		kfree(task);
>>>>>>> +		return NULL;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	task->cmdq = cmdq;
>>>>>>> +	task->command_size = rec->command_size;
>>>>>>> +	task->engine_flag = rec->engine_flag;
>>>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>>>> +	task->cb = cb;
>>>>>>> +	memcpy(task->va_base, rec->buf, rec->command_size);
>>>>>>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
>>>>>>> +	return task;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
>>>>>>> +			       u32 offset)
>>>>>>> +{
>>>>>>> +	writel(value, thread->base + offset);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
>>>>>>> +{
>>>>>>> +	return readl(thread->base + offset);
>>>>>>> +}
>>>>>>
>>>>>> We can get rid of cmdq_thread_readl/writel.
>>>>>
>>>>> Will do.
>>>>>
>>>>>>> +
>>>>>>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	u32 status;
>>>>>>> +
>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
>>>>>>> +
>>>>>>> +	/* If already disabled, treat as suspended successful. */
>>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>> +	      CMDQ_THR_ENABLED))
>>>>>>> +		return 0;
>>>>>>> +
>>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
>>>>>>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
>>>>>>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
>>>>>>> +			(u32)(thread->base - cmdq->base));
>>>>>>> +		return -EFAULT;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	u32 warm_reset;
>>>>>>> +
>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
>>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
>>>>>>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
>>>>>>> +			0, 10)) {
>>>>>>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
>>>>>>> +			(u32)(thread->base - cmdq->base));
>>>>>>> +		return -EFAULT;
>>>>>>> +	}
>>>>>>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	cmdq_thread_reset(cmdq, thread);
>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
>>>>>>> +}
>>>>>>> +
>>>>>>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
>>>>>>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	cmdq_thread_writel(thread,
>>>>>>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
>>>>>>> +			   CMDQ_THR_CURR_ADDR);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
>>>>>>> +{
>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>> +	struct cmdq_task *prev_task = list_last_entry(
>>>>>>> +			&thread->task_busy_list, typeof(*task), list_entry);
>>>>>>> +	u64 *prev_task_base = prev_task->va_base;
>>>>>>> +
>>>>>>> +	/* let previous task jump to this task */
>>>>>>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
>>>>>>> +						 task->pa_base;
>>>>>>> +
>>>>>>> +	cmdq_thread_invalidate_fetched_data(thread);
>>>>>>> +}
>>>>>>> +
>>>>>>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
>>>>>>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
>>>>>>> +{
>>>>>>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>>>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
>>>>>>> +	u32 *base = task->va_base;
>>>>>>> +	u32 num_cmd = task->num_cmd << 1;
>>>>>>> +	int i;
>>>>>>> +
>>>>>>> +	for (i = 0; i < num_cmd; i += 2)
>>>>>>> +		if (base[i] == wfe_option &&
>>>>>>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
>>>>>>> +			base[i] = CMDQ_JUMP_PASS;
>>>>>>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
>>>>>>> +		}
>>>>>>
>>>>>> After using the command buffer as a void pointer a u64 pointer, we now
>>>>>> reference to it as u32. I would prefer to explain here, how the command
>>>>>> looks like we are searching for and use a for loop passing task->num_cmd
>>>>>> instead.
>>>>>
>>>>> Will use u64* to rewrite the above code.
>>>>>
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>> +	unsigned long flags;
>>>>>>> +	unsigned long curr_pa, end_pa;
>>>>>>> +
>>>>>>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>
>>>>>> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
>>>>>> the spin_lock here? Can't we just use one of the two?
>>>>>
>>>>> We can drop task_mutex, but we will get some side effects.
>>>>> 1. exec_lock needs to include more code, but I think it is not good for
>>>>>       spinlock.
>>>>> 2. In cmdq_rec_flush_async(), task_mutex needs to protect
>>>>>       (1) cmdq->suspended, (2) cmdq_task_exec(), and
>>>>>       (3) cmdq_task_wait_release_schedule().
>>>>>       If we drop task_mutex, we have to put cmdq->suspended if condition
>>>>>       just before cmdq_task_exec() and inside exec_lock, and we have to
>>>>>       release task and its command buffer if error. This will let flow
>>>>>       become more complex and enlarge code size.
>>>>>
>>>>> What do you think?
>>>>
>>>> Why do you need to protect cmdq_task_wait_release_schedule? We don't
>>>> care about the order of the workqueue elements, do we?
>>>
>>> According to CK's comment, we have to ensure order to remove previous
>>> task.
>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>>>
>>>> As far as I understand you would need to protect cmdq_task_acquire as
>>>> well, to "ensure" continously growing pa_base. More on that below.
>>>
>>> We need to ensure continuous physical addresses in a task, but we don't
>>> need to ensure continuous physical addresses between tasks.
>>> So, I think it is unnecessary to protect by mutex or spinlock.
>>>
>>
>> True, I didn't get that.
>>
>>>>>
>>>>>>> +	task->thread = thread;
>>>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>>>
>>>>>> That was already set in cmdq_task_acquire, why do we need to set it here
>>>>>> again?
>>>>>
>>>>> Will drop it.
>>>>>
>>>>
>>>> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire
>>>> instead.
>>>
>>> Will drop it in cmdq_task_acquire instead.
>>>
>>>>>>> +	if (list_empty(&thread->task_busy_list)) {
>>>>>>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
>>>>>>> +
>>>>>>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
>>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>>>> +				   CMDQ_THR_END_ADDR);
>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
>>>>>>> +				   CMDQ_THR_IRQ_ENABLE);
>>>>>>> +
>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
>>>>>>> +				   CMDQ_THR_ENABLE_TASK);
>>>>>>> +	} else {
>>>>>>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>>>> +
>>>>>>> +		/*
>>>>>>> +		 * check boundary condition
>>>>>>> +		 * PC = END - 8, EOC is executed
>>>>>>> +		 * PC = END, all CMDs are executed
>>>>>>> +		 */
>>>>>>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
>>>>>>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
>>>>>>
>>>>>> 8 refers to CMDQ_INST_SIZE, right?
>>>>>
>>>>> Yes, I will use CMDQ_INST_SIZE.
>>>>>
>>>>>>> +			/* set to this task directly */
>>>>>>> +			cmdq_thread_writel(thread, task->pa_base,
>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>> +		} else {
>>>>>>> +			cmdq_task_insert_into_thread(task);
>>>>>>> +
>>>>>>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
>>>>>>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
>>>>>>> +				cmdq_task_remove_wfe(task);
>>>>>>
>>>>>> We could do this check using the task->engine_flag, I suppose that's
>>>>>> easier to undestand then.
>>>>>
>>>>> Will use task->engine_flag.
>>>>>
>>>>>>> +
>>>>>>> +			smp_mb(); /* modify jump before enable thread */
>>>>>>> +		}
>>>>>>> +
>>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>>>> +				   CMDQ_THR_END_ADDR);
>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>> +	}
>>>>>>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>> +				   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>> +{
>>>>>>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>> +	u32 curr_pa;
>>>>>>> +	struct cmdq_cb_data cmdq_cb_data;
>>>>>>> +	bool err;
>>>>>>> +
>>>>>>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>> +		err = true;
>>>>>>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>> +		err = false;
>>>>>>> +	else
>>>>>>> +		return;
>>>>>>> +
>>>>>>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>> +
>>>>>>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>> +				 list_entry) {
>>>>>>> +		if (curr_pa >= task->pa_base &&
>>>>>>> +		    curr_pa < (task->pa_base + task->command_size))
>>>>>>
>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>> assumptions about pa_base and the order of execution of commands in the
>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
>>>>>> about dma_handle?
>>>>>
>>>>> 1. Check what is the current running task in this GCE thread.
>>>>> 2. Yes.
>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>
>>>>
>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>> guarantee that the dma_handle address is steadily growing, when calling
>>>> dma_alloc_coherent. And if I understand the code correctly, you use this
>>>> assumption to decide if the task picked from task_busy_list is currently
>>>> executing. So I think this mecanism is not working.
>>>
>>> I don't use dma_handle address, and just use physical addresses.
>>>   From CPU's point of view, tasks are linked by the busy list.
>>>   From GCE's point of view, tasks are linked by the JUMP command.
>>>
>>>> In which cases does the HW thread raise an interrupt.
>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>
>>> GCE will raise interrupt if any task is done or error.
>>> However, GCE is fast, so CPU may get multiple done tasks
>>> when it is running ISR.
>>>
>>> In case of error, that GCE thread will pause and raise interrupt.
>>> So, CPU may get multiple done tasks and one error task.
>>>
>>
>> I think we should reimplement the ISR mechanism. Can't we just read
>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>> cmdq_handle_error_done to the thread_fn? You will need to pass
>> information from the handler to thread_fn, but that shouldn't be an
>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>> there as short as possible. Traversing task_busy_list is expensive, so
>> we need to do it in a thread context.
>
> Actually, our initial implementation is similar to your suggestion,
> but display needs CMDQ to return callback function very precisely,
> else display will drop frame.
> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> and CMDQ needs to call callback function in ISR.
> If we defer callback to workqueue, the time interval may be larger than
> 32 ms.sometimes.
>

I think the problem is, that you implemented the workqueue as a ordered 
workqueue, so there is no parallel processing. I'm still not sure why 
you need the workqueue to be ordered. Can you please explain.

>> I keep thinking about how to get rid of the two data structures,
>> task_busy_list and the task_release_wq. We need the latter for the only
>> sake of getting a timeout.
>>
>> Did you have a look on how the mailbox framework handles this?
>> By the way, what is the reason to not implement the whole driver as a
>> mailbox controller? For me, this driver looks like a good fit.
>
> CMDQ needs to encode commands for GCE hardware. We think this behavior
> should be put in CMDQ driver, and client just call CMDQ functions.
> Therefore, if we want to use mailbox framework, cmdq_rec must be
> mailbox client, and the others must be mailbox controller.
>

You mean the functions to fill the cmdq_rec and execute it?
I think this should be part of the driver.

Jassi, can you have a look on the interface this driver exports [0].
They are needed to actually create the message which will be send.
Could something like this be part of a mailbox driver?

[0] https://patchwork.kernel.org/patch/9140221/

> However, if we use mailbox controller, CMDQ driver still needs to
> control busy list for each GCE thread, and use workqueue to handle
> timeout tasks.
>

Let me summarize my ideas around this driver:
When we enter the ISR, we know that all task in task_busy_list before 
the entry which represents curr_task can be set to TASK_STATE_DONE.
The curr_task could be TASK_STATE_ERROR if the corresponding bit in the 
irq status registers is set.
Do we need to call the callback in the same order as the tasks got 
dispatched to the HW thread? If not, we could try to call all this 
callbacks in a multithreaded workqueue.

Regards,
Matthias

> The only thing that we can borrow from mailbox framework is the send
> (CMDQ flush) and receive (CMDQ callback) interface, However, we don't
> think we can gain many benefits from it, and we have some overheads to
> conform to mailbox interface.
>
>
>>
>>>>>>> +			curr_task = task;
>>>>>>> +		if (task->cb.cb) {
>>>>>>> +			cmdq_cb_data.err = curr_task ? err : false;
>>>>>>> +			cmdq_cb_data.data = task->cb.data;
>>>>>>> +			task->cb.cb(cmdq_cb_data);
>>>>>>> +		}
>>>>>>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
>>>>>>> +				TASK_STATE_DONE;
>>>>>>> +		list_del(&task->list_entry);
>>>>>>> +		if (curr_task)
>>>>>>> +			break;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	wake_up(&thread->wait_task_done);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
>>>>>>> +{
>>>>>>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
>>>>>>> +	unsigned long flags = 0L;
>>>>>>> +	u32 irq_flag;
>>>>>>> +
>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>> +
>>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * Another CPU core could run "release task" right before we acquire
>>>>>>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
>>>>>>> +	 * need to check the enable bit of this GCE thread.
>>>>>>> +	 */
>>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>> +	      CMDQ_THR_ENABLED))
>>>>>>> +		irq_flag = 0;
>>>>>>
>>>>>> cmdq_handle_error_done just retuns in this case. Programming this way
>>>>>> just makes things confusing. What about:
>>>>>>
>>>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>> 	      CMDQ_THR_ENABLED)
>>>>>> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>> else
>>>>>> 		irq_flag = 0;
>>>>>>
>>>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>
>>>>> We still need to clear irq_flag if GCE thread is disabled.
>>>>> So, I think we can just return here.
>>>>>
>>>>> 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>> 	      CMDQ_THR_ENABLED))
>>>>> 		return;
>>>>>
>>>>> What do you think?
>>>>>
>>>>
>>>> No, you can't just return, you need to unlock the spinlock.
>>>> Anyway I would prefer it the other way round, as I put it in my last
>>>> mail. Just delete the else branch, we don't need to set irq_flag to zero.
>>>
>>> Sorry for my previous wrong reply since I merge your comment
>>> and CK's comment.
>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>>> So, I will put this if condition into cmdq_task_handle_error_result()
>>> and then just return it if GCE thread is disabled.
>>>
>>
>> You mean in cmdq_task_handle_done
>> We should rename this functions to not create confusion.
>
> Sorry again. I mean in cmdq_handle_error_done().
> This function handles both done and error.
>
> I agree the function name looks confusion.
> I think it can be renamed to cmdq_thread_irq_handler()
> since it is used to handle irq for GCE thread.
>
>> Regards,
>> Matthias
>
> Thanks,
> HS
>
>>>>>>> +
>>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = dev;
>>>>>>> +	u32 irq_status;
>>>>>>> +	int i;
>>>>>>> +
>>>>>>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
>>>>>>> +	irq_status &= CMDQ_IRQ_MASK;
>>>>>>> +	irq_status ^= CMDQ_IRQ_MASK;
>>>>>>
>>>>>> irq_status can be much bigger then 3, which is the number of threads in
>>>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
>>>>>> clear to me.
>>>>>
>>>>> Our GCE hardware has 16 threads, but we only use 3 threads currently.
>>>>>
>>>>
>>>> Ok, but please use bitops here.
>>>
>>> Will use bitops.
>>>
>>>>>>> +
>>>>>>> +	if (!irq_status)
>>>>>>> +		return IRQ_NONE;
>>>>>>> +
>>>>>>> +	while (irq_status) {
>>>>>>> +		i = ffs(irq_status) - 1;
>>>>>>> +		irq_status &= ~BIT(i);
>>>>>>> +		cmdq_thread_irq_handler(cmdq, i);
>>>>>>> +	}
>>>>>>
>>>>>> Can you explain how the irq status register looks like, that would it
>>>>>> make much easier to understand what happens here.
>>>>>
>>>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
>>>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
>>>>>
>>>>
>>>> Thanks, that helped. :)
>>>>
>>>>>>> +
>>>>>>> +	return IRQ_HANDLED;	
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
>>>>>>
>>>>>> We never check the return values, why do we have them?
>>>>>
>>>>> Will drop return value.
>>>>>
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>> +	struct device *dev = cmdq->dev;
>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>> +	struct cmdq_task *next_task, *prev_task;
>>>>>>> +	u32 irq_flag;
>>>>>>> +
>>>>>>> +	/* suspend GCE thread to ensure consistency */
>>>>>>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>>>> +
>>>>>>> +	/* ISR has handled this error task */
>>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>>>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
>>>>>>> +				struct cmdq_task, list_entry);
>>>>>>> +		if (next_task) /* move to next task */
>>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>
>>>>>> We have to do this, as we suppose that the thread did not reach the jump
>>>>>> instruction we put into it's command queue, right?
>>>>>
>>>>> Yes.
>>>>>
>>>>
>>>> So this should then go into it's own function. In wait_release_work,
>>>> something like this:
>>>>
>>>> if(task->task_state == TASK_STATE_ERROR)
>>>>         cmdq_task_handle_error(task)
>>>
>>> OK.
>>> I will write new function cmdq_task_handle_error() to handle error case.
>>>
>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>> +		return -ECANCELED;
>>>>>>> +	}
>>>>>>> +
>>>>>>
>>>>>> if task_state != ERROR and != DONE. This means that the timeout of
>>>>>> task_release_wq has timed out, right?
>>>>>
>>>>> Yes.
>>>>>
>>>>>>> +	/*
>>>>>>> +	 * Save next_task and prev_task in advance
>>>>>>> +	 * since cmdq_handle_error_done will remove list_entry.
>>>>>>> +	 */
>>>>>>> +	next_task = prev_task = NULL;
>>>>>>> +	if (task->list_entry.next != &thread->task_busy_list)
>>>>>>> +		next_task = list_next_entry(task, list_entry);
>>>>>>> +	if (task->list_entry.prev != &thread->task_busy_list)
>>>>>>> +		prev_task = list_prev_entry(task, list_entry);
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * Although IRQ is disabled, GCE continues to execute.
>>>>>>> +	 * It may have pending IRQ before GCE thread is suspended,
>>>>>>> +	 * so check this condition again.
>>>>>>> +	 */
>>>>>>
>>>>>> The first thing we did in this function was suspending the thread. Why
>>>>>> do we need this then?
>>>>>
>>>>> Because timeout is CPU timeout not GCE timeout, GCE could just finish
>>>>> this task before the GCE thread is suspended.
>>>>>
>>>>
>>>> What are the reasons for a timeout? An error has happend, or the task is
>>>> still executing.
>>>
>>>   From GCE's point of view, this task is still executing.
>>> But, it could be an error of client.
>>> For example, task may never get event if display turn off hardware
>>> before waiting for task to finish its work.
>>>
>>>>>> To be honest this whole functions looks really like a design error. We
>>>>>> have to sperate the states much clearer so that it is possible to
>>>>>> understand what is happening in the GCE. Isn't it for example posible to
>>>>>> have worker queues for timed out tasks and tasks with an error? I'm not
>>>>>> sure how to do this, actually I'm not sure if I really understood how
>>>>>> this is supposed to work.
>>>>>
>>>>> GCE doesn't have timeout. The timeout is decided and controlled by CPU,
>>>>> so we check timeout in release work.
>>>>> For error and done, they are easy to check by register, and we have
>>>>> already created release work for timeout. So, I don't think we need to
>>>>> create work queue for each case.
>>>>>
>>>>> What do you think?
>>>>>
>>>>
>>>> I think, if we find in here, that the irq_flag is set, then the the
>>>> interrupt handler was triggered and is spinning the spinlock. If this is
>>>> not the case, we have a timeout and we handle this.
>>>
>>> I will write another function to handle error, and handle timeout here.
>>>
>>>>>> How much do we win, when we patch the thread command queue for every
>>>>>> task we add, instead of just taking one task after another from the
>>>>>> task_busy_list?
>>>>>
>>>>> GCE is used to help read/write registers with critical time limitation.
>>>>> Sometimes, client may ask to process multiple tasks in a short period
>>>>> of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
>>>>> shouldn't limit to process one task after another from the
>>>>> task_busy_list. Currently, when interrupt or timeout, we will check
>>>>> how many tasks are done, and which one is error or timeout.
>>>>>
>>>>
>>>> So I suppose the device driver who use this are interested in throughput
>>>> and not in latency. The callback of every
>>>>
>>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>> +
>>>>>>> +	if (task->task_state == TASK_STATE_DONE) {
>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>> +		return 0;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>>>> +		dev_err(dev, "task 0x%p error\n", task);
>>>>>>> +		if (next_task) /* move to next task */
>>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>> +		return -ECANCELED;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	/* Task is running, so we force to remove it. */
>>>>>>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
>>>>>>> +	task->task_state = TASK_STATE_ERROR;
>>>>>>> +
>>>>>>> +	if (prev_task) {
>>>>>>> +		u64 *prev_va = prev_task->va_base;
>>>>>>> +		u64 *curr_va = task->va_base;
>>>>>>> +
>>>>>>> +		/* copy JUMP instruction */
>>>>>>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
>>>>>>> +
>>>>>>> +		cmdq_thread_invalidate_fetched_data(thread);
>>>>>>> +	} else if (next_task) { /* move to next task */
>>>>>>> +		cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>> +				   CMDQ_THR_CURR_ADDR);
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	list_del(&task->list_entry);
>>>>>>> +	cmdq_thread_resume(thread);
>>>>>>> +
>>>>>>> +	/* call cb here to prevent lock */
>>>>>>> +	if (task->cb.cb) {
>>>>>>> +		struct cmdq_cb_data cmdq_cb_data;
>>>>>>> +
>>>>>>> +		cmdq_cb_data.err = true;
>>>>>>> +		cmdq_cb_data.data = task->cb.data;
>>>>>>> +		task->cb.cb(cmdq_cb_data);
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	return -ECANCELED;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
>>>>>>> +{
>>>>>>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
>>>>>>> +					      release_work);
>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>> +	unsigned long flags;
>>>>>>> +
>>>>>>> +	wait_event_timeout(thread->wait_task_done,
>>>>>>> +			   task->task_state != TASK_STATE_BUSY,
>>>>>>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
>>>>>>> +
>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>> +	if (task->task_state != TASK_STATE_DONE)
>>>>>>> +		cmdq_task_handle_error_result(task);
>>>>>>> +	if (list_empty(&thread->task_busy_list))
>>>>>>> +		cmdq_thread_disable(cmdq, thread);
>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>> +
>>>>>>> +	/* release regardless of success or not */
>>>>>>> +	clk_disable_unprepare(cmdq->clock);
>>>>>>> +	cmdq_task_release(task);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>> +
>>>>>>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
>>>>>>> +	queue_work(cmdq->task_release_wq, &task->release_work);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
>>>>>>> +{
>>>>>>> +	void *new_buf;
>>>>>>> +
>>>>>>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
>>>>>>> +	if (!new_buf)
>>>>>>> +		return -ENOMEM;
>>>>>>> +	rec->buf = new_buf;
>>>>>>> +	rec->buf_size = size;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
>>>>>>> +{
>>>>>>> +	struct cmdq_base *cmdq_base;
>>>>>>> +	struct resource res;
>>>>>>> +	int subsys;
>>>>>>> +	u32 base;
>>>>>>> +
>>>>>>> +	if (of_address_to_resource(dev->of_node, 0, &res))
>>>>>>> +		return NULL;
>>>>>>> +	base = (u32)res.start;
>>>>>>> +
>>>>>>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
>>>>>>> +	if (subsys < 0)
>>>>>>> +		return NULL;
>>>>>>> +
>>>>>>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
>>>>>>> +	if (!cmdq_base)
>>>>>>> +		return NULL;
>>>>>>> +	cmdq_base->subsys = subsys;
>>>>>>> +	cmdq_base->base = base;
>>>>>>> +
>>>>>>> +	return cmdq_base;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_register_device);
>>>>>>> +
>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>> +		    struct cmdq_rec **rec_ptr)
>>>>>>> +{
>>>>>>> +	struct cmdq_rec *rec;
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
>>>>>>> +	if (!rec)
>>>>>>> +		return -ENOMEM;
>>>>>>> +	rec->cmdq = dev_get_drvdata(dev);
>>>>>>> +	rec->engine_flag = engine_flag;
>>>>>>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
>>>>>>
>>>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
>>>>>
>>>>> Will do.
>>>>>
>>>>>>> +	if (err < 0) {
>>>>>>> +		kfree(rec);
>>>>>>> +		return err;
>>>>>>> +	}
>>>>>>> +	*rec_ptr = rec;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
>>>>>>> +
>>>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
>>>>>>> +				   u32 arg_a, u32 arg_b)
>>>>>>> +{
>>>>>>> +	u64 *cmd_ptr;
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	if (WARN_ON(rec->finalized))
>>>>>>> +		return -EBUSY;
>>>>>>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
>>>>>>> +		return -EINVAL;
>>>>>>
>>>>>> cmdq_rec_append_command is just called from inside this driver and code
>>>>>> is a enum. We can expect it to be correct, no need for this check.
>>>>>
>>>>> Will drop this check.
>>>>>
>>>>>>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
>>>>>>
>>>>>> command_size is the offset into the buffer to which a new command is
>>>>>> written, so this name is highly confusing. I wonder if this would be
>>>>>> easier to understand if we redefine command_size to something like the
>>>>>> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
>>>>>
>>>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
>>>>> dividing CMDQ_INST_SIZE.
>>>>> What do you think?
>>>>>
>>>>>>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
>>>>>>> +		if (err < 0)
>>>>>>> +			return err;
>>>>>>> +	}
>>>>>>> +	cmd_ptr = rec->buf + rec->command_size;
>>>>>>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
>>>>>>> +	rec->command_size += CMDQ_INST_SIZE;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
>>>>>>> +		   u32 offset)
>>>>>>> +{
>>>>>>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
>>>>>>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
>>>>>>
>>>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
>>>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
>>>>>
>>>>> Will drop it.
>>>>>
>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
>>>>>>> +
>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask)
>>>>>>> +{
>>>>>>> +	u32 offset_mask = offset;
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	if (mask != 0xffffffff) {
>>>>>>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
>>>>>>> +		if (err < 0)
>>>>>>> +			return err;
>>>>>>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
>>>>>>> +	}
>>>>>>> +	return cmdq_rec_write(rec, value, base, offset_mask);
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
>>>>>>> +
>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
>>>>>>> +{
>>>>>>> +	u32 arg_b;
>>>>>>> +
>>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>> +		return -EINVAL;
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * bit 0-11: wait value
>>>>>>> +	 * bit 15: 1 - wait, 0 - no wait
>>>>>>> +	 * bit 16-27: update value
>>>>>>> +	 * bit 31: 1 - update, 0 - no update
>>>>>>> +	 */
>>>>>>
>>>>>> I don't understand this comments. What are they for?
>>>>>
>>>>> This is for WFE command. I will comment it.
>>>>>
>>>>>>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
>>>>>>> +
>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
>>>>>>> +{
>>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>> +		return -EINVAL;
>>>>>>> +
>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
>>>>>>> +				       CMDQ_WFE_UPDATE);
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
>>>>>>> +
>>>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
>>>>>>> +{
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	if (rec->finalized)
>>>>>>> +		return 0;
>>>>>>> +
>>>>>>> +	/* insert EOC and generate IRQ for each command iteration */
>>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
>>>>>>> +	if (err < 0)
>>>>>>> +		return err;
>>>>>>> +
>>>>>>> +	/* JUMP to end */
>>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
>>>>>>> +	if (err < 0)
>>>>>>> +		return err;
>>>>>>> +
>>>>>>
>>>>>> Does this need to be atomic?
>>>>>> What happens if after CODE_EOC and before CODE_JUMP some
>>>>>> write/read/event gets added?
>>>>>> What happens if more commands get added to the queue after CODE_JUMP,
>>>>>> but before finalized is set to true. Why don't you use atomic functions
>>>>>> to access finalized?
>>>>>
>>>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
>>>>> client uses cmdq_rec.
>>>>>
>>>>
>>>> Well I think that rec->finalized tries to implement this, but might
>>>> fail, if two kernel threads work on the same cmdq_rec.
>>>>
>>>>>>> +	rec->finalized = true;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>>>> +			 void *data)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>>>> +	struct cmdq_task *task;
>>>>>>> +	struct cmdq_task_cb task_cb;
>>>>>>> +	struct cmdq_thread *thread;
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>>>> +	if (cmdq->suspended) {
>>>>>>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>> +		return -EPERM;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	err = cmdq_rec_finalize(rec);
>>>>>>> +	if (err < 0) {
>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>> +		return err;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	task_cb.cb = cb;
>>>>>>> +	task_cb.data = data;
>>>>>>> +	task = cmdq_task_acquire(rec, task_cb);
>>>>>>> +	if (!task) {
>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>> +		return -EFAULT;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
>>>>>>> +	cmdq_task_exec(task, thread);
>>>>>>> +	cmdq_task_wait_release_schedule(task);
>>>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
>>>>>>> +
>>>>>>> +struct cmdq_flush_completion {
>>>>>>> +	struct completion cmplt;
>>>>>>> +	bool err;
>>>>>>> +};
>>>>>>> +
>>>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
>>>>>>> +{
>>>>>>> +	struct cmdq_flush_completion *cmplt = data.data;
>>>>>>> +
>>>>>>> +	cmplt->err = data.err;
>>>>>>> +	complete(&cmplt->cmplt);
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
>>>>>>> +{
>>>>>>> +	struct cmdq_flush_completion cmplt;
>>>>>>> +	int err;
>>>>>>> +
>>>>>>> +	init_completion(&cmplt.cmplt);
>>>>>>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
>>>>>>> +	if (err < 0)
>>>>>>> +		return err;
>>>>>>> +	wait_for_completion(&cmplt.cmplt);
>>>>>>> +	return cmplt.err ? -EFAULT : 0;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
>>>>>>> +
>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
>>>>>>> +{
>>>>>>> +	kfree(rec->buf);
>>>>>>> +	kfree(rec);
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
>>>>>>> +
>>>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
>>>>>>> +{
>>>>>>> +	struct cmdq_thread *thread;
>>>>>>> +	int i;
>>>>>>> +
>>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>> +		thread = &cmdq->thread[i];
>>>>>>> +		if (!list_empty(&thread->task_busy_list))
>>>>>>> +			return false;
>>>>>>> +	}
>>>>>>> +	return true;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_suspend(struct device *dev)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>> +	u32 exec_threads;
>>>>>>> +
>>>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>>>> +	cmdq->suspended = true;
>>>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>>>> +
>>>>>>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
>>>>>>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
>>>>>>> +		dev_err(dev, "wait active tasks timeout.\n");
>>>>>>> +		flush_workqueue(cmdq->task_release_wq);
>>>>>>> +	}
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_resume(struct device *dev)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>> +
>>>>>>> +	cmdq->suspended = false;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_remove(struct platform_device *pdev)
>>>>>>> +{
>>>>>>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
>>>>>>> +
>>>>>>> +	destroy_workqueue(cmdq->task_release_wq);
>>>>>>> +	cmdq->task_release_wq = NULL;
>>>>>>> +	return 0;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int cmdq_probe(struct platform_device *pdev)
>>>>>>> +{
>>>>>>> +	struct device *dev = &pdev->dev;
>>>>>>> +	struct device_node *node = dev->of_node;
>>>>>>> +	struct resource *res;
>>>>>>> +	struct cmdq *cmdq;
>>>>>>> +	int err, i;
>>>>>>> +
>>>>>>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
>>>>>>> +	if (!cmdq)
>>>>>>> +		return -ENOMEM;
>>>>>>> +	cmdq->dev = dev;
>>>>>>> +
>>>>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>>> +	cmdq->base = devm_ioremap_resource(dev, res);
>>>>>>> +	if (IS_ERR(cmdq->base)) {
>>>>>>> +		dev_err(dev, "failed to ioremap gce\n");
>>>>>>> +		return PTR_ERR(cmdq->base);
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
>>>>>>> +	if (!cmdq->irq) {
>>>>>>> +		dev_err(dev, "failed to get irq\n");
>>>>>>> +		return -EINVAL;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
>>>>>>> +		dev, cmdq->base, cmdq->irq);
>>>>>>> +
>>>>>>> +	mutex_init(&cmdq->task_mutex);
>>>>>>> +	spin_lock_init(&cmdq->exec_lock);
>>>>>>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
>>>>>>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
>>>>>>> +			"cmdq_task_wait_release");
>>>>>>> +
>>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
>>>>>>> +				CMDQ_THR_SHIFT * i;
>>>>>>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
>>>>>>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	platform_set_drvdata(pdev, cmdq);
>>>>>>> +
>>>>>>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
>>>>>>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
>>>>>>> +	if (err < 0) {
>>>>>>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
>>>>>>> +		goto fail;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
>>>>>>> +	if (IS_ERR(cmdq->clock)) {
>>>>>>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
>>>>>>> +		err = PTR_ERR(cmdq->clock);
>>>>>>> +		goto fail;
>>>>>>> +	}
>>>>>>> +	return 0;
>>>>>>> +
>>>>>>> +fail:
>>>>>>> +	cmdq_remove(pdev);
>>>>>>> +	return err;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
>>>>>>> +	.suspend = cmdq_suspend,
>>>>>>> +	.resume = cmdq_resume,
>>>>>>> +};
>>>>>>> +
>>>>>>> +static const struct of_device_id cmdq_of_ids[] = {
>>>>>>> +	{.compatible = "mediatek,mt8173-gce",},
>>>>>>> +	{}
>>>>>>> +};
>>>>>>> +
>>>>>>> +static struct platform_driver cmdq_drv = {
>>>>>>> +	.probe = cmdq_probe,
>>>>>>> +	.remove = cmdq_remove,
>>>>>>> +	.driver = {
>>>>>>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
>>>>>>> +		.owner = THIS_MODULE,
>>>>>>> +		.pm = &cmdq_pm_ops,
>>>>>>> +		.of_match_table = cmdq_of_ids,
>>>>>>> +	}
>>>>>>> +};
>>>>>>> +
>>>>>>> +builtin_platform_driver(cmdq_drv);
>>>>>>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
>>>>>>> new file mode 100644
>>>>>>> index 0000000..60eef3d
>>>>>>> --- /dev/null
>>>>>>> +++ b/include/soc/mediatek/cmdq.h
>>>>>>> @@ -0,0 +1,197 @@
>>>>>>> +/*
>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>>>> + *
>>>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>>>> + * published by the Free Software Foundation.
>>>>>>> + *
>>>>>>> + * This program is distributed in the hope that it will be useful,
>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>>>> + * GNU General Public License for more details.
>>>>>>> + */
>>>>>>> +
>>>>>>> +#ifndef __MTK_CMDQ_H__
>>>>>>> +#define __MTK_CMDQ_H__
>>>>>>> +
>>>>>>> +#include <linux/platform_device.h>
>>>>>>> +#include <linux/types.h>
>>>>>>> +
>>>>>>> +enum cmdq_eng {
>>>>>>> +	CMDQ_ENG_DISP_AAL,
>>>>>>> +	CMDQ_ENG_DISP_COLOR0,
>>>>>>> +	CMDQ_ENG_DISP_COLOR1,
>>>>>>> +	CMDQ_ENG_DISP_DPI0,
>>>>>>> +	CMDQ_ENG_DISP_DSI0,
>>>>>>> +	CMDQ_ENG_DISP_DSI1,
>>>>>>> +	CMDQ_ENG_DISP_GAMMA,
>>>>>>> +	CMDQ_ENG_DISP_OD,
>>>>>>> +	CMDQ_ENG_DISP_OVL0,
>>>>>>> +	CMDQ_ENG_DISP_OVL1,
>>>>>>> +	CMDQ_ENG_DISP_PWM0,
>>>>>>> +	CMDQ_ENG_DISP_PWM1,
>>>>>>> +	CMDQ_ENG_DISP_RDMA0,
>>>>>>> +	CMDQ_ENG_DISP_RDMA1,
>>>>>>> +	CMDQ_ENG_DISP_RDMA2,
>>>>>>> +	CMDQ_ENG_DISP_UFOE,
>>>>>>> +	CMDQ_ENG_DISP_WDMA0,
>>>>>>> +	CMDQ_ENG_DISP_WDMA1,
>>>>>>> +	CMDQ_ENG_MAX,
>>>>>>> +};
>>>>>>> +
>>>>>>> +/* events for CMDQ and display */
>>>>>>> +enum cmdq_event {
>>>>>>> +	/* Display start of frame(SOF) events */
>>>>>>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
>>>>>>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
>>>>>>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
>>>>>>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
>>>>>>> +	/* Display end of frame(EOF) events */
>>>>>>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
>>>>>>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
>>>>>>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
>>>>>>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
>>>>>>> +	/* Mutex end of frame(EOF) events */
>>>>>>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
>>>>>>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
>>>>>>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
>>>>>>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
>>>>>>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
>>>>>>> +	/* Display underrun events */
>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
>>>>>>> +	/* Keep this at the end of HW events */
>>>>>>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
>>>>>>> +};
>>>>>>> +
>>>>>>> +struct cmdq_cb_data {
>>>>>>> +	bool	err;
>>>>>>> +	void	*data;
>>>>>>> +};
>>>>>>> +
>>>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
>>>>>>> +
>>>>>>> +struct cmdq_task;
>>>>>>> +struct cmdq;
>>>>>>> +
>>>>>>> +struct cmdq_rec {
>>>>>>> +	struct cmdq		*cmdq;
>>>>>>> +	u64			engine_flag;
>>>>>>> +	size_t			command_size;
>>>>>>> +	void			*buf;
>>>>>>> +	size_t			buf_size;
>>>>>>> +	bool			finalized;
>>>>>>> +};
>>>>
>>>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that
>>>> and this way make the driver less complex?
>>>
>>> There are two main reasons for cmdq_rec.
>>> 1. It is slow to access dma too frequently.
>>>      So, we append commands to cacheable memory, and then flush to dma.
>>> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
>>>      If we merge them, we need to take care of some synchronization
>>>      issues.
>>>
>>>>>>> +
>>>>>>> +struct cmdq_base {
>>>>>>> +	int	subsys;
>>>>>>> +	u32	base;
>>>>>>
>>>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
>>>>>> so we can get rid of the struct, right?
>>>>>
>>>>> Current subsys method is based on previous comment from Daniel Kurtz.
>>>>> Please take a look of our previous discussion.
>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>>> Thanks.
>>>>>
>>>>
>>>> I have to look deeper into this, but from what I read, the proposal from
>>>> Daniel [1] seems good to me.
>>>>
>>>> [1] https://patchwork.kernel.org/patch/8068311/
>>>
>>> The initial proposal has some problem, so please see the follow-up
>>> discussions. Thanks.
>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>
>>>
>>>>>>> +};
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_register_device() - register device which needs CMDQ
>>>>>>> + * @dev:		device
>>>>>>> + *
>>>>>>> + * Return: cmdq_base pointer or NULL for failed
>>>>>>> + */
>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_create() - create command queue record
>>>>>>> + * @dev:		device
>>>>>>> + * @engine_flag:	command queue engine flag
>>>>>>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + */
>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>> +		    struct cmdq_rec **rec_ptr);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_write() - append write command to the command queue record
>>>>>>> + * @rec:	the command queue record
>>>>>>> + * @value:	the specified target register value
>>>>>>> + * @base:	the command queue base
>>>>>>> + * @offset:	register offset from module base
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + */
>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
>>>>>>> +		   struct cmdq_base *base, u32 offset);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_write_mask() - append write command with mask to the command
>>>>>>> + *			   queue record
>>>>>>> + * @rec:	the command queue record
>>>>>>> + * @value:	the specified target register value
>>>>>>> + * @base:	the command queue base
>>>>>>> + * @offset:	register offset from module base
>>>>>>> + * @mask:	the specified target register mask
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + */
>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
>>>>>>
>>>>>> reco rd -> record
>>>>>
>>>>> Will fix it.
>>>>>
>>>>>> Regards,
>>>>>> Matthias
>>>>>>
>>>>>>> + * @rec:	the command queue record
>>>>>>> + * @event:	the desired event type to "wait and CLEAR"
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + */
>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_clear_event() - append clear event command to the command queue
>>>>>>> + *			    record
>>>>>>> + * @rec:	the command queue record
>>>>>>> + * @event:	the desired event to be cleared
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + */
>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
>>>>>>> + * @rec:	the command queue record
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + *
>>>>>>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
>>>>>>> + * synchronous flush function. When the function returned, the recorded
>>>>>>> + * commands have been done.
>>>>>>> + */
>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
>>>>>>> + *			    commands and call back after ISR is finished
>>>>>>> + * @rec:	the command queue record
>>>>>>> + * @cb:		called in the end of CMDQ ISR
>>>>>>> + * @data:	this data will pass back to cb
>>>>>>> + *
>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>> + *
>>>>>>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
>>>>>>> + * after ISR is finished. Note that this is an ASYNC function. When the function
>>>>>>> + * returned, it may or may not be finished. The ISR callback function is called
>>>>>>> + * in the end of ISR.
>>>>
>>>> "The callback is called from the ISR."
>>>>
>>>> Regards,
>>>> Matthias
>>>>
>>>>>>> + */
>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>>>> +			 void *data);
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * cmdq_rec_destroy() - destroy command queue record
>>>>>>> + * @rec:	the command queue record
>>>>>>> + */
>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
>>>>>>> +
>>>>>>> +#endif	/* __MTK_CMDQ_H__ */
>>>>>>>
>>>>>
>>>>> Thanks,
>>>>> HS
>>>>>
>>>
>>> Thanks,
>>> HS
>>>
>
>


From hs.liao at mediatek.com  Fri Jun  3 05:13:57 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Fri, 3 Jun 2016 20:13:57 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <57516774.5080008@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com>
Message-ID: <1464956037.16029.8.camel@mtksdaap41>

Hi Matthias,

On Fri, 2016-06-03 at 13:18 +0200, Matthias Brugger wrote:
> 
> On 03/06/16 08:12, Horng-Shyang Liao wrote:
> > Hi Mathias,
> >
> > Please see my inline reply.
> >
> > On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
> >>
> >> On 01/06/16 11:57, Horng-Shyang Liao wrote:
> >>> Hi Mathias,
> >>>
> >>> Please see my inline reply.
> >>>
> >>> On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
> >>>>
> >>>> On 31/05/16 10:36, Horng-Shyang Liao wrote:
> >>>>> Hi Mathias,
> >>>>>
> >>>>> Please see my inline reply.
> >>>>>
> >>>>> On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
> >>>>>>
> >>>>>> On 30/05/16 05:19, HS Liao wrote:
> >>>>>>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> >>>>>>> CMDQ is used to help read/write registers with critical time limitation,
> >>>>>>> such as updating display configuration during the vblank. It controls
> >>>>>>> Global Command Engine (GCE) hardware to achieve this requirement.
> >>>>>>> Currently, CMDQ only supports display related hardwares, but we expect
> >>>>>>> it can be extended to other hardwares for future requirements.
> >>>>>>>
> >>>>>>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> >>>>>>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> >>>>>>> ---
> >>>>>>>      drivers/soc/mediatek/Kconfig    |  10 +
> >>>>>>>      drivers/soc/mediatek/Makefile   |   1 +
> >>>>>>>      drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> >>>>>>>      include/soc/mediatek/cmdq.h     | 197 +++++++++
> >>>>>>>      4 files changed, 1151 insertions(+)
> >>>>>>>      create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> >>>>>>>      create mode 100644 include/soc/mediatek/cmdq.h
> >>>>>>>
> >>>>>>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> >>>>>>> index 0a4ea80..c4ad75c 100644
> >>>>>>> --- a/drivers/soc/mediatek/Kconfig
> >>>>>>> +++ b/drivers/soc/mediatek/Kconfig
> >>>>>>> @@ -1,6 +1,16 @@
> >>>>>>>      #
> >>>>>>>      # MediaTek SoC drivers
> >>>>>>>      #
> >>>>>>> +config MTK_CMDQ
> >>>>>>> +	bool "MediaTek CMDQ Support"
> >>>>>>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> >>>>
> >>>> depends on ARM64 ?
> >>>
> >>> Will add ARM64.
> >>>
> >>>>>>> +	select MTK_INFRACFG
> >>>>>>> +	help
> >>>>>>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> >>>>>>> +	  driver. The CMDQ is used to help read/write registers with critical
> >>>>>>> +	  time limitation, such as updating display configuration during the
> >>>>>>> +	  vblank.
> >>>>>>> +
> >>>>>>>      config MTK_INFRACFG
> >>>>>>>      	bool "MediaTek INFRACFG Support"
> >>>>>>>      	depends on ARCH_MEDIATEK || COMPILE_TEST
> >>>>>>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> >>>>>>> index 12998b0..f7397ef 100644
> >>>>>>> --- a/drivers/soc/mediatek/Makefile
> >>>>>>> +++ b/drivers/soc/mediatek/Makefile
> >>>>>>> @@ -1,3 +1,4 @@
> >>>>>>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
> >>>>>>>      obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
> >>>>>>>      obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
> >>>>>>>      obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
> >>>>>>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
> >>>>>>> new file mode 100644
> >>>>>>> index 0000000..e9d6e1c
> >>>>>>> --- /dev/null
> >>>>>>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
> >>>>>>> @@ -0,0 +1,943 @@
> >>>>>>> +/*
> >>>>>>> + * Copyright (c) 2015 MediaTek Inc.
> >>>>>>> + *
> >>>>>>> + * This program is free software; you can redistribute it and/or modify
> >>>>>>> + * it under the terms of the GNU General Public License version 2 as
> >>>>>>> + * published by the Free Software Foundation.
> >>>>>>> + *
> >>>>>>> + * This program is distributed in the hope that it will be useful,
> >>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>>>>> + * GNU General Public License for more details.
> >>>>>>> + */
> >>>>>>> +
> >>>>>>> +#include <linux/clk.h>
> >>>>>>> +#include <linux/clk-provider.h>
> >>>>>>> +#include <linux/completion.h>
> >>>>>>> +#include <linux/dma-mapping.h>
> >>>>>>> +#include <linux/errno.h>
> >>>>>>> +#include <linux/interrupt.h>
> >>>>>>> +#include <linux/iopoll.h>
> >>>>>>> +#include <linux/kernel.h>
> >>>>>>> +#include <linux/kthread.h>
> >>>>>>> +#include <linux/module.h>
> >>>>>>> +#include <linux/mutex.h>
> >>>>>>> +#include <linux/of_address.h>
> >>>>>>> +#include <linux/of_irq.h>
> >>>>>>> +#include <linux/platform_device.h>
> >>>>>>> +#include <linux/slab.h>
> >>>>>>> +#include <linux/spinlock.h>
> >>>>>>> +#include <linux/suspend.h>
> >>>>>>> +#include <linux/workqueue.h>
> >>>>>>> +#include <soc/mediatek/cmdq.h>
> >>>>>>> +
> >>>>>>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
> >>>>>>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
> >>>>>>> +#define CMDQ_TIMEOUT_MS			1000
> >>>>>>> +#define CMDQ_IRQ_MASK			0xffff
> >>>>>>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
> >>>>>>> +#define CMDQ_CLK_NAME			"gce"
> >>>>>>
> >>>>>> We can put the names in directly to un-bloat the defines.
> >>>>>
> >>>>> I will use the names directly and remove defines.
> >>>>>
> >>>>>>> +
> >>>>>>> +#define CMDQ_CURR_IRQ_STATUS		0x010
> >>>>>>> +#define CMDQ_CURR_LOADED_THR		0x018
> >>>>>>> +#define CMDQ_THR_SLOT_CYCLES		0x030
> >>>>>>> +
> >>>>>>> +#define CMDQ_THR_BASE			0x100
> >>>>>>> +#define CMDQ_THR_SHIFT			0x080
> >>>>>>
> >>>>>> Wouldn't be CMDQ_THR_SIZE more accurate?
> >>>>>
> >>>>> Will rename it.
> >>>>>
> >>>>>>> +#define CMDQ_THR_WARM_RESET		0x00
> >>>>>>> +#define CMDQ_THR_ENABLE_TASK		0x04
> >>>>>>> +#define CMDQ_THR_SUSPEND_TASK		0x08
> >>>>>>> +#define CMDQ_THR_CURR_STATUS		0x0c
> >>>>>>> +#define CMDQ_THR_IRQ_STATUS		0x10
> >>>>>>> +#define CMDQ_THR_IRQ_ENABLE		0x14
> >>>>>>> +#define CMDQ_THR_CURR_ADDR		0x20
> >>>>>>> +#define CMDQ_THR_END_ADDR		0x24
> >>>>>>> +#define CMDQ_THR_CFG			0x40
> >>>>>>> +
> >>>>>>> +#define CMDQ_THR_ENABLED		0x1
> >>>>>>> +#define CMDQ_THR_DISABLED		0x0
> >>>>>>> +#define CMDQ_THR_SUSPEND		0x1
> >>>>>>> +#define CMDQ_THR_RESUME			0x0
> >>>>>>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
> >>>>>>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
> >>>>>>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
> >>>>>>> +#define CMDQ_THR_PRIORITY		3
> >>>>>>> +#define CMDQ_THR_IRQ_DONE		0x1
> >>>>>>> +#define CMDQ_THR_IRQ_ERROR		0x12
> >>>>>>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
> >>>>>>
> >>>>>> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
> >>>>>
> >>>>> Will do.
> >>>>>
> >>>>>>> +#define CMDQ_THR_IRQ_MASK		0x13
> >>>>>>
> >>>>>> never used.
> >>>>>
> >>>>> Will remove.
> >>>>>
> >>>>>>> +#define CMDQ_THR_EXECUTING		BIT(31)
> >>>>>>> +
> >>>>>>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
> >>>>>>> +#define CMDQ_SUBSYS_MASK		0x1f
> >>>>>>> +#define CMDQ_OP_CODE_MASK		0xff000000
> >>>>>>> +
> >>>>>>> +#define CMDQ_OP_CODE_SHIFT		24
> >>>>>>
> >>>>>> Couldn't we connect the mask with the shift, or aren't they related?
> >>>>>>
> >>>>>> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
> >>>>>
> >>>>> Will do.
> >>>>>
> >>>>>>> +#define CMDQ_SUBSYS_SHIFT		16
> >>>>>>> +
> >>>>>>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
> >>>>>>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
> >>>>>>> +#define CMDQ_JUMP_BY_PA			0x10000001
> >>>>>>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
> >>>>>>> +#define CMDQ_WFE_UPDATE			BIT(31)
> >>>>>>> +#define CMDQ_WFE_WAIT			BIT(15)
> >>>>>>> +#define CMDQ_WFE_WAIT_VALUE		0x1
> >>>>>>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
> >>>>>>> +
> >>>>>>> +enum cmdq_thread_index {
> >>>>>>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
> >>>>>>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
> >>>>>>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
> >>>>>>> +	CMDQ_THR_MAX_COUNT,	/* max */
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +/*
> >>>>>>> + * CMDQ_CODE_MOVE:
> >>>>>>> + *   move value into internal register as mask
> >>>>>>> + *   format: op mask
> >>>>>>> + * CMDQ_CODE_WRITE:
> >>>>>>> + *   write value into target register
> >>>>>>> + *   format: op subsys address value
> >>>>>>> + * CMDQ_CODE_JUMP:
> >>>>>>> + *   jump by offset
> >>>>>>> + *   format: op offset
> >>>>>>> + * CMDQ_CODE_WFE:
> >>>>>>> + *   wait for event and clear
> >>>>>>> + *   it is just clear if no wait
> >>>>>>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
> >>>>>>> + *           [clear] op event update:1 to_wait:0 wait:0
> >>>>>>> + * CMDQ_CODE_EOC:
> >>>>>>> + *   end of command
> >>>>>>> + *   format: op irq_flag
> >>>>>>> + */
> >>>>>>
> >>>>>> I think we need more documentation of how this command queue engine is
> >>>>>> working. If not, I think it will be really complicated to understand how
> >>>>>> to use this.
> >>>>>>
> >>>>>>> +enum cmdq_code {
> >>>>>>> +	CMDQ_CODE_MOVE = 0x02,
> >>>>>>> +	CMDQ_CODE_WRITE = 0x04,
> >>>>>>> +	CMDQ_CODE_JUMP = 0x10,
> >>>>>>> +	CMDQ_CODE_WFE = 0x20,
> >>>>>>> +	CMDQ_CODE_EOC = 0x40,
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +enum cmdq_task_state {
> >>>>>>> +	TASK_STATE_BUSY, /* running on a GCE thread */
> >>>>>>> +	TASK_STATE_ERROR,
> >>>>>>> +	TASK_STATE_DONE,
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq_task_cb {
> >>>>>>> +	cmdq_async_flush_cb	cb;
> >>>>>>> +	void			*data;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq_thread {
> >>>>>>> +	void __iomem		*base;
> >>>>>>> +	struct list_head	task_busy_list;
> >>>>>>> +	wait_queue_head_t	wait_task_done;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq_task {
> >>>>>>> +	struct cmdq		*cmdq;
> >>>>>>> +	struct list_head	list_entry;
> >>>>>>> +	enum cmdq_task_state	task_state;
> >>>>>>> +	void			*va_base;
> >>>>>>> +	dma_addr_t		pa_base;
> >>>>>>> +	u64			engine_flag;
> >>>>>>> +	size_t			command_size;
> >>>>>>> +	u32			num_cmd;
> >>>>>>
> >>>>>> num_cmd is directly connected to command_size. I prefer to just keep
> >>>>>> num_cmd and calculate the command_size where necessary.
> >>>>>
> >>>>> After I trace code, I prefer to keep command_size and calculate num_cmd
> >>>>> where necessary. What do you think?
> >>>>>
> >>>>
> >>>> I suppose you prefer this, as you are writing to the GCE depending on
> >>>> the command_size. I think it is worth to create a macro for the
> >>>> calculation of the number of commands, to make the code more readable.
> >>>> Would be nice if you would just pass cmdq_task to it and it would return
> >>>> the number. Just as an idea.
> >>>
> >>> Will add macro.
> >>>
> >>>>>>> +	struct cmdq_thread	*thread;
> >>>>>>> +	struct cmdq_task_cb	cb;
> >>>>>>> +	struct work_struct	release_work;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq {
> >>>>>>> +	struct device		*dev;
> >>>>>>> +	void __iomem		*base;
> >>>>>>> +	u32			irq;
> >>>>>>> +	struct workqueue_struct	*task_release_wq;
> >>>>>>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
> >>>>>>> +	struct mutex		task_mutex;	/* for task */
> >>>>>>> +	spinlock_t		exec_lock;	/* for exec */
> >>>>>>> +	struct clk		*clock;
> >>>>>>> +	bool			suspended;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq_subsys {
> >>>>>>> +	u32	base;
> >>>>>>> +	int	id;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +static const struct cmdq_subsys gce_subsys[] = {
> >>>>>>> +	{0x1400, 1},
> >>>>>>> +	{0x1401, 2},
> >>>>>>> +	{0x1402, 3},
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +static int cmdq_subsys_base_to_id(u32 base)
> >>>>>>> +{
> >>>>>>> +	int i;
> >>>>>>> +
> >>>>>>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
> >>>>>>> +		if (gce_subsys[i].base == base)
> >>>>>>> +			return gce_subsys[i].id;
> >>>>>>> +	return -EFAULT;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_eng_get_thread(u64 flag)
> >>>>>>> +{
> >>>>>>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> >>>>>>> +		return CMDQ_THR_DISP_MAIN_IDX;
> >>>>>>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> >>>>>>> +		return CMDQ_THR_DISP_SUB_IDX;
> >>>>>>> +	else
> >>>>>>> +		return CMDQ_THR_DISP_MISC_IDX;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_task_release(struct cmdq_task *task)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>>>> +
> >>>>>>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
> >>>>>>> +			  task->pa_base);
> >>>>>>> +	kfree(task);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
> >>>>>>> +					   struct cmdq_task_cb cb)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = rec->cmdq;
> >>>>>>> +	struct device *dev = cmdq->dev;
> >>>>>>> +	struct cmdq_task *task;
> >>>>>>> +
> >>>>>>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
> >>>>>>> +	INIT_LIST_HEAD(&task->list_entry);
> >>>>>>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
> >>>>>>> +					   &task->pa_base, GFP_KERNEL);
> >>>>>>> +	if (!task->va_base) {
> >>>>>>> +		dev_err(dev, "allocate command buffer failed\n");
> >>>>>>> +		kfree(task);
> >>>>>>> +		return NULL;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	task->cmdq = cmdq;
> >>>>>>> +	task->command_size = rec->command_size;
> >>>>>>> +	task->engine_flag = rec->engine_flag;
> >>>>>>> +	task->task_state = TASK_STATE_BUSY;
> >>>>>>> +	task->cb = cb;
> >>>>>>> +	memcpy(task->va_base, rec->buf, rec->command_size);
> >>>>>>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
> >>>>>>> +	return task;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
> >>>>>>> +			       u32 offset)
> >>>>>>> +{
> >>>>>>> +	writel(value, thread->base + offset);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
> >>>>>>> +{
> >>>>>>> +	return readl(thread->base + offset);
> >>>>>>> +}
> >>>>>>
> >>>>>> We can get rid of cmdq_thread_readl/writel.
> >>>>>
> >>>>> Will do.
> >>>>>
> >>>>>>> +
> >>>>>>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	u32 status;
> >>>>>>> +
> >>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
> >>>>>>> +
> >>>>>>> +	/* If already disabled, treat as suspended successful. */
> >>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>>> +	      CMDQ_THR_ENABLED))
> >>>>>>> +		return 0;
> >>>>>>> +
> >>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
> >>>>>>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
> >>>>>>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
> >>>>>>> +			(u32)(thread->base - cmdq->base));
> >>>>>>> +		return -EFAULT;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	u32 warm_reset;
> >>>>>>> +
> >>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
> >>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
> >>>>>>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
> >>>>>>> +			0, 10)) {
> >>>>>>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
> >>>>>>> +			(u32)(thread->base - cmdq->base));
> >>>>>>> +		return -EFAULT;
> >>>>>>> +	}
> >>>>>>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	cmdq_thread_reset(cmdq, thread);
> >>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
> >>>>>>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	cmdq_thread_writel(thread,
> >>>>>>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
> >>>>>>> +			   CMDQ_THR_CURR_ADDR);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>>>> +	struct cmdq_task *prev_task = list_last_entry(
> >>>>>>> +			&thread->task_busy_list, typeof(*task), list_entry);
> >>>>>>> +	u64 *prev_task_base = prev_task->va_base;
> >>>>>>> +
> >>>>>>> +	/* let previous task jump to this task */
> >>>>>>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
> >>>>>>> +						 task->pa_base;
> >>>>>>> +
> >>>>>>> +	cmdq_thread_invalidate_fetched_data(thread);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
> >>>>>>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
> >>>>>>> +{
> >>>>>>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>>>>>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
> >>>>>>> +	u32 *base = task->va_base;
> >>>>>>> +	u32 num_cmd = task->num_cmd << 1;
> >>>>>>> +	int i;
> >>>>>>> +
> >>>>>>> +	for (i = 0; i < num_cmd; i += 2)
> >>>>>>> +		if (base[i] == wfe_option &&
> >>>>>>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
> >>>>>>> +			base[i] = CMDQ_JUMP_PASS;
> >>>>>>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
> >>>>>>> +		}
> >>>>>>
> >>>>>> After using the command buffer as a void pointer a u64 pointer, we now
> >>>>>> reference to it as u32. I would prefer to explain here, how the command
> >>>>>> looks like we are searching for and use a for loop passing task->num_cmd
> >>>>>> instead.
> >>>>>
> >>>>> Will use u64* to rewrite the above code.
> >>>>>
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>>>> +	unsigned long flags;
> >>>>>>> +	unsigned long curr_pa, end_pa;
> >>>>>>> +
> >>>>>>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
> >>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>>>
> >>>>>> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
> >>>>>> the spin_lock here? Can't we just use one of the two?
> >>>>>
> >>>>> We can drop task_mutex, but we will get some side effects.
> >>>>> 1. exec_lock needs to include more code, but I think it is not good for
> >>>>>       spinlock.
> >>>>> 2. In cmdq_rec_flush_async(), task_mutex needs to protect
> >>>>>       (1) cmdq->suspended, (2) cmdq_task_exec(), and
> >>>>>       (3) cmdq_task_wait_release_schedule().
> >>>>>       If we drop task_mutex, we have to put cmdq->suspended if condition
> >>>>>       just before cmdq_task_exec() and inside exec_lock, and we have to
> >>>>>       release task and its command buffer if error. This will let flow
> >>>>>       become more complex and enlarge code size.
> >>>>>
> >>>>> What do you think?
> >>>>
> >>>> Why do you need to protect cmdq_task_wait_release_schedule? We don't
> >>>> care about the order of the workqueue elements, do we?
> >>>
> >>> According to CK's comment, we have to ensure order to remove previous
> >>> task.
> >>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> >>>
> >>>> As far as I understand you would need to protect cmdq_task_acquire as
> >>>> well, to "ensure" continously growing pa_base. More on that below.
> >>>
> >>> We need to ensure continuous physical addresses in a task, but we don't
> >>> need to ensure continuous physical addresses between tasks.
> >>> So, I think it is unnecessary to protect by mutex or spinlock.
> >>>
> >>
> >> True, I didn't get that.
> >>
> >>>>>
> >>>>>>> +	task->thread = thread;
> >>>>>>> +	task->task_state = TASK_STATE_BUSY;
> >>>>>>
> >>>>>> That was already set in cmdq_task_acquire, why do we need to set it here
> >>>>>> again?
> >>>>>
> >>>>> Will drop it.
> >>>>>
> >>>>
> >>>> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire
> >>>> instead.
> >>>
> >>> Will drop it in cmdq_task_acquire instead.
> >>>
> >>>>>>> +	if (list_empty(&thread->task_busy_list)) {
> >>>>>>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
> >>>>>>> +
> >>>>>>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
> >>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>>>>>> +				   CMDQ_THR_END_ADDR);
> >>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
> >>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
> >>>>>>> +				   CMDQ_THR_IRQ_ENABLE);
> >>>>>>> +
> >>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
> >>>>>>> +				   CMDQ_THR_ENABLE_TASK);
> >>>>>>> +	} else {
> >>>>>>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>>>>>> +
> >>>>>>> +		/*
> >>>>>>> +		 * check boundary condition
> >>>>>>> +		 * PC = END - 8, EOC is executed
> >>>>>>> +		 * PC = END, all CMDs are executed
> >>>>>>> +		 */
> >>>>>>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
> >>>>>>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
> >>>>>>
> >>>>>> 8 refers to CMDQ_INST_SIZE, right?
> >>>>>
> >>>>> Yes, I will use CMDQ_INST_SIZE.
> >>>>>
> >>>>>>> +			/* set to this task directly */
> >>>>>>> +			cmdq_thread_writel(thread, task->pa_base,
> >>>>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>>>> +		} else {
> >>>>>>> +			cmdq_task_insert_into_thread(task);
> >>>>>>> +
> >>>>>>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
> >>>>>>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
> >>>>>>> +				cmdq_task_remove_wfe(task);
> >>>>>>
> >>>>>> We could do this check using the task->engine_flag, I suppose that's
> >>>>>> easier to undestand then.
> >>>>>
> >>>>> Will use task->engine_flag.
> >>>>>
> >>>>>>> +
> >>>>>>> +			smp_mb(); /* modify jump before enable thread */
> >>>>>>> +		}
> >>>>>>> +
> >>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
> >>>>>>> +				   CMDQ_THR_END_ADDR);
> >>>>>>> +		cmdq_thread_resume(thread);
> >>>>>>> +	}
> >>>>>>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>> +				   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>> +	u32 curr_pa;
> >>>>>>> +	struct cmdq_cb_data cmdq_cb_data;
> >>>>>>> +	bool err;
> >>>>>>> +
> >>>>>>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>> +		err = true;
> >>>>>>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>> +		err = false;
> >>>>>>> +	else
> >>>>>>> +		return;
> >>>>>>> +
> >>>>>>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>> +
> >>>>>>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>> +				 list_entry) {
> >>>>>>> +		if (curr_pa >= task->pa_base &&
> >>>>>>> +		    curr_pa < (task->pa_base + task->command_size))
> >>>>>>
> >>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>> assumptions about pa_base and the order of execution of commands in the
> >>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
> >>>>>> about dma_handle?
> >>>>>
> >>>>> 1. Check what is the current running task in this GCE thread.
> >>>>> 2. Yes.
> >>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>
> >>>>
> >>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>> guarantee that the dma_handle address is steadily growing, when calling
> >>>> dma_alloc_coherent. And if I understand the code correctly, you use this
> >>>> assumption to decide if the task picked from task_busy_list is currently
> >>>> executing. So I think this mecanism is not working.
> >>>
> >>> I don't use dma_handle address, and just use physical addresses.
> >>>   From CPU's point of view, tasks are linked by the busy list.
> >>>   From GCE's point of view, tasks are linked by the JUMP command.
> >>>
> >>>> In which cases does the HW thread raise an interrupt.
> >>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>
> >>> GCE will raise interrupt if any task is done or error.
> >>> However, GCE is fast, so CPU may get multiple done tasks
> >>> when it is running ISR.
> >>>
> >>> In case of error, that GCE thread will pause and raise interrupt.
> >>> So, CPU may get multiple done tasks and one error task.
> >>>
> >>
> >> I think we should reimplement the ISR mechanism. Can't we just read
> >> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >> cmdq_handle_error_done to the thread_fn? You will need to pass
> >> information from the handler to thread_fn, but that shouldn't be an
> >> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >> there as short as possible. Traversing task_busy_list is expensive, so
> >> we need to do it in a thread context.
> >
> > Actually, our initial implementation is similar to your suggestion,
> > but display needs CMDQ to return callback function very precisely,
> > else display will drop frame.
> > For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> > and CMDQ needs to call callback function in ISR.
> > If we defer callback to workqueue, the time interval may be larger than
> > 32 ms.sometimes.
> >
> 
> I think the problem is, that you implemented the workqueue as a ordered 
> workqueue, so there is no parallel processing. I'm still not sure why 
> you need the workqueue to be ordered. Can you please explain.

The order should be kept.
Let me use mouse cursor as an example.
If task 1 means move mouse cursor to point A, task 2 means point B,
and task 3 means point C, our expected result is A -> B -> C.
If the order is not kept, the result could become A -> C -> B.

> >> I keep thinking about how to get rid of the two data structures,
> >> task_busy_list and the task_release_wq. We need the latter for the only
> >> sake of getting a timeout.
> >>
> >> Did you have a look on how the mailbox framework handles this?
> >> By the way, what is the reason to not implement the whole driver as a
> >> mailbox controller? For me, this driver looks like a good fit.
> >
> > CMDQ needs to encode commands for GCE hardware. We think this behavior
> > should be put in CMDQ driver, and client just call CMDQ functions.
> > Therefore, if we want to use mailbox framework, cmdq_rec must be
> > mailbox client, and the others must be mailbox controller.
> >
> 
> You mean the functions to fill the cmdq_rec and execute it?
> I think this should be part of the driver.

Yes.

> Jassi, can you have a look on the interface this driver exports [0].
> They are needed to actually create the message which will be send.
> Could something like this be part of a mailbox driver?
> 
> [0] https://patchwork.kernel.org/patch/9140221/
> 
> > However, if we use mailbox controller, CMDQ driver still needs to
> > control busy list for each GCE thread, and use workqueue to handle
> > timeout tasks.
> >
> 
> Let me summarize my ideas around this driver:
> When we enter the ISR, we know that all task in task_busy_list before 
> the entry which represents curr_task can be set to TASK_STATE_DONE.
> The curr_task could be TASK_STATE_ERROR if the corresponding bit in the 
> irq status registers is set.
> Do we need to call the callback in the same order as the tasks got 
> dispatched to the HW thread? If not, we could try to call all this 
> callbacks in a multithreaded workqueue.

Yes, we should keep order.

> Regards,
> Matthias

Thanks,
HS

> > The only thing that we can borrow from mailbox framework is the send
> > (CMDQ flush) and receive (CMDQ callback) interface, However, we don't
> > think we can gain many benefits from it, and we have some overheads to
> > conform to mailbox interface.
> >
> >
> >>
> >>>>>>> +			curr_task = task;
> >>>>>>> +		if (task->cb.cb) {
> >>>>>>> +			cmdq_cb_data.err = curr_task ? err : false;
> >>>>>>> +			cmdq_cb_data.data = task->cb.data;
> >>>>>>> +			task->cb.cb(cmdq_cb_data);
> >>>>>>> +		}
> >>>>>>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
> >>>>>>> +				TASK_STATE_DONE;
> >>>>>>> +		list_del(&task->list_entry);
> >>>>>>> +		if (curr_task)
> >>>>>>> +			break;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	wake_up(&thread->wait_task_done);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
> >>>>>>> +	unsigned long flags = 0L;
> >>>>>>> +	u32 irq_flag;
> >>>>>>> +
> >>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>>>> +
> >>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>>>> +
> >>>>>>> +	/*
> >>>>>>> +	 * Another CPU core could run "release task" right before we acquire
> >>>>>>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
> >>>>>>> +	 * need to check the enable bit of this GCE thread.
> >>>>>>> +	 */
> >>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>>> +	      CMDQ_THR_ENABLED))
> >>>>>>> +		irq_flag = 0;
> >>>>>>
> >>>>>> cmdq_handle_error_done just retuns in this case. Programming this way
> >>>>>> just makes things confusing. What about:
> >>>>>>
> >>>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>> 	      CMDQ_THR_ENABLED)
> >>>>>> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>> else
> >>>>>> 		irq_flag = 0;
> >>>>>>
> >>>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>
> >>>>> We still need to clear irq_flag if GCE thread is disabled.
> >>>>> So, I think we can just return here.
> >>>>>
> >>>>> 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>> 	      CMDQ_THR_ENABLED))
> >>>>> 		return;
> >>>>>
> >>>>> What do you think?
> >>>>>
> >>>>
> >>>> No, you can't just return, you need to unlock the spinlock.
> >>>> Anyway I would prefer it the other way round, as I put it in my last
> >>>> mail. Just delete the else branch, we don't need to set irq_flag to zero.
> >>>
> >>> Sorry for my previous wrong reply since I merge your comment
> >>> and CK's comment.
> >>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> >>> So, I will put this if condition into cmdq_task_handle_error_result()
> >>> and then just return it if GCE thread is disabled.
> >>>
> >>
> >> You mean in cmdq_task_handle_done
> >> We should rename this functions to not create confusion.
> >
> > Sorry again. I mean in cmdq_handle_error_done().
> > This function handles both done and error.
> >
> > I agree the function name looks confusion.
> > I think it can be renamed to cmdq_thread_irq_handler()
> > since it is used to handle irq for GCE thread.
> >
> >> Regards,
> >> Matthias
> >
> > Thanks,
> > HS
> >
> >>>>>>> +
> >>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = dev;
> >>>>>>> +	u32 irq_status;
> >>>>>>> +	int i;
> >>>>>>> +
> >>>>>>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
> >>>>>>> +	irq_status &= CMDQ_IRQ_MASK;
> >>>>>>> +	irq_status ^= CMDQ_IRQ_MASK;
> >>>>>>
> >>>>>> irq_status can be much bigger then 3, which is the number of threads in
> >>>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
> >>>>>> clear to me.
> >>>>>
> >>>>> Our GCE hardware has 16 threads, but we only use 3 threads currently.
> >>>>>
> >>>>
> >>>> Ok, but please use bitops here.
> >>>
> >>> Will use bitops.
> >>>
> >>>>>>> +
> >>>>>>> +	if (!irq_status)
> >>>>>>> +		return IRQ_NONE;
> >>>>>>> +
> >>>>>>> +	while (irq_status) {
> >>>>>>> +		i = ffs(irq_status) - 1;
> >>>>>>> +		irq_status &= ~BIT(i);
> >>>>>>> +		cmdq_thread_irq_handler(cmdq, i);
> >>>>>>> +	}
> >>>>>>
> >>>>>> Can you explain how the irq status register looks like, that would it
> >>>>>> make much easier to understand what happens here.
> >>>>>
> >>>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
> >>>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
> >>>>>
> >>>>
> >>>> Thanks, that helped. :)
> >>>>
> >>>>>>> +
> >>>>>>> +	return IRQ_HANDLED;	
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
> >>>>>>
> >>>>>> We never check the return values, why do we have them?
> >>>>>
> >>>>> Will drop return value.
> >>>>>
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>>>> +	struct device *dev = cmdq->dev;
> >>>>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>>>> +	struct cmdq_task *next_task, *prev_task;
> >>>>>>> +	u32 irq_flag;
> >>>>>>> +
> >>>>>>> +	/* suspend GCE thread to ensure consistency */
> >>>>>>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>>>>>> +
> >>>>>>> +	/* ISR has handled this error task */
> >>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>>>>>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
> >>>>>>> +				struct cmdq_task, list_entry);
> >>>>>>> +		if (next_task) /* move to next task */
> >>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>>>
> >>>>>> We have to do this, as we suppose that the thread did not reach the jump
> >>>>>> instruction we put into it's command queue, right?
> >>>>>
> >>>>> Yes.
> >>>>>
> >>>>
> >>>> So this should then go into it's own function. In wait_release_work,
> >>>> something like this:
> >>>>
> >>>> if(task->task_state == TASK_STATE_ERROR)
> >>>>         cmdq_task_handle_error(task)
> >>>
> >>> OK.
> >>> I will write new function cmdq_task_handle_error() to handle error case.
> >>>
> >>>>>>> +		cmdq_thread_resume(thread);
> >>>>>>> +		return -ECANCELED;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>
> >>>>>> if task_state != ERROR and != DONE. This means that the timeout of
> >>>>>> task_release_wq has timed out, right?
> >>>>>
> >>>>> Yes.
> >>>>>
> >>>>>>> +	/*
> >>>>>>> +	 * Save next_task and prev_task in advance
> >>>>>>> +	 * since cmdq_handle_error_done will remove list_entry.
> >>>>>>> +	 */
> >>>>>>> +	next_task = prev_task = NULL;
> >>>>>>> +	if (task->list_entry.next != &thread->task_busy_list)
> >>>>>>> +		next_task = list_next_entry(task, list_entry);
> >>>>>>> +	if (task->list_entry.prev != &thread->task_busy_list)
> >>>>>>> +		prev_task = list_prev_entry(task, list_entry);
> >>>>>>> +
> >>>>>>> +	/*
> >>>>>>> +	 * Although IRQ is disabled, GCE continues to execute.
> >>>>>>> +	 * It may have pending IRQ before GCE thread is suspended,
> >>>>>>> +	 * so check this condition again.
> >>>>>>> +	 */
> >>>>>>
> >>>>>> The first thing we did in this function was suspending the thread. Why
> >>>>>> do we need this then?
> >>>>>
> >>>>> Because timeout is CPU timeout not GCE timeout, GCE could just finish
> >>>>> this task before the GCE thread is suspended.
> >>>>>
> >>>>
> >>>> What are the reasons for a timeout? An error has happend, or the task is
> >>>> still executing.
> >>>
> >>>   From GCE's point of view, this task is still executing.
> >>> But, it could be an error of client.
> >>> For example, task may never get event if display turn off hardware
> >>> before waiting for task to finish its work.
> >>>
> >>>>>> To be honest this whole functions looks really like a design error. We
> >>>>>> have to sperate the states much clearer so that it is possible to
> >>>>>> understand what is happening in the GCE. Isn't it for example posible to
> >>>>>> have worker queues for timed out tasks and tasks with an error? I'm not
> >>>>>> sure how to do this, actually I'm not sure if I really understood how
> >>>>>> this is supposed to work.
> >>>>>
> >>>>> GCE doesn't have timeout. The timeout is decided and controlled by CPU,
> >>>>> so we check timeout in release work.
> >>>>> For error and done, they are easy to check by register, and we have
> >>>>> already created release work for timeout. So, I don't think we need to
> >>>>> create work queue for each case.
> >>>>>
> >>>>> What do you think?
> >>>>>
> >>>>
> >>>> I think, if we find in here, that the irq_flag is set, then the the
> >>>> interrupt handler was triggered and is spinning the spinlock. If this is
> >>>> not the case, we have a timeout and we handle this.
> >>>
> >>> I will write another function to handle error, and handle timeout here.
> >>>
> >>>>>> How much do we win, when we patch the thread command queue for every
> >>>>>> task we add, instead of just taking one task after another from the
> >>>>>> task_busy_list?
> >>>>>
> >>>>> GCE is used to help read/write registers with critical time limitation.
> >>>>> Sometimes, client may ask to process multiple tasks in a short period
> >>>>> of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
> >>>>> shouldn't limit to process one task after another from the
> >>>>> task_busy_list. Currently, when interrupt or timeout, we will check
> >>>>> how many tasks are done, and which one is error or timeout.
> >>>>>
> >>>>
> >>>> So I suppose the device driver who use this are interested in throughput
> >>>> and not in latency. The callback of every
> >>>>
> >>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>>>> +
> >>>>>>> +	if (task->task_state == TASK_STATE_DONE) {
> >>>>>>> +		cmdq_thread_resume(thread);
> >>>>>>> +		return 0;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
> >>>>>>> +		dev_err(dev, "task 0x%p error\n", task);
> >>>>>>> +		if (next_task) /* move to next task */
> >>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>> +					   CMDQ_THR_CURR_ADDR);
> >>>>>>> +		cmdq_thread_resume(thread);
> >>>>>>> +		return -ECANCELED;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	/* Task is running, so we force to remove it. */
> >>>>>>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
> >>>>>>> +	task->task_state = TASK_STATE_ERROR;
> >>>>>>> +
> >>>>>>> +	if (prev_task) {
> >>>>>>> +		u64 *prev_va = prev_task->va_base;
> >>>>>>> +		u64 *curr_va = task->va_base;
> >>>>>>> +
> >>>>>>> +		/* copy JUMP instruction */
> >>>>>>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
> >>>>>>> +
> >>>>>>> +		cmdq_thread_invalidate_fetched_data(thread);
> >>>>>>> +	} else if (next_task) { /* move to next task */
> >>>>>>> +		cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>> +				   CMDQ_THR_CURR_ADDR);
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	list_del(&task->list_entry);
> >>>>>>> +	cmdq_thread_resume(thread);
> >>>>>>> +
> >>>>>>> +	/* call cb here to prevent lock */
> >>>>>>> +	if (task->cb.cb) {
> >>>>>>> +		struct cmdq_cb_data cmdq_cb_data;
> >>>>>>> +
> >>>>>>> +		cmdq_cb_data.err = true;
> >>>>>>> +		cmdq_cb_data.data = task->cb.data;
> >>>>>>> +		task->cb.cb(cmdq_cb_data);
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	return -ECANCELED;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
> >>>>>>> +					      release_work);
> >>>>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>>>> +	struct cmdq_thread *thread = task->thread;
> >>>>>>> +	unsigned long flags;
> >>>>>>> +
> >>>>>>> +	wait_event_timeout(thread->wait_task_done,
> >>>>>>> +			   task->task_state != TASK_STATE_BUSY,
> >>>>>>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
> >>>>>>> +
> >>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>>>> +	if (task->task_state != TASK_STATE_DONE)
> >>>>>>> +		cmdq_task_handle_error_result(task);
> >>>>>>> +	if (list_empty(&thread->task_busy_list))
> >>>>>>> +		cmdq_thread_disable(cmdq, thread);
> >>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>> +
> >>>>>>> +	/* release regardless of success or not */
> >>>>>>> +	clk_disable_unprepare(cmdq->clock);
> >>>>>>> +	cmdq_task_release(task);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = task->cmdq;
> >>>>>>> +
> >>>>>>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
> >>>>>>> +	queue_work(cmdq->task_release_wq, &task->release_work);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
> >>>>>>> +{
> >>>>>>> +	void *new_buf;
> >>>>>>> +
> >>>>>>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
> >>>>>>> +	if (!new_buf)
> >>>>>>> +		return -ENOMEM;
> >>>>>>> +	rec->buf = new_buf;
> >>>>>>> +	rec->buf_size = size;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_base *cmdq_base;
> >>>>>>> +	struct resource res;
> >>>>>>> +	int subsys;
> >>>>>>> +	u32 base;
> >>>>>>> +
> >>>>>>> +	if (of_address_to_resource(dev->of_node, 0, &res))
> >>>>>>> +		return NULL;
> >>>>>>> +	base = (u32)res.start;
> >>>>>>> +
> >>>>>>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
> >>>>>>> +	if (subsys < 0)
> >>>>>>> +		return NULL;
> >>>>>>> +
> >>>>>>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
> >>>>>>> +	if (!cmdq_base)
> >>>>>>> +		return NULL;
> >>>>>>> +	cmdq_base->subsys = subsys;
> >>>>>>> +	cmdq_base->base = base;
> >>>>>>> +
> >>>>>>> +	return cmdq_base;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_register_device);
> >>>>>>> +
> >>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>>>> +		    struct cmdq_rec **rec_ptr)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_rec *rec;
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
> >>>>>>> +	if (!rec)
> >>>>>>> +		return -ENOMEM;
> >>>>>>> +	rec->cmdq = dev_get_drvdata(dev);
> >>>>>>> +	rec->engine_flag = engine_flag;
> >>>>>>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
> >>>>>>
> >>>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
> >>>>>
> >>>>> Will do.
> >>>>>
> >>>>>>> +	if (err < 0) {
> >>>>>>> +		kfree(rec);
> >>>>>>> +		return err;
> >>>>>>> +	}
> >>>>>>> +	*rec_ptr = rec;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
> >>>>>>> +
> >>>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
> >>>>>>> +				   u32 arg_a, u32 arg_b)
> >>>>>>> +{
> >>>>>>> +	u64 *cmd_ptr;
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	if (WARN_ON(rec->finalized))
> >>>>>>> +		return -EBUSY;
> >>>>>>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
> >>>>>>> +		return -EINVAL;
> >>>>>>
> >>>>>> cmdq_rec_append_command is just called from inside this driver and code
> >>>>>> is a enum. We can expect it to be correct, no need for this check.
> >>>>>
> >>>>> Will drop this check.
> >>>>>
> >>>>>>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
> >>>>>>
> >>>>>> command_size is the offset into the buffer to which a new command is
> >>>>>> written, so this name is highly confusing. I wonder if this would be
> >>>>>> easier to understand if we redefine command_size to something like the
> >>>>>> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
> >>>>>
> >>>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
> >>>>> dividing CMDQ_INST_SIZE.
> >>>>> What do you think?
> >>>>>
> >>>>>>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
> >>>>>>> +		if (err < 0)
> >>>>>>> +			return err;
> >>>>>>> +	}
> >>>>>>> +	cmd_ptr = rec->buf + rec->command_size;
> >>>>>>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
> >>>>>>> +	rec->command_size += CMDQ_INST_SIZE;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
> >>>>>>> +		   u32 offset)
> >>>>>>> +{
> >>>>>>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
> >>>>>>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
> >>>>>>
> >>>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
> >>>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
> >>>>>
> >>>>> Will drop it.
> >>>>>
> >>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
> >>>>>>> +
> >>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask)
> >>>>>>> +{
> >>>>>>> +	u32 offset_mask = offset;
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	if (mask != 0xffffffff) {
> >>>>>>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
> >>>>>>> +		if (err < 0)
> >>>>>>> +			return err;
> >>>>>>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> >>>>>>> +	}
> >>>>>>> +	return cmdq_rec_write(rec, value, base, offset_mask);
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
> >>>>>>> +
> >>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> >>>>>>> +{
> >>>>>>> +	u32 arg_b;
> >>>>>>> +
> >>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>>>> +		return -EINVAL;
> >>>>>>> +
> >>>>>>> +	/*
> >>>>>>> +	 * bit 0-11: wait value
> >>>>>>> +	 * bit 15: 1 - wait, 0 - no wait
> >>>>>>> +	 * bit 16-27: update value
> >>>>>>> +	 * bit 31: 1 - update, 0 - no update
> >>>>>>> +	 */
> >>>>>>
> >>>>>> I don't understand this comments. What are they for?
> >>>>>
> >>>>> This is for WFE command. I will comment it.
> >>>>>
> >>>>>>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
> >>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
> >>>>>>> +
> >>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
> >>>>>>> +{
> >>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>>>> +		return -EINVAL;
> >>>>>>> +
> >>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
> >>>>>>> +				       CMDQ_WFE_UPDATE);
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
> >>>>>>> +
> >>>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
> >>>>>>> +{
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	if (rec->finalized)
> >>>>>>> +		return 0;
> >>>>>>> +
> >>>>>>> +	/* insert EOC and generate IRQ for each command iteration */
> >>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
> >>>>>>> +	if (err < 0)
> >>>>>>> +		return err;
> >>>>>>> +
> >>>>>>> +	/* JUMP to end */
> >>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
> >>>>>>> +	if (err < 0)
> >>>>>>> +		return err;
> >>>>>>> +
> >>>>>>
> >>>>>> Does this need to be atomic?
> >>>>>> What happens if after CODE_EOC and before CODE_JUMP some
> >>>>>> write/read/event gets added?
> >>>>>> What happens if more commands get added to the queue after CODE_JUMP,
> >>>>>> but before finalized is set to true. Why don't you use atomic functions
> >>>>>> to access finalized?
> >>>>>
> >>>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
> >>>>> client uses cmdq_rec.
> >>>>>
> >>>>
> >>>> Well I think that rec->finalized tries to implement this, but might
> >>>> fail, if two kernel threads work on the same cmdq_rec.
> >>>>
> >>>>>>> +	rec->finalized = true;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>>>>>> +			 void *data)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = rec->cmdq;
> >>>>>>> +	struct cmdq_task *task;
> >>>>>>> +	struct cmdq_task_cb task_cb;
> >>>>>>> +	struct cmdq_thread *thread;
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	mutex_lock(&cmdq->task_mutex);
> >>>>>>> +	if (cmdq->suspended) {
> >>>>>>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
> >>>>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>>>> +		return -EPERM;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	err = cmdq_rec_finalize(rec);
> >>>>>>> +	if (err < 0) {
> >>>>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>>>> +		return err;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	task_cb.cb = cb;
> >>>>>>> +	task_cb.data = data;
> >>>>>>> +	task = cmdq_task_acquire(rec, task_cb);
> >>>>>>> +	if (!task) {
> >>>>>>> +		mutex_unlock(&cmdq->task_mutex);
> >>>>>>> +		return -EFAULT;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
> >>>>>>> +	cmdq_task_exec(task, thread);
> >>>>>>> +	cmdq_task_wait_release_schedule(task);
> >>>>>>> +	mutex_unlock(&cmdq->task_mutex);
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
> >>>>>>> +
> >>>>>>> +struct cmdq_flush_completion {
> >>>>>>> +	struct completion cmplt;
> >>>>>>> +	bool err;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_flush_completion *cmplt = data.data;
> >>>>>>> +
> >>>>>>> +	cmplt->err = data.err;
> >>>>>>> +	complete(&cmplt->cmplt);
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_flush_completion cmplt;
> >>>>>>> +	int err;
> >>>>>>> +
> >>>>>>> +	init_completion(&cmplt.cmplt);
> >>>>>>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
> >>>>>>> +	if (err < 0)
> >>>>>>> +		return err;
> >>>>>>> +	wait_for_completion(&cmplt.cmplt);
> >>>>>>> +	return cmplt.err ? -EFAULT : 0;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
> >>>>>>> +
> >>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
> >>>>>>> +{
> >>>>>>> +	kfree(rec->buf);
> >>>>>>> +	kfree(rec);
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
> >>>>>>> +
> >>>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
> >>>>>>> +{
> >>>>>>> +	struct cmdq_thread *thread;
> >>>>>>> +	int i;
> >>>>>>> +
> >>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>>>> +		thread = &cmdq->thread[i];
> >>>>>>> +		if (!list_empty(&thread->task_busy_list))
> >>>>>>> +			return false;
> >>>>>>> +	}
> >>>>>>> +	return true;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_suspend(struct device *dev)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>>>> +	u32 exec_threads;
> >>>>>>> +
> >>>>>>> +	mutex_lock(&cmdq->task_mutex);
> >>>>>>> +	cmdq->suspended = true;
> >>>>>>> +	mutex_unlock(&cmdq->task_mutex);
> >>>>>>> +
> >>>>>>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
> >>>>>>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
> >>>>>>> +		dev_err(dev, "wait active tasks timeout.\n");
> >>>>>>> +		flush_workqueue(cmdq->task_release_wq);
> >>>>>>> +	}
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_resume(struct device *dev)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>>>> +
> >>>>>>> +	cmdq->suspended = false;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_remove(struct platform_device *pdev)
> >>>>>>> +{
> >>>>>>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
> >>>>>>> +
> >>>>>>> +	destroy_workqueue(cmdq->task_release_wq);
> >>>>>>> +	cmdq->task_release_wq = NULL;
> >>>>>>> +	return 0;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int cmdq_probe(struct platform_device *pdev)
> >>>>>>> +{
> >>>>>>> +	struct device *dev = &pdev->dev;
> >>>>>>> +	struct device_node *node = dev->of_node;
> >>>>>>> +	struct resource *res;
> >>>>>>> +	struct cmdq *cmdq;
> >>>>>>> +	int err, i;
> >>>>>>> +
> >>>>>>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
> >>>>>>> +	if (!cmdq)
> >>>>>>> +		return -ENOMEM;
> >>>>>>> +	cmdq->dev = dev;
> >>>>>>> +
> >>>>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>>>>>> +	cmdq->base = devm_ioremap_resource(dev, res);
> >>>>>>> +	if (IS_ERR(cmdq->base)) {
> >>>>>>> +		dev_err(dev, "failed to ioremap gce\n");
> >>>>>>> +		return PTR_ERR(cmdq->base);
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
> >>>>>>> +	if (!cmdq->irq) {
> >>>>>>> +		dev_err(dev, "failed to get irq\n");
> >>>>>>> +		return -EINVAL;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
> >>>>>>> +		dev, cmdq->base, cmdq->irq);
> >>>>>>> +
> >>>>>>> +	mutex_init(&cmdq->task_mutex);
> >>>>>>> +	spin_lock_init(&cmdq->exec_lock);
> >>>>>>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
> >>>>>>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
> >>>>>>> +			"cmdq_task_wait_release");
> >>>>>>> +
> >>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>>>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
> >>>>>>> +				CMDQ_THR_SHIFT * i;
> >>>>>>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
> >>>>>>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	platform_set_drvdata(pdev, cmdq);
> >>>>>>> +
> >>>>>>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
> >>>>>>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
> >>>>>>> +	if (err < 0) {
> >>>>>>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
> >>>>>>> +		goto fail;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
> >>>>>>> +	if (IS_ERR(cmdq->clock)) {
> >>>>>>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
> >>>>>>> +		err = PTR_ERR(cmdq->clock);
> >>>>>>> +		goto fail;
> >>>>>>> +	}
> >>>>>>> +	return 0;
> >>>>>>> +
> >>>>>>> +fail:
> >>>>>>> +	cmdq_remove(pdev);
> >>>>>>> +	return err;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
> >>>>>>> +	.suspend = cmdq_suspend,
> >>>>>>> +	.resume = cmdq_resume,
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +static const struct of_device_id cmdq_of_ids[] = {
> >>>>>>> +	{.compatible = "mediatek,mt8173-gce",},
> >>>>>>> +	{}
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +static struct platform_driver cmdq_drv = {
> >>>>>>> +	.probe = cmdq_probe,
> >>>>>>> +	.remove = cmdq_remove,
> >>>>>>> +	.driver = {
> >>>>>>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
> >>>>>>> +		.owner = THIS_MODULE,
> >>>>>>> +		.pm = &cmdq_pm_ops,
> >>>>>>> +		.of_match_table = cmdq_of_ids,
> >>>>>>> +	}
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +builtin_platform_driver(cmdq_drv);
> >>>>>>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
> >>>>>>> new file mode 100644
> >>>>>>> index 0000000..60eef3d
> >>>>>>> --- /dev/null
> >>>>>>> +++ b/include/soc/mediatek/cmdq.h
> >>>>>>> @@ -0,0 +1,197 @@
> >>>>>>> +/*
> >>>>>>> + * Copyright (c) 2015 MediaTek Inc.
> >>>>>>> + *
> >>>>>>> + * This program is free software; you can redistribute it and/or modify
> >>>>>>> + * it under the terms of the GNU General Public License version 2 as
> >>>>>>> + * published by the Free Software Foundation.
> >>>>>>> + *
> >>>>>>> + * This program is distributed in the hope that it will be useful,
> >>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>>>>> + * GNU General Public License for more details.
> >>>>>>> + */
> >>>>>>> +
> >>>>>>> +#ifndef __MTK_CMDQ_H__
> >>>>>>> +#define __MTK_CMDQ_H__
> >>>>>>> +
> >>>>>>> +#include <linux/platform_device.h>
> >>>>>>> +#include <linux/types.h>
> >>>>>>> +
> >>>>>>> +enum cmdq_eng {
> >>>>>>> +	CMDQ_ENG_DISP_AAL,
> >>>>>>> +	CMDQ_ENG_DISP_COLOR0,
> >>>>>>> +	CMDQ_ENG_DISP_COLOR1,
> >>>>>>> +	CMDQ_ENG_DISP_DPI0,
> >>>>>>> +	CMDQ_ENG_DISP_DSI0,
> >>>>>>> +	CMDQ_ENG_DISP_DSI1,
> >>>>>>> +	CMDQ_ENG_DISP_GAMMA,
> >>>>>>> +	CMDQ_ENG_DISP_OD,
> >>>>>>> +	CMDQ_ENG_DISP_OVL0,
> >>>>>>> +	CMDQ_ENG_DISP_OVL1,
> >>>>>>> +	CMDQ_ENG_DISP_PWM0,
> >>>>>>> +	CMDQ_ENG_DISP_PWM1,
> >>>>>>> +	CMDQ_ENG_DISP_RDMA0,
> >>>>>>> +	CMDQ_ENG_DISP_RDMA1,
> >>>>>>> +	CMDQ_ENG_DISP_RDMA2,
> >>>>>>> +	CMDQ_ENG_DISP_UFOE,
> >>>>>>> +	CMDQ_ENG_DISP_WDMA0,
> >>>>>>> +	CMDQ_ENG_DISP_WDMA1,
> >>>>>>> +	CMDQ_ENG_MAX,
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +/* events for CMDQ and display */
> >>>>>>> +enum cmdq_event {
> >>>>>>> +	/* Display start of frame(SOF) events */
> >>>>>>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> >>>>>>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> >>>>>>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> >>>>>>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> >>>>>>> +	/* Display end of frame(EOF) events */
> >>>>>>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> >>>>>>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> >>>>>>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> >>>>>>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> >>>>>>> +	/* Mutex end of frame(EOF) events */
> >>>>>>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> >>>>>>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> >>>>>>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> >>>>>>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> >>>>>>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> >>>>>>> +	/* Display underrun events */
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> >>>>>>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> >>>>>>> +	/* Keep this at the end of HW events */
> >>>>>>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +struct cmdq_cb_data {
> >>>>>>> +	bool	err;
> >>>>>>> +	void	*data;
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
> >>>>>>> +
> >>>>>>> +struct cmdq_task;
> >>>>>>> +struct cmdq;
> >>>>>>> +
> >>>>>>> +struct cmdq_rec {
> >>>>>>> +	struct cmdq		*cmdq;
> >>>>>>> +	u64			engine_flag;
> >>>>>>> +	size_t			command_size;
> >>>>>>> +	void			*buf;
> >>>>>>> +	size_t			buf_size;
> >>>>>>> +	bool			finalized;
> >>>>>>> +};
> >>>>
> >>>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that
> >>>> and this way make the driver less complex?
> >>>
> >>> There are two main reasons for cmdq_rec.
> >>> 1. It is slow to access dma too frequently.
> >>>      So, we append commands to cacheable memory, and then flush to dma.
> >>> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
> >>>      If we merge them, we need to take care of some synchronization
> >>>      issues.
> >>>
> >>>>>>> +
> >>>>>>> +struct cmdq_base {
> >>>>>>> +	int	subsys;
> >>>>>>> +	u32	base;
> >>>>>>
> >>>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
> >>>>>> so we can get rid of the struct, right?
> >>>>>
> >>>>> Current subsys method is based on previous comment from Daniel Kurtz.
> >>>>> Please take a look of our previous discussion.
> >>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >>>>> Thanks.
> >>>>>
> >>>>
> >>>> I have to look deeper into this, but from what I read, the proposal from
> >>>> Daniel [1] seems good to me.
> >>>>
> >>>> [1] https://patchwork.kernel.org/patch/8068311/
> >>>
> >>> The initial proposal has some problem, so please see the follow-up
> >>> discussions. Thanks.
> >>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
> >>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >>>
> >>>
> >>>>>>> +};
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_register_device() - register device which needs CMDQ
> >>>>>>> + * @dev:		device
> >>>>>>> + *
> >>>>>>> + * Return: cmdq_base pointer or NULL for failed
> >>>>>>> + */
> >>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_create() - create command queue record
> >>>>>>> + * @dev:		device
> >>>>>>> + * @engine_flag:	command queue engine flag
> >>>>>>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>>>> +		    struct cmdq_rec **rec_ptr);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_write() - append write command to the command queue record
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + * @value:	the specified target register value
> >>>>>>> + * @base:	the command queue base
> >>>>>>> + * @offset:	register offset from module base
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
> >>>>>>> +		   struct cmdq_base *base, u32 offset);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_write_mask() - append write command with mask to the command
> >>>>>>> + *			   queue record
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + * @value:	the specified target register value
> >>>>>>> + * @base:	the command queue base
> >>>>>>> + * @offset:	register offset from module base
> >>>>>>> + * @mask:	the specified target register mask
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
> >>>>>>
> >>>>>> reco rd -> record
> >>>>>
> >>>>> Will fix it.
> >>>>>
> >>>>>> Regards,
> >>>>>> Matthias
> >>>>>>
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + * @event:	the desired event type to "wait and CLEAR"
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_clear_event() - append clear event command to the command queue
> >>>>>>> + *			    record
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + * @event:	the desired event to be cleared
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + *
> >>>>>>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
> >>>>>>> + * synchronous flush function. When the function returned, the recorded
> >>>>>>> + * commands have been done.
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
> >>>>>>> + *			    commands and call back after ISR is finished
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + * @cb:		called in the end of CMDQ ISR
> >>>>>>> + * @data:	this data will pass back to cb
> >>>>>>> + *
> >>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>> + *
> >>>>>>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
> >>>>>>> + * after ISR is finished. Note that this is an ASYNC function. When the function
> >>>>>>> + * returned, it may or may not be finished. The ISR callback function is called
> >>>>>>> + * in the end of ISR.
> >>>>
> >>>> "The callback is called from the ISR."
> >>>>
> >>>> Regards,
> >>>> Matthias
> >>>>
> >>>>>>> + */
> >>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
> >>>>>>> +			 void *data);
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * cmdq_rec_destroy() - destroy command queue record
> >>>>>>> + * @rec:	the command queue record
> >>>>>>> + */
> >>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
> >>>>>>> +
> >>>>>>> +#endif	/* __MTK_CMDQ_H__ */
> >>>>>>>
> >>>>>
> >>>>> Thanks,
> >>>>> HS
> >>>>>
> >>>
> >>> Thanks,
> >>> HS
> >>>
> >
> >




From matthias.bgg at gmail.com  Fri Jun  3 06:11:01 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 15:11:01 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464956037.16029.8.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
Message-ID: <575181E5.6090603@gmail.com>



On 03/06/16 14:13, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Fri, 2016-06-03 at 13:18 +0200, Matthias Brugger wrote:
>>
>> On 03/06/16 08:12, Horng-Shyang Liao wrote:
>>> Hi Mathias,
>>>
>>> Please see my inline reply.
>>>
>>> On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
>>>>
>>>> On 01/06/16 11:57, Horng-Shyang Liao wrote:
>>>>> Hi Mathias,
>>>>>
>>>>> Please see my inline reply.
>>>>>
>>>>> On Tue, 2016-05-31 at 22:04 +0200, Matthias Brugger wrote:
>>>>>>
>>>>>> On 31/05/16 10:36, Horng-Shyang Liao wrote:
>>>>>>> Hi Mathias,
>>>>>>>
>>>>>>> Please see my inline reply.
>>>>>>>
>>>>>>> On Mon, 2016-05-30 at 17:31 +0200, Matthias Brugger wrote:
>>>>>>>>
>>>>>>>> On 30/05/16 05:19, HS Liao wrote:
>>>>>>>>> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
>>>>>>>>> CMDQ is used to help read/write registers with critical time limitation,
>>>>>>>>> such as updating display configuration during the vblank. It controls
>>>>>>>>> Global Command Engine (GCE) hardware to achieve this requirement.
>>>>>>>>> Currently, CMDQ only supports display related hardwares, but we expect
>>>>>>>>> it can be extended to other hardwares for future requirements.
>>>>>>>>>
>>>>>>>>> Signed-off-by: HS Liao <hs.liao at mediatek.com>
>>>>>>>>> Signed-off-by: CK Hu <ck.hu at mediatek.com>
>>>>>>>>> ---
>>>>>>>>>       drivers/soc/mediatek/Kconfig    |  10 +
>>>>>>>>>       drivers/soc/mediatek/Makefile   |   1 +
>>>>>>>>>       drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
>>>>>>>>>       include/soc/mediatek/cmdq.h     | 197 +++++++++
>>>>>>>>>       4 files changed, 1151 insertions(+)
>>>>>>>>>       create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
>>>>>>>>>       create mode 100644 include/soc/mediatek/cmdq.h
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
>>>>>>>>> index 0a4ea80..c4ad75c 100644
>>>>>>>>> --- a/drivers/soc/mediatek/Kconfig
>>>>>>>>> +++ b/drivers/soc/mediatek/Kconfig
>>>>>>>>> @@ -1,6 +1,16 @@
>>>>>>>>>       #
>>>>>>>>>       # MediaTek SoC drivers
>>>>>>>>>       #
>>>>>>>>> +config MTK_CMDQ
>>>>>>>>> +	bool "MediaTek CMDQ Support"
>>>>>>>>> +	depends on ARCH_MEDIATEK || COMPILE_TEST
>>>>>>
>>>>>> depends on ARM64 ?
>>>>>
>>>>> Will add ARM64.
>>>>>
>>>>>>>>> +	select MTK_INFRACFG
>>>>>>>>> +	help
>>>>>>>>> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
>>>>>>>>> +	  driver. The CMDQ is used to help read/write registers with critical
>>>>>>>>> +	  time limitation, such as updating display configuration during the
>>>>>>>>> +	  vblank.
>>>>>>>>> +
>>>>>>>>>       config MTK_INFRACFG
>>>>>>>>>       	bool "MediaTek INFRACFG Support"
>>>>>>>>>       	depends on ARCH_MEDIATEK || COMPILE_TEST
>>>>>>>>> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
>>>>>>>>> index 12998b0..f7397ef 100644
>>>>>>>>> --- a/drivers/soc/mediatek/Makefile
>>>>>>>>> +++ b/drivers/soc/mediatek/Makefile
>>>>>>>>> @@ -1,3 +1,4 @@
>>>>>>>>> +obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
>>>>>>>>>       obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
>>>>>>>>>       obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
>>>>>>>>>       obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
>>>>>>>>> diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..e9d6e1c
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/drivers/soc/mediatek/mtk-cmdq.c
>>>>>>>>> @@ -0,0 +1,943 @@
>>>>>>>>> +/*
>>>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>>>>>> + *
>>>>>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>>>>>> + * published by the Free Software Foundation.
>>>>>>>>> + *
>>>>>>>>> + * This program is distributed in the hope that it will be useful,
>>>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>>>>>> + * GNU General Public License for more details.
>>>>>>>>> + */
>>>>>>>>> +
>>>>>>>>> +#include <linux/clk.h>
>>>>>>>>> +#include <linux/clk-provider.h>
>>>>>>>>> +#include <linux/completion.h>
>>>>>>>>> +#include <linux/dma-mapping.h>
>>>>>>>>> +#include <linux/errno.h>
>>>>>>>>> +#include <linux/interrupt.h>
>>>>>>>>> +#include <linux/iopoll.h>
>>>>>>>>> +#include <linux/kernel.h>
>>>>>>>>> +#include <linux/kthread.h>
>>>>>>>>> +#include <linux/module.h>
>>>>>>>>> +#include <linux/mutex.h>
>>>>>>>>> +#include <linux/of_address.h>
>>>>>>>>> +#include <linux/of_irq.h>
>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>> +#include <linux/slab.h>
>>>>>>>>> +#include <linux/spinlock.h>
>>>>>>>>> +#include <linux/suspend.h>
>>>>>>>>> +#include <linux/workqueue.h>
>>>>>>>>> +#include <soc/mediatek/cmdq.h>
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_INITIAL_CMD_BLOCK_SIZE	PAGE_SIZE
>>>>>>>>> +#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
>>>>>>>>> +#define CMDQ_TIMEOUT_MS			1000
>>>>>>>>> +#define CMDQ_IRQ_MASK			0xffff
>>>>>>>>> +#define CMDQ_DRIVER_DEVICE_NAME		"mtk_cmdq"
>>>>>>>>> +#define CMDQ_CLK_NAME			"gce"
>>>>>>>>
>>>>>>>> We can put the names in directly to un-bloat the defines.
>>>>>>>
>>>>>>> I will use the names directly and remove defines.
>>>>>>>
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_CURR_IRQ_STATUS		0x010
>>>>>>>>> +#define CMDQ_CURR_LOADED_THR		0x018
>>>>>>>>> +#define CMDQ_THR_SLOT_CYCLES		0x030
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_THR_BASE			0x100
>>>>>>>>> +#define CMDQ_THR_SHIFT			0x080
>>>>>>>>
>>>>>>>> Wouldn't be CMDQ_THR_SIZE more accurate?
>>>>>>>
>>>>>>> Will rename it.
>>>>>>>
>>>>>>>>> +#define CMDQ_THR_WARM_RESET		0x00
>>>>>>>>> +#define CMDQ_THR_ENABLE_TASK		0x04
>>>>>>>>> +#define CMDQ_THR_SUSPEND_TASK		0x08
>>>>>>>>> +#define CMDQ_THR_CURR_STATUS		0x0c
>>>>>>>>> +#define CMDQ_THR_IRQ_STATUS		0x10
>>>>>>>>> +#define CMDQ_THR_IRQ_ENABLE		0x14
>>>>>>>>> +#define CMDQ_THR_CURR_ADDR		0x20
>>>>>>>>> +#define CMDQ_THR_END_ADDR		0x24
>>>>>>>>> +#define CMDQ_THR_CFG			0x40
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_THR_ENABLED		0x1
>>>>>>>>> +#define CMDQ_THR_DISABLED		0x0
>>>>>>>>> +#define CMDQ_THR_SUSPEND		0x1
>>>>>>>>> +#define CMDQ_THR_RESUME			0x0
>>>>>>>>> +#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
>>>>>>>>> +#define CMDQ_THR_DO_WARM_RESET		BIT(0)
>>>>>>>>> +#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
>>>>>>>>> +#define CMDQ_THR_PRIORITY		3
>>>>>>>>> +#define CMDQ_THR_IRQ_DONE		0x1
>>>>>>>>> +#define CMDQ_THR_IRQ_ERROR		0x12
>>>>>>>>> +#define CMDQ_THR_IRQ_EN			0x13 /* done + error */
>>>>>>>>
>>>>>>>> #define CMDQ_THR_IRQ_EN (CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
>>>>>>>
>>>>>>> Will do.
>>>>>>>
>>>>>>>>> +#define CMDQ_THR_IRQ_MASK		0x13
>>>>>>>>
>>>>>>>> never used.
>>>>>>>
>>>>>>> Will remove.
>>>>>>>
>>>>>>>>> +#define CMDQ_THR_EXECUTING		BIT(31)
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_ARG_A_WRITE_MASK		0xffff
>>>>>>>>> +#define CMDQ_SUBSYS_MASK		0x1f
>>>>>>>>> +#define CMDQ_OP_CODE_MASK		0xff000000
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_OP_CODE_SHIFT		24
>>>>>>>>
>>>>>>>> Couldn't we connect the mask with the shift, or aren't they related?
>>>>>>>>
>>>>>>>> #define CMDQ_OP_CODE_MASK (0xff << CMDQ_OP_CODE_SHIFT)
>>>>>>>
>>>>>>> Will do.
>>>>>>>
>>>>>>>>> +#define CMDQ_SUBSYS_SHIFT		16
>>>>>>>>> +
>>>>>>>>> +#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
>>>>>>>>> +#define CMDQ_JUMP_BY_OFFSET		0x10000000
>>>>>>>>> +#define CMDQ_JUMP_BY_PA			0x10000001
>>>>>>>>> +#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
>>>>>>>>> +#define CMDQ_WFE_UPDATE			BIT(31)
>>>>>>>>> +#define CMDQ_WFE_WAIT			BIT(15)
>>>>>>>>> +#define CMDQ_WFE_WAIT_VALUE		0x1
>>>>>>>>> +#define CMDQ_EOC_IRQ_EN			BIT(0)
>>>>>>>>> +
>>>>>>>>> +enum cmdq_thread_index {
>>>>>>>>> +	CMDQ_THR_DISP_MAIN_IDX,	/* main */
>>>>>>>>> +	CMDQ_THR_DISP_SUB_IDX,	/* sub */
>>>>>>>>> +	CMDQ_THR_DISP_MISC_IDX,	/* misc */
>>>>>>>>> +	CMDQ_THR_MAX_COUNT,	/* max */
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +/*
>>>>>>>>> + * CMDQ_CODE_MOVE:
>>>>>>>>> + *   move value into internal register as mask
>>>>>>>>> + *   format: op mask
>>>>>>>>> + * CMDQ_CODE_WRITE:
>>>>>>>>> + *   write value into target register
>>>>>>>>> + *   format: op subsys address value
>>>>>>>>> + * CMDQ_CODE_JUMP:
>>>>>>>>> + *   jump by offset
>>>>>>>>> + *   format: op offset
>>>>>>>>> + * CMDQ_CODE_WFE:
>>>>>>>>> + *   wait for event and clear
>>>>>>>>> + *   it is just clear if no wait
>>>>>>>>> + *   format: [wait]  op event update:1 to_wait:1 wait:1
>>>>>>>>> + *           [clear] op event update:1 to_wait:0 wait:0
>>>>>>>>> + * CMDQ_CODE_EOC:
>>>>>>>>> + *   end of command
>>>>>>>>> + *   format: op irq_flag
>>>>>>>>> + */
>>>>>>>>
>>>>>>>> I think we need more documentation of how this command queue engine is
>>>>>>>> working. If not, I think it will be really complicated to understand how
>>>>>>>> to use this.
>>>>>>>>
>>>>>>>>> +enum cmdq_code {
>>>>>>>>> +	CMDQ_CODE_MOVE = 0x02,
>>>>>>>>> +	CMDQ_CODE_WRITE = 0x04,
>>>>>>>>> +	CMDQ_CODE_JUMP = 0x10,
>>>>>>>>> +	CMDQ_CODE_WFE = 0x20,
>>>>>>>>> +	CMDQ_CODE_EOC = 0x40,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +enum cmdq_task_state {
>>>>>>>>> +	TASK_STATE_BUSY, /* running on a GCE thread */
>>>>>>>>> +	TASK_STATE_ERROR,
>>>>>>>>> +	TASK_STATE_DONE,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq_task_cb {
>>>>>>>>> +	cmdq_async_flush_cb	cb;
>>>>>>>>> +	void			*data;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq_thread {
>>>>>>>>> +	void __iomem		*base;
>>>>>>>>> +	struct list_head	task_busy_list;
>>>>>>>>> +	wait_queue_head_t	wait_task_done;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq_task {
>>>>>>>>> +	struct cmdq		*cmdq;
>>>>>>>>> +	struct list_head	list_entry;
>>>>>>>>> +	enum cmdq_task_state	task_state;
>>>>>>>>> +	void			*va_base;
>>>>>>>>> +	dma_addr_t		pa_base;
>>>>>>>>> +	u64			engine_flag;
>>>>>>>>> +	size_t			command_size;
>>>>>>>>> +	u32			num_cmd;
>>>>>>>>
>>>>>>>> num_cmd is directly connected to command_size. I prefer to just keep
>>>>>>>> num_cmd and calculate the command_size where necessary.
>>>>>>>
>>>>>>> After I trace code, I prefer to keep command_size and calculate num_cmd
>>>>>>> where necessary. What do you think?
>>>>>>>
>>>>>>
>>>>>> I suppose you prefer this, as you are writing to the GCE depending on
>>>>>> the command_size. I think it is worth to create a macro for the
>>>>>> calculation of the number of commands, to make the code more readable.
>>>>>> Would be nice if you would just pass cmdq_task to it and it would return
>>>>>> the number. Just as an idea.
>>>>>
>>>>> Will add macro.
>>>>>
>>>>>>>>> +	struct cmdq_thread	*thread;
>>>>>>>>> +	struct cmdq_task_cb	cb;
>>>>>>>>> +	struct work_struct	release_work;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq {
>>>>>>>>> +	struct device		*dev;
>>>>>>>>> +	void __iomem		*base;
>>>>>>>>> +	u32			irq;
>>>>>>>>> +	struct workqueue_struct	*task_release_wq;
>>>>>>>>> +	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
>>>>>>>>> +	struct mutex		task_mutex;	/* for task */
>>>>>>>>> +	spinlock_t		exec_lock;	/* for exec */
>>>>>>>>> +	struct clk		*clock;
>>>>>>>>> +	bool			suspended;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq_subsys {
>>>>>>>>> +	u32	base;
>>>>>>>>> +	int	id;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static const struct cmdq_subsys gce_subsys[] = {
>>>>>>>>> +	{0x1400, 1},
>>>>>>>>> +	{0x1401, 2},
>>>>>>>>> +	{0x1402, 3},
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static int cmdq_subsys_base_to_id(u32 base)
>>>>>>>>> +{
>>>>>>>>> +	int i;
>>>>>>>>> +
>>>>>>>>> +	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
>>>>>>>>> +		if (gce_subsys[i].base == base)
>>>>>>>>> +			return gce_subsys[i].id;
>>>>>>>>> +	return -EFAULT;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_eng_get_thread(u64 flag)
>>>>>>>>> +{
>>>>>>>>> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
>>>>>>>>> +		return CMDQ_THR_DISP_MAIN_IDX;
>>>>>>>>> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
>>>>>>>>> +		return CMDQ_THR_DISP_SUB_IDX;
>>>>>>>>> +	else
>>>>>>>>> +		return CMDQ_THR_DISP_MISC_IDX;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_task_release(struct cmdq_task *task)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>>>> +
>>>>>>>>> +	dma_free_coherent(cmdq->dev, task->command_size, task->va_base,
>>>>>>>>> +			  task->pa_base);
>>>>>>>>> +	kfree(task);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
>>>>>>>>> +					   struct cmdq_task_cb cb)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>>>>>> +	struct device *dev = cmdq->dev;
>>>>>>>>> +	struct cmdq_task *task;
>>>>>>>>> +
>>>>>>>>> +	task = kzalloc(sizeof(*task), GFP_KERNEL);
>>>>>>>>> +	INIT_LIST_HEAD(&task->list_entry);
>>>>>>>>> +	task->va_base = dma_alloc_coherent(dev, rec->command_size,
>>>>>>>>> +					   &task->pa_base, GFP_KERNEL);
>>>>>>>>> +	if (!task->va_base) {
>>>>>>>>> +		dev_err(dev, "allocate command buffer failed\n");
>>>>>>>>> +		kfree(task);
>>>>>>>>> +		return NULL;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	task->cmdq = cmdq;
>>>>>>>>> +	task->command_size = rec->command_size;
>>>>>>>>> +	task->engine_flag = rec->engine_flag;
>>>>>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>>>>>> +	task->cb = cb;
>>>>>>>>> +	memcpy(task->va_base, rec->buf, rec->command_size);
>>>>>>>>> +	task->num_cmd = task->command_size / CMDQ_INST_SIZE;
>>>>>>>>> +	return task;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_thread_writel(struct cmdq_thread *thread, u32 value,
>>>>>>>>> +			       u32 offset)
>>>>>>>>> +{
>>>>>>>>> +	writel(value, thread->base + offset);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static u32 cmdq_thread_readl(struct cmdq_thread *thread, u32 offset)
>>>>>>>>> +{
>>>>>>>>> +	return readl(thread->base + offset);
>>>>>>>>> +}
>>>>>>>>
>>>>>>>> We can get rid of cmdq_thread_readl/writel.
>>>>>>>
>>>>>>> Will do.
>>>>>>>
>>>>>>>>> +
>>>>>>>>> +static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	u32 status;
>>>>>>>>> +
>>>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_SUSPEND, CMDQ_THR_SUSPEND_TASK);
>>>>>>>>> +
>>>>>>>>> +	/* If already disabled, treat as suspended successful. */
>>>>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>>> +	      CMDQ_THR_ENABLED))
>>>>>>>>> +		return 0;
>>>>>>>>> +
>>>>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
>>>>>>>>> +			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
>>>>>>>>> +		dev_err(cmdq->dev, "suspend GCE thread 0x%x failed\n",
>>>>>>>>> +			(u32)(thread->base - cmdq->base));
>>>>>>>>> +		return -EFAULT;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_thread_resume(struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_RESUME, CMDQ_THR_SUSPEND_TASK);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	u32 warm_reset;
>>>>>>>>> +
>>>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DO_WARM_RESET, CMDQ_THR_WARM_RESET);
>>>>>>>>> +	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
>>>>>>>>> +			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
>>>>>>>>> +			0, 10)) {
>>>>>>>>> +		dev_err(cmdq->dev, "reset GCE thread 0x%x failed\n",
>>>>>>>>> +			(u32)(thread->base - cmdq->base));
>>>>>>>>> +		return -EFAULT;
>>>>>>>>> +	}
>>>>>>>>> +	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	cmdq_thread_reset(cmdq, thread);
>>>>>>>>> +	cmdq_thread_writel(thread, CMDQ_THR_DISABLED, CMDQ_THR_ENABLE_TASK);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +/* notify GCE to re-fetch commands by setting GCE thread PC */
>>>>>>>>> +static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	cmdq_thread_writel(thread,
>>>>>>>>> +			   cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR),
>>>>>>>>> +			   CMDQ_THR_CURR_ADDR);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_task_insert_into_thread(struct cmdq_task *task)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>>>> +	struct cmdq_task *prev_task = list_last_entry(
>>>>>>>>> +			&thread->task_busy_list, typeof(*task), list_entry);
>>>>>>>>> +	u64 *prev_task_base = prev_task->va_base;
>>>>>>>>> +
>>>>>>>>> +	/* let previous task jump to this task */
>>>>>>>>> +	prev_task_base[prev_task->num_cmd - 1] = (u64)CMDQ_JUMP_BY_PA << 32 |
>>>>>>>>> +						 task->pa_base;
>>>>>>>>> +
>>>>>>>>> +	cmdq_thread_invalidate_fetched_data(thread);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +/* we assume tasks in the same display GCE thread are waiting the same event. */
>>>>>>>>> +static void cmdq_task_remove_wfe(struct cmdq_task *task)
>>>>>>>>> +{
>>>>>>>>> +	u32 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>>>>>> +	u32 wfe_op = CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT;
>>>>>>>>> +	u32 *base = task->va_base;
>>>>>>>>> +	u32 num_cmd = task->num_cmd << 1;
>>>>>>>>> +	int i;
>>>>>>>>> +
>>>>>>>>> +	for (i = 0; i < num_cmd; i += 2)
>>>>>>>>> +		if (base[i] == wfe_option &&
>>>>>>>>> +		    (base[i + 1] & CMDQ_OP_CODE_MASK) == wfe_op) {
>>>>>>>>> +			base[i] = CMDQ_JUMP_PASS;
>>>>>>>>> +			base[i + 1] = CMDQ_JUMP_BY_OFFSET;
>>>>>>>>> +		}
>>>>>>>>
>>>>>>>> After using the command buffer as a void pointer a u64 pointer, we now
>>>>>>>> reference to it as u32. I would prefer to explain here, how the command
>>>>>>>> looks like we are searching for and use a for loop passing task->num_cmd
>>>>>>>> instead.
>>>>>>>
>>>>>>> Will use u64* to rewrite the above code.
>>>>>>>
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>>>> +	unsigned long flags;
>>>>>>>>> +	unsigned long curr_pa, end_pa;
>>>>>>>>> +
>>>>>>>>> +	WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
>>>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>>>
>>>>>>>> cmdq_task_exec is called with cmdq->task_mutex held, so why do we need
>>>>>>>> the spin_lock here? Can't we just use one of the two?
>>>>>>>
>>>>>>> We can drop task_mutex, but we will get some side effects.
>>>>>>> 1. exec_lock needs to include more code, but I think it is not good for
>>>>>>>        spinlock.
>>>>>>> 2. In cmdq_rec_flush_async(), task_mutex needs to protect
>>>>>>>        (1) cmdq->suspended, (2) cmdq_task_exec(), and
>>>>>>>        (3) cmdq_task_wait_release_schedule().
>>>>>>>        If we drop task_mutex, we have to put cmdq->suspended if condition
>>>>>>>        just before cmdq_task_exec() and inside exec_lock, and we have to
>>>>>>>        release task and its command buffer if error. This will let flow
>>>>>>>        become more complex and enlarge code size.
>>>>>>>
>>>>>>> What do you think?
>>>>>>
>>>>>> Why do you need to protect cmdq_task_wait_release_schedule? We don't
>>>>>> care about the order of the workqueue elements, do we?
>>>>>
>>>>> According to CK's comment, we have to ensure order to remove previous
>>>>> task.
>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>>>>>
>>>>>> As far as I understand you would need to protect cmdq_task_acquire as
>>>>>> well, to "ensure" continously growing pa_base. More on that below.
>>>>>
>>>>> We need to ensure continuous physical addresses in a task, but we don't
>>>>> need to ensure continuous physical addresses between tasks.
>>>>> So, I think it is unnecessary to protect by mutex or spinlock.
>>>>>
>>>>
>>>> True, I didn't get that.
>>>>
>>>>>>>
>>>>>>>>> +	task->thread = thread;
>>>>>>>>> +	task->task_state = TASK_STATE_BUSY;
>>>>>>>>
>>>>>>>> That was already set in cmdq_task_acquire, why do we need to set it here
>>>>>>>> again?
>>>>>>>
>>>>>>> Will drop it.
>>>>>>>
>>>>>>
>>>>>> Yeah, but I think it makes more sense to drop it in cmdq_task_acquire
>>>>>> instead.
>>>>>
>>>>> Will drop it in cmdq_task_acquire instead.
>>>>>
>>>>>>>>> +	if (list_empty(&thread->task_busy_list)) {
>>>>>>>>> +		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
>>>>>>>>> +
>>>>>>>>> +		cmdq_thread_writel(thread, task->pa_base, CMDQ_THR_CURR_ADDR);
>>>>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>>>>>> +				   CMDQ_THR_END_ADDR);
>>>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_PRIORITY, CMDQ_THR_CFG);
>>>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_IRQ_EN,
>>>>>>>>> +				   CMDQ_THR_IRQ_ENABLE);
>>>>>>>>> +
>>>>>>>>> +		cmdq_thread_writel(thread, CMDQ_THR_ENABLED,
>>>>>>>>> +				   CMDQ_THR_ENABLE_TASK);
>>>>>>>>> +	} else {
>>>>>>>>> +		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>>>>>> +
>>>>>>>>> +		/*
>>>>>>>>> +		 * check boundary condition
>>>>>>>>> +		 * PC = END - 8, EOC is executed
>>>>>>>>> +		 * PC = END, all CMDs are executed
>>>>>>>>> +		 */
>>>>>>>>> +		curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>> +		end_pa = cmdq_thread_readl(thread, CMDQ_THR_END_ADDR);
>>>>>>>>> +		if (curr_pa == end_pa - 8 || curr_pa == end_pa) {
>>>>>>>>
>>>>>>>> 8 refers to CMDQ_INST_SIZE, right?
>>>>>>>
>>>>>>> Yes, I will use CMDQ_INST_SIZE.
>>>>>>>
>>>>>>>>> +			/* set to this task directly */
>>>>>>>>> +			cmdq_thread_writel(thread, task->pa_base,
>>>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>>>> +		} else {
>>>>>>>>> +			cmdq_task_insert_into_thread(task);
>>>>>>>>> +
>>>>>>>>> +			if (thread == &cmdq->thread[CMDQ_THR_DISP_MAIN_IDX] ||
>>>>>>>>> +			    thread == &cmdq->thread[CMDQ_THR_DISP_SUB_IDX])
>>>>>>>>> +				cmdq_task_remove_wfe(task);
>>>>>>>>
>>>>>>>> We could do this check using the task->engine_flag, I suppose that's
>>>>>>>> easier to undestand then.
>>>>>>>
>>>>>>> Will use task->engine_flag.
>>>>>>>
>>>>>>>>> +
>>>>>>>>> +			smp_mb(); /* modify jump before enable thread */
>>>>>>>>> +		}
>>>>>>>>> +
>>>>>>>>> +		cmdq_thread_writel(thread, task->pa_base + task->command_size,
>>>>>>>>> +				   CMDQ_THR_END_ADDR);
>>>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>>>> +	}
>>>>>>>>> +	list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>> +				   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>> +	u32 curr_pa;
>>>>>>>>> +	struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>> +	bool err;
>>>>>>>>> +
>>>>>>>>> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>> +		err = true;
>>>>>>>>> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>> +		err = false;
>>>>>>>>> +	else
>>>>>>>>> +		return;
>>>>>>>>> +
>>>>>>>>> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>> +
>>>>>>>>> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>> +				 list_entry) {
>>>>>>>>> +		if (curr_pa >= task->pa_base &&
>>>>>>>>> +		    curr_pa < (task->pa_base + task->command_size))
>>>>>>>>
>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>> assumptions about pa_base and the order of execution of commands in the
>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any guarantees
>>>>>>>> about dma_handle?
>>>>>>>
>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>> 2. Yes.
>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>
>>>>>>
>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>> guarantee that the dma_handle address is steadily growing, when calling
>>>>>> dma_alloc_coherent. And if I understand the code correctly, you use this
>>>>>> assumption to decide if the task picked from task_busy_list is currently
>>>>>> executing. So I think this mecanism is not working.
>>>>>
>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>    From CPU's point of view, tasks are linked by the busy list.
>>>>>    From GCE's point of view, tasks are linked by the JUMP command.
>>>>>
>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>
>>>>> GCE will raise interrupt if any task is done or error.
>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>> when it is running ISR.
>>>>>
>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>
>>>>
>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>> information from the handler to thread_fn, but that shouldn't be an
>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>> we need to do it in a thread context.
>>>
>>> Actually, our initial implementation is similar to your suggestion,
>>> but display needs CMDQ to return callback function very precisely,
>>> else display will drop frame.
>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>> and CMDQ needs to call callback function in ISR.
>>> If we defer callback to workqueue, the time interval may be larger than
>>> 32 ms.sometimes.
>>>
>>
>> I think the problem is, that you implemented the workqueue as a ordered
>> workqueue, so there is no parallel processing. I'm still not sure why
>> you need the workqueue to be ordered. Can you please explain.
>
> The order should be kept.
> Let me use mouse cursor as an example.
> If task 1 means move mouse cursor to point A, task 2 means point B,
> and task 3 means point C, our expected result is A -> B -> C.
> If the order is not kept, the result could become A -> C -> B.
>

Got it, thanks for the clarification.

Matthias

>>>> I keep thinking about how to get rid of the two data structures,
>>>> task_busy_list and the task_release_wq. We need the latter for the only
>>>> sake of getting a timeout.
>>>>
>>>> Did you have a look on how the mailbox framework handles this?
>>>> By the way, what is the reason to not implement the whole driver as a
>>>> mailbox controller? For me, this driver looks like a good fit.
>>>
>>> CMDQ needs to encode commands for GCE hardware. We think this behavior
>>> should be put in CMDQ driver, and client just call CMDQ functions.
>>> Therefore, if we want to use mailbox framework, cmdq_rec must be
>>> mailbox client, and the others must be mailbox controller.
>>>
>>
>> You mean the functions to fill the cmdq_rec and execute it?
>> I think this should be part of the driver.
>
> Yes.
>
>> Jassi, can you have a look on the interface this driver exports [0].
>> They are needed to actually create the message which will be send.
>> Could something like this be part of a mailbox driver?
>>
>> [0] https://patchwork.kernel.org/patch/9140221/
>>
>>> However, if we use mailbox controller, CMDQ driver still needs to
>>> control busy list for each GCE thread, and use workqueue to handle
>>> timeout tasks.
>>>
>>
>> Let me summarize my ideas around this driver:
>> When we enter the ISR, we know that all task in task_busy_list before
>> the entry which represents curr_task can be set to TASK_STATE_DONE.
>> The curr_task could be TASK_STATE_ERROR if the corresponding bit in the
>> irq status registers is set.
>> Do we need to call the callback in the same order as the tasks got
>> dispatched to the HW thread? If not, we could try to call all this
>> callbacks in a multithreaded workqueue.
>
> Yes, we should keep order.
>
>> Regards,
>> Matthias
>
> Thanks,
> HS
>
>>> The only thing that we can borrow from mailbox framework is the send
>>> (CMDQ flush) and receive (CMDQ callback) interface, However, we don't
>>> think we can gain many benefits from it, and we have some overheads to
>>> conform to mailbox interface.
>>>
>>>
>>>>
>>>>>>>>> +			curr_task = task;
>>>>>>>>> +		if (task->cb.cb) {
>>>>>>>>> +			cmdq_cb_data.err = curr_task ? err : false;
>>>>>>>>> +			cmdq_cb_data.data = task->cb.data;
>>>>>>>>> +			task->cb.cb(cmdq_cb_data);
>>>>>>>>> +		}
>>>>>>>>> +		task->task_state = (curr_task && err) ? TASK_STATE_ERROR :
>>>>>>>>> +				TASK_STATE_DONE;
>>>>>>>>> +		list_del(&task->list_entry);
>>>>>>>>> +		if (curr_task)
>>>>>>>>> +			break;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	wake_up(&thread->wait_task_done);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_thread *thread = &cmdq->thread[tid];
>>>>>>>>> +	unsigned long flags = 0L;
>>>>>>>>> +	u32 irq_flag;
>>>>>>>>> +
>>>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>>>> +
>>>>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>>>> +
>>>>>>>>> +	/*
>>>>>>>>> +	 * Another CPU core could run "release task" right before we acquire
>>>>>>>>> +	 * the spin lock, and thus reset / disable this GCE thread, so we
>>>>>>>>> +	 * need to check the enable bit of this GCE thread.
>>>>>>>>> +	 */
>>>>>>>>> +	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>>> +	      CMDQ_THR_ENABLED))
>>>>>>>>> +		irq_flag = 0;
>>>>>>>>
>>>>>>>> cmdq_handle_error_done just retuns in this case. Programming this way
>>>>>>>> just makes things confusing. What about:
>>>>>>>>
>>>>>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>> 	      CMDQ_THR_ENABLED)
>>>>>>>> 	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>> else
>>>>>>>> 		irq_flag = 0;
>>>>>>>>
>>>>>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>
>>>>>>> We still need to clear irq_flag if GCE thread is disabled.
>>>>>>> So, I think we can just return here.
>>>>>>>
>>>>>>> 	if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>> 	      CMDQ_THR_ENABLED))
>>>>>>> 		return;
>>>>>>>
>>>>>>> What do you think?
>>>>>>>
>>>>>>
>>>>>> No, you can't just return, you need to unlock the spinlock.
>>>>>> Anyway I would prefer it the other way round, as I put it in my last
>>>>>> mail. Just delete the else branch, we don't need to set irq_flag to zero.
>>>>>
>>>>> Sorry for my previous wrong reply since I merge your comment
>>>>> and CK's comment.
>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>>>>> So, I will put this if condition into cmdq_task_handle_error_result()
>>>>> and then just return it if GCE thread is disabled.
>>>>>
>>>>
>>>> You mean in cmdq_task_handle_done
>>>> We should rename this functions to not create confusion.
>>>
>>> Sorry again. I mean in cmdq_handle_error_done().
>>> This function handles both done and error.
>>>
>>> I agree the function name looks confusion.
>>> I think it can be renamed to cmdq_thread_irq_handler()
>>> since it is used to handle irq for GCE thread.
>>>
>>>> Regards,
>>>> Matthias
>>>
>>> Thanks,
>>> HS
>>>
>>>>>>>>> +
>>>>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = dev;
>>>>>>>>> +	u32 irq_status;
>>>>>>>>> +	int i;
>>>>>>>>> +
>>>>>>>>> +	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
>>>>>>>>> +	irq_status &= CMDQ_IRQ_MASK;
>>>>>>>>> +	irq_status ^= CMDQ_IRQ_MASK;
>>>>>>>>
>>>>>>>> irq_status can be much bigger then 3, which is the number of threads in
>>>>>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here isn't
>>>>>>>> clear to me.
>>>>>>>
>>>>>>> Our GCE hardware has 16 threads, but we only use 3 threads currently.
>>>>>>>
>>>>>>
>>>>>> Ok, but please use bitops here.
>>>>>
>>>>> Will use bitops.
>>>>>
>>>>>>>>> +
>>>>>>>>> +	if (!irq_status)
>>>>>>>>> +		return IRQ_NONE;
>>>>>>>>> +
>>>>>>>>> +	while (irq_status) {
>>>>>>>>> +		i = ffs(irq_status) - 1;
>>>>>>>>> +		irq_status &= ~BIT(i);
>>>>>>>>> +		cmdq_thread_irq_handler(cmdq, i);
>>>>>>>>> +	}
>>>>>>>>
>>>>>>>> Can you explain how the irq status register looks like, that would it
>>>>>>>> make much easier to understand what happens here.
>>>>>>>
>>>>>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
>>>>>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
>>>>>>>
>>>>>>
>>>>>> Thanks, that helped. :)
>>>>>>
>>>>>>>>> +
>>>>>>>>> +	return IRQ_HANDLED;	
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
>>>>>>>>
>>>>>>>> We never check the return values, why do we have them?
>>>>>>>
>>>>>>> Will drop return value.
>>>>>>>
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>>>> +	struct device *dev = cmdq->dev;
>>>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>>>> +	struct cmdq_task *next_task, *prev_task;
>>>>>>>>> +	u32 irq_flag;
>>>>>>>>> +
>>>>>>>>> +	/* suspend GCE thread to ensure consistency */
>>>>>>>>> +	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>>>>>> +
>>>>>>>>> +	/* ISR has handled this error task */
>>>>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>>>>>> +		next_task = list_first_entry_or_null(&thread->task_busy_list,
>>>>>>>>> +				struct cmdq_task, list_entry);
>>>>>>>>> +		if (next_task) /* move to next task */
>>>>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>>>
>>>>>>>> We have to do this, as we suppose that the thread did not reach the jump
>>>>>>>> instruction we put into it's command queue, right?
>>>>>>>
>>>>>>> Yes.
>>>>>>>
>>>>>>
>>>>>> So this should then go into it's own function. In wait_release_work,
>>>>>> something like this:
>>>>>>
>>>>>> if(task->task_state == TASK_STATE_ERROR)
>>>>>>          cmdq_task_handle_error(task)
>>>>>
>>>>> OK.
>>>>> I will write new function cmdq_task_handle_error() to handle error case.
>>>>>
>>>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>>>> +		return -ECANCELED;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>
>>>>>>>> if task_state != ERROR and != DONE. This means that the timeout of
>>>>>>>> task_release_wq has timed out, right?
>>>>>>>
>>>>>>> Yes.
>>>>>>>
>>>>>>>>> +	/*
>>>>>>>>> +	 * Save next_task and prev_task in advance
>>>>>>>>> +	 * since cmdq_handle_error_done will remove list_entry.
>>>>>>>>> +	 */
>>>>>>>>> +	next_task = prev_task = NULL;
>>>>>>>>> +	if (task->list_entry.next != &thread->task_busy_list)
>>>>>>>>> +		next_task = list_next_entry(task, list_entry);
>>>>>>>>> +	if (task->list_entry.prev != &thread->task_busy_list)
>>>>>>>>> +		prev_task = list_prev_entry(task, list_entry);
>>>>>>>>> +
>>>>>>>>> +	/*
>>>>>>>>> +	 * Although IRQ is disabled, GCE continues to execute.
>>>>>>>>> +	 * It may have pending IRQ before GCE thread is suspended,
>>>>>>>>> +	 * so check this condition again.
>>>>>>>>> +	 */
>>>>>>>>
>>>>>>>> The first thing we did in this function was suspending the thread. Why
>>>>>>>> do we need this then?
>>>>>>>
>>>>>>> Because timeout is CPU timeout not GCE timeout, GCE could just finish
>>>>>>> this task before the GCE thread is suspended.
>>>>>>>
>>>>>>
>>>>>> What are the reasons for a timeout? An error has happend, or the task is
>>>>>> still executing.
>>>>>
>>>>>    From GCE's point of view, this task is still executing.
>>>>> But, it could be an error of client.
>>>>> For example, task may never get event if display turn off hardware
>>>>> before waiting for task to finish its work.
>>>>>
>>>>>>>> To be honest this whole functions looks really like a design error. We
>>>>>>>> have to sperate the states much clearer so that it is possible to
>>>>>>>> understand what is happening in the GCE. Isn't it for example posible to
>>>>>>>> have worker queues for timed out tasks and tasks with an error? I'm not
>>>>>>>> sure how to do this, actually I'm not sure if I really understood how
>>>>>>>> this is supposed to work.
>>>>>>>
>>>>>>> GCE doesn't have timeout. The timeout is decided and controlled by CPU,
>>>>>>> so we check timeout in release work.
>>>>>>> For error and done, they are easy to check by register, and we have
>>>>>>> already created release work for timeout. So, I don't think we need to
>>>>>>> create work queue for each case.
>>>>>>>
>>>>>>> What do you think?
>>>>>>>
>>>>>>
>>>>>> I think, if we find in here, that the irq_flag is set, then the the
>>>>>> interrupt handler was triggered and is spinning the spinlock. If this is
>>>>>> not the case, we have a timeout and we handle this.
>>>>>
>>>>> I will write another function to handle error, and handle timeout here.
>>>>>
>>>>>>>> How much do we win, when we patch the thread command queue for every
>>>>>>>> task we add, instead of just taking one task after another from the
>>>>>>>> task_busy_list?
>>>>>>>
>>>>>>> GCE is used to help read/write registers with critical time limitation.
>>>>>>> Sometimes, client may ask to process multiple tasks in a short period
>>>>>>> of time, e.g. display flush multiple tasks for next vblank. So, CMDQ
>>>>>>> shouldn't limit to process one task after another from the
>>>>>>> task_busy_list. Currently, when interrupt or timeout, we will check
>>>>>>> how many tasks are done, and which one is error or timeout.
>>>>>>>
>>>>>>
>>>>>> So I suppose the device driver who use this are interested in throughput
>>>>>> and not in latency. The callback of every
>>>>>>
>>>>>>>>> +	irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>>>> +	cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>>> +	cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>>>> +
>>>>>>>>> +	if (task->task_state == TASK_STATE_DONE) {
>>>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>>>> +		return 0;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	if (task->task_state == TASK_STATE_ERROR) {
>>>>>>>>> +		dev_err(dev, "task 0x%p error\n", task);
>>>>>>>>> +		if (next_task) /* move to next task */
>>>>>>>>> +			cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>> +					   CMDQ_THR_CURR_ADDR);
>>>>>>>>> +		cmdq_thread_resume(thread);
>>>>>>>>> +		return -ECANCELED;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	/* Task is running, so we force to remove it. */
>>>>>>>>> +	dev_err(dev, "task 0x%p timeout or killed\n", task);
>>>>>>>>> +	task->task_state = TASK_STATE_ERROR;
>>>>>>>>> +
>>>>>>>>> +	if (prev_task) {
>>>>>>>>> +		u64 *prev_va = prev_task->va_base;
>>>>>>>>> +		u64 *curr_va = task->va_base;
>>>>>>>>> +
>>>>>>>>> +		/* copy JUMP instruction */
>>>>>>>>> +		prev_va[prev_task->num_cmd - 1] = curr_va[task->num_cmd - 1];
>>>>>>>>> +
>>>>>>>>> +		cmdq_thread_invalidate_fetched_data(thread);
>>>>>>>>> +	} else if (next_task) { /* move to next task */
>>>>>>>>> +		cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>> +				   CMDQ_THR_CURR_ADDR);
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	list_del(&task->list_entry);
>>>>>>>>> +	cmdq_thread_resume(thread);
>>>>>>>>> +
>>>>>>>>> +	/* call cb here to prevent lock */
>>>>>>>>> +	if (task->cb.cb) {
>>>>>>>>> +		struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>> +
>>>>>>>>> +		cmdq_cb_data.err = true;
>>>>>>>>> +		cmdq_cb_data.data = task->cb.data;
>>>>>>>>> +		task->cb.cb(cmdq_cb_data);
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	return -ECANCELED;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_task_wait_release_work(struct work_struct *work_item)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_task *task = container_of(work_item, struct cmdq_task,
>>>>>>>>> +					      release_work);
>>>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>>>> +	struct cmdq_thread *thread = task->thread;
>>>>>>>>> +	unsigned long flags;
>>>>>>>>> +
>>>>>>>>> +	wait_event_timeout(thread->wait_task_done,
>>>>>>>>> +			   task->task_state != TASK_STATE_BUSY,
>>>>>>>>> +			   msecs_to_jiffies(CMDQ_TIMEOUT_MS));
>>>>>>>>> +
>>>>>>>>> +	spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>>>> +	if (task->task_state != TASK_STATE_DONE)
>>>>>>>>> +		cmdq_task_handle_error_result(task);
>>>>>>>>> +	if (list_empty(&thread->task_busy_list))
>>>>>>>>> +		cmdq_thread_disable(cmdq, thread);
>>>>>>>>> +	spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>> +
>>>>>>>>> +	/* release regardless of success or not */
>>>>>>>>> +	clk_disable_unprepare(cmdq->clock);
>>>>>>>>> +	cmdq_task_release(task);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task *task)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = task->cmdq;
>>>>>>>>> +
>>>>>>>>> +	INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
>>>>>>>>> +	queue_work(cmdq->task_release_wq, &task->release_work);
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
>>>>>>>>> +{
>>>>>>>>> +	void *new_buf;
>>>>>>>>> +
>>>>>>>>> +	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
>>>>>>>>> +	if (!new_buf)
>>>>>>>>> +		return -ENOMEM;
>>>>>>>>> +	rec->buf = new_buf;
>>>>>>>>> +	rec->buf_size = size;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_base *cmdq_base;
>>>>>>>>> +	struct resource res;
>>>>>>>>> +	int subsys;
>>>>>>>>> +	u32 base;
>>>>>>>>> +
>>>>>>>>> +	if (of_address_to_resource(dev->of_node, 0, &res))
>>>>>>>>> +		return NULL;
>>>>>>>>> +	base = (u32)res.start;
>>>>>>>>> +
>>>>>>>>> +	subsys = cmdq_subsys_base_to_id(base >> 16);
>>>>>>>>> +	if (subsys < 0)
>>>>>>>>> +		return NULL;
>>>>>>>>> +
>>>>>>>>> +	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
>>>>>>>>> +	if (!cmdq_base)
>>>>>>>>> +		return NULL;
>>>>>>>>> +	cmdq_base->subsys = subsys;
>>>>>>>>> +	cmdq_base->base = base;
>>>>>>>>> +
>>>>>>>>> +	return cmdq_base;
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_register_device);
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>>>> +		    struct cmdq_rec **rec_ptr)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_rec *rec;
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
>>>>>>>>> +	if (!rec)
>>>>>>>>> +		return -ENOMEM;
>>>>>>>>> +	rec->cmdq = dev_get_drvdata(dev);
>>>>>>>>> +	rec->engine_flag = engine_flag;
>>>>>>>>> +	err = cmdq_rec_realloc_cmd_buffer(rec, CMDQ_INITIAL_CMD_BLOCK_SIZE);
>>>>>>>>
>>>>>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
>>>>>>>
>>>>>>> Will do.
>>>>>>>
>>>>>>>>> +	if (err < 0) {
>>>>>>>>> +		kfree(rec);
>>>>>>>>> +		return err;
>>>>>>>>> +	}
>>>>>>>>> +	*rec_ptr = rec;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
>>>>>>>>> +
>>>>>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
>>>>>>>>> +				   u32 arg_a, u32 arg_b)
>>>>>>>>> +{
>>>>>>>>> +	u64 *cmd_ptr;
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	if (WARN_ON(rec->finalized))
>>>>>>>>> +		return -EBUSY;
>>>>>>>>> +	if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
>>>>>>>>> +		return -EINVAL;
>>>>>>>>
>>>>>>>> cmdq_rec_append_command is just called from inside this driver and code
>>>>>>>> is a enum. We can expect it to be correct, no need for this check.
>>>>>>>
>>>>>>> Will drop this check.
>>>>>>>
>>>>>>>>> +	if (unlikely(rec->command_size + CMDQ_INST_SIZE > rec->buf_size)) {
>>>>>>>>
>>>>>>>> command_size is the offset into the buffer to which a new command is
>>>>>>>> written, so this name is highly confusing. I wonder if this would be
>>>>>>>> easier to understand if we redefine command_size to something like the
>>>>>>>> number of commands and divide/multiply CMDQ_INST_SIZE where this is needed.
>>>>>>>
>>>>>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
>>>>>>> dividing CMDQ_INST_SIZE.
>>>>>>> What do you think?
>>>>>>>
>>>>>>>>> +		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
>>>>>>>>> +		if (err < 0)
>>>>>>>>> +			return err;
>>>>>>>>> +	}
>>>>>>>>> +	cmd_ptr = rec->buf + rec->command_size;
>>>>>>>>> +	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
>>>>>>>>> +	rec->command_size += CMDQ_INST_SIZE;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
>>>>>>>>> +		   u32 offset)
>>>>>>>>> +{
>>>>>>>>> +	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
>>>>>>>>> +		    ((base->subsys & CMDQ_SUBSYS_MASK) << CMDQ_SUBSYS_SHIFT);
>>>>>>>>
>>>>>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
>>>>>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
>>>>>>>
>>>>>>> Will drop it.
>>>>>>>
>>>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask)
>>>>>>>>> +{
>>>>>>>>> +	u32 offset_mask = offset;
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	if (mask != 0xffffffff) {
>>>>>>>>> +		err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0, ~mask);
>>>>>>>>> +		if (err < 0)
>>>>>>>>> +			return err;
>>>>>>>>> +		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
>>>>>>>>> +	}
>>>>>>>>> +	return cmdq_rec_write(rec, value, base, offset_mask);
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
>>>>>>>>> +{
>>>>>>>>> +	u32 arg_b;
>>>>>>>>> +
>>>>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>>>> +		return -EINVAL;
>>>>>>>>> +
>>>>>>>>> +	/*
>>>>>>>>> +	 * bit 0-11: wait value
>>>>>>>>> +	 * bit 15: 1 - wait, 0 - no wait
>>>>>>>>> +	 * bit 16-27: update value
>>>>>>>>> +	 * bit 31: 1 - update, 0 - no update
>>>>>>>>> +	 */
>>>>>>>>
>>>>>>>> I don't understand this comments. What are they for?
>>>>>>>
>>>>>>> This is for WFE command. I will comment it.
>>>>>>>
>>>>>>>>> +	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
>>>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event, arg_b);
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
>>>>>>>>> +{
>>>>>>>>> +	if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>>>> +		return -EINVAL;
>>>>>>>>> +
>>>>>>>>> +	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
>>>>>>>>> +				       CMDQ_WFE_UPDATE);
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
>>>>>>>>> +
>>>>>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
>>>>>>>>> +{
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	if (rec->finalized)
>>>>>>>>> +		return 0;
>>>>>>>>> +
>>>>>>>>> +	/* insert EOC and generate IRQ for each command iteration */
>>>>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
>>>>>>>>> +	if (err < 0)
>>>>>>>>> +		return err;
>>>>>>>>> +
>>>>>>>>> +	/* JUMP to end */
>>>>>>>>> +	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
>>>>>>>>> +	if (err < 0)
>>>>>>>>> +		return err;
>>>>>>>>> +
>>>>>>>>
>>>>>>>> Does this need to be atomic?
>>>>>>>> What happens if after CODE_EOC and before CODE_JUMP some
>>>>>>>> write/read/event gets added?
>>>>>>>> What happens if more commands get added to the queue after CODE_JUMP,
>>>>>>>> but before finalized is set to true. Why don't you use atomic functions
>>>>>>>> to access finalized?
>>>>>>>
>>>>>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
>>>>>>> client uses cmdq_rec.
>>>>>>>
>>>>>>
>>>>>> Well I think that rec->finalized tries to implement this, but might
>>>>>> fail, if two kernel threads work on the same cmdq_rec.
>>>>>>
>>>>>>>>> +	rec->finalized = true;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>>>>>> +			 void *data)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = rec->cmdq;
>>>>>>>>> +	struct cmdq_task *task;
>>>>>>>>> +	struct cmdq_task_cb task_cb;
>>>>>>>>> +	struct cmdq_thread *thread;
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>>>>>> +	if (cmdq->suspended) {
>>>>>>>>> +		dev_err(cmdq->dev, "%s is called after suspended\n", __func__);
>>>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>>>> +		return -EPERM;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	err = cmdq_rec_finalize(rec);
>>>>>>>>> +	if (err < 0) {
>>>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>>>> +		return err;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	task_cb.cb = cb;
>>>>>>>>> +	task_cb.data = data;
>>>>>>>>> +	task = cmdq_task_acquire(rec, task_cb);
>>>>>>>>> +	if (!task) {
>>>>>>>>> +		mutex_unlock(&cmdq->task_mutex);
>>>>>>>>> +		return -EFAULT;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	thread = &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
>>>>>>>>> +	cmdq_task_exec(task, thread);
>>>>>>>>> +	cmdq_task_wait_release_schedule(task);
>>>>>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
>>>>>>>>> +
>>>>>>>>> +struct cmdq_flush_completion {
>>>>>>>>> +	struct completion cmplt;
>>>>>>>>> +	bool err;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_flush_completion *cmplt = data.data;
>>>>>>>>> +
>>>>>>>>> +	cmplt->err = data.err;
>>>>>>>>> +	complete(&cmplt->cmplt);
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_flush_completion cmplt;
>>>>>>>>> +	int err;
>>>>>>>>> +
>>>>>>>>> +	init_completion(&cmplt.cmplt);
>>>>>>>>> +	err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
>>>>>>>>> +	if (err < 0)
>>>>>>>>> +		return err;
>>>>>>>>> +	wait_for_completion(&cmplt.cmplt);
>>>>>>>>> +	return cmplt.err ? -EFAULT : 0;
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
>>>>>>>>> +
>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
>>>>>>>>> +{
>>>>>>>>> +	kfree(rec->buf);
>>>>>>>>> +	kfree(rec);
>>>>>>>>> +}
>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
>>>>>>>>> +
>>>>>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq_thread *thread;
>>>>>>>>> +	int i;
>>>>>>>>> +
>>>>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>>>> +		thread = &cmdq->thread[i];
>>>>>>>>> +		if (!list_empty(&thread->task_busy_list))
>>>>>>>>> +			return false;
>>>>>>>>> +	}
>>>>>>>>> +	return true;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_suspend(struct device *dev)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>>>> +	u32 exec_threads;
>>>>>>>>> +
>>>>>>>>> +	mutex_lock(&cmdq->task_mutex);
>>>>>>>>> +	cmdq->suspended = true;
>>>>>>>>> +	mutex_unlock(&cmdq->task_mutex);
>>>>>>>>> +
>>>>>>>>> +	exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
>>>>>>>>> +	if ((exec_threads & CMDQ_THR_EXECUTING) && !cmdq_task_is_empty(cmdq)) {
>>>>>>>>> +		dev_err(dev, "wait active tasks timeout.\n");
>>>>>>>>> +		flush_workqueue(cmdq->task_release_wq);
>>>>>>>>> +	}
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_resume(struct device *dev)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>>>> +
>>>>>>>>> +	cmdq->suspended = false;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_remove(struct platform_device *pdev)
>>>>>>>>> +{
>>>>>>>>> +	struct cmdq *cmdq = platform_get_drvdata(pdev);
>>>>>>>>> +
>>>>>>>>> +	destroy_workqueue(cmdq->task_release_wq);
>>>>>>>>> +	cmdq->task_release_wq = NULL;
>>>>>>>>> +	return 0;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static int cmdq_probe(struct platform_device *pdev)
>>>>>>>>> +{
>>>>>>>>> +	struct device *dev = &pdev->dev;
>>>>>>>>> +	struct device_node *node = dev->of_node;
>>>>>>>>> +	struct resource *res;
>>>>>>>>> +	struct cmdq *cmdq;
>>>>>>>>> +	int err, i;
>>>>>>>>> +
>>>>>>>>> +	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
>>>>>>>>> +	if (!cmdq)
>>>>>>>>> +		return -ENOMEM;
>>>>>>>>> +	cmdq->dev = dev;
>>>>>>>>> +
>>>>>>>>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>>>>> +	cmdq->base = devm_ioremap_resource(dev, res);
>>>>>>>>> +	if (IS_ERR(cmdq->base)) {
>>>>>>>>> +		dev_err(dev, "failed to ioremap gce\n");
>>>>>>>>> +		return PTR_ERR(cmdq->base);
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	cmdq->irq = irq_of_parse_and_map(node, 0);
>>>>>>>>> +	if (!cmdq->irq) {
>>>>>>>>> +		dev_err(dev, "failed to get irq\n");
>>>>>>>>> +		return -EINVAL;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
>>>>>>>>> +		dev, cmdq->base, cmdq->irq);
>>>>>>>>> +
>>>>>>>>> +	mutex_init(&cmdq->task_mutex);
>>>>>>>>> +	spin_lock_init(&cmdq->exec_lock);
>>>>>>>>> +	cmdq->task_release_wq = alloc_ordered_workqueue(
>>>>>>>>> +			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
>>>>>>>>> +			"cmdq_task_wait_release");
>>>>>>>>> +
>>>>>>>>> +	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>>>> +		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
>>>>>>>>> +				CMDQ_THR_SHIFT * i;
>>>>>>>>> +		init_waitqueue_head(&cmdq->thread[i].wait_task_done);
>>>>>>>>> +		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	platform_set_drvdata(pdev, cmdq);
>>>>>>>>> +
>>>>>>>>> +	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
>>>>>>>>> +			       CMDQ_DRIVER_DEVICE_NAME, cmdq);
>>>>>>>>> +	if (err < 0) {
>>>>>>>>> +		dev_err(dev, "failed to register ISR (%d)\n", err);
>>>>>>>>> +		goto fail;
>>>>>>>>> +	}
>>>>>>>>> +
>>>>>>>>> +	cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
>>>>>>>>> +	if (IS_ERR(cmdq->clock)) {
>>>>>>>>> +		dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
>>>>>>>>> +		err = PTR_ERR(cmdq->clock);
>>>>>>>>> +		goto fail;
>>>>>>>>> +	}
>>>>>>>>> +	return 0;
>>>>>>>>> +
>>>>>>>>> +fail:
>>>>>>>>> +	cmdq_remove(pdev);
>>>>>>>>> +	return err;
>>>>>>>>> +}
>>>>>>>>> +
>>>>>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
>>>>>>>>> +	.suspend = cmdq_suspend,
>>>>>>>>> +	.resume = cmdq_resume,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static const struct of_device_id cmdq_of_ids[] = {
>>>>>>>>> +	{.compatible = "mediatek,mt8173-gce",},
>>>>>>>>> +	{}
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +static struct platform_driver cmdq_drv = {
>>>>>>>>> +	.probe = cmdq_probe,
>>>>>>>>> +	.remove = cmdq_remove,
>>>>>>>>> +	.driver = {
>>>>>>>>> +		.name = CMDQ_DRIVER_DEVICE_NAME,
>>>>>>>>> +		.owner = THIS_MODULE,
>>>>>>>>> +		.pm = &cmdq_pm_ops,
>>>>>>>>> +		.of_match_table = cmdq_of_ids,
>>>>>>>>> +	}
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +builtin_platform_driver(cmdq_drv);
>>>>>>>>> diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
>>>>>>>>> new file mode 100644
>>>>>>>>> index 0000000..60eef3d
>>>>>>>>> --- /dev/null
>>>>>>>>> +++ b/include/soc/mediatek/cmdq.h
>>>>>>>>> @@ -0,0 +1,197 @@
>>>>>>>>> +/*
>>>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>>>>>> + *
>>>>>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>>>>>> + * it under the terms of the GNU General Public License version 2 as
>>>>>>>>> + * published by the Free Software Foundation.
>>>>>>>>> + *
>>>>>>>>> + * This program is distributed in the hope that it will be useful,
>>>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>>>>>> + * GNU General Public License for more details.
>>>>>>>>> + */
>>>>>>>>> +
>>>>>>>>> +#ifndef __MTK_CMDQ_H__
>>>>>>>>> +#define __MTK_CMDQ_H__
>>>>>>>>> +
>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>> +#include <linux/types.h>
>>>>>>>>> +
>>>>>>>>> +enum cmdq_eng {
>>>>>>>>> +	CMDQ_ENG_DISP_AAL,
>>>>>>>>> +	CMDQ_ENG_DISP_COLOR0,
>>>>>>>>> +	CMDQ_ENG_DISP_COLOR1,
>>>>>>>>> +	CMDQ_ENG_DISP_DPI0,
>>>>>>>>> +	CMDQ_ENG_DISP_DSI0,
>>>>>>>>> +	CMDQ_ENG_DISP_DSI1,
>>>>>>>>> +	CMDQ_ENG_DISP_GAMMA,
>>>>>>>>> +	CMDQ_ENG_DISP_OD,
>>>>>>>>> +	CMDQ_ENG_DISP_OVL0,
>>>>>>>>> +	CMDQ_ENG_DISP_OVL1,
>>>>>>>>> +	CMDQ_ENG_DISP_PWM0,
>>>>>>>>> +	CMDQ_ENG_DISP_PWM1,
>>>>>>>>> +	CMDQ_ENG_DISP_RDMA0,
>>>>>>>>> +	CMDQ_ENG_DISP_RDMA1,
>>>>>>>>> +	CMDQ_ENG_DISP_RDMA2,
>>>>>>>>> +	CMDQ_ENG_DISP_UFOE,
>>>>>>>>> +	CMDQ_ENG_DISP_WDMA0,
>>>>>>>>> +	CMDQ_ENG_DISP_WDMA1,
>>>>>>>>> +	CMDQ_ENG_MAX,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +/* events for CMDQ and display */
>>>>>>>>> +enum cmdq_event {
>>>>>>>>> +	/* Display start of frame(SOF) events */
>>>>>>>>> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
>>>>>>>>> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
>>>>>>>>> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
>>>>>>>>> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
>>>>>>>>> +	/* Display end of frame(EOF) events */
>>>>>>>>> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
>>>>>>>>> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
>>>>>>>>> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
>>>>>>>>> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
>>>>>>>>> +	/* Mutex end of frame(EOF) events */
>>>>>>>>> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
>>>>>>>>> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
>>>>>>>>> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
>>>>>>>>> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
>>>>>>>>> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
>>>>>>>>> +	/* Display underrun events */
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
>>>>>>>>> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
>>>>>>>>> +	/* Keep this at the end of HW events */
>>>>>>>>> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +struct cmdq_cb_data {
>>>>>>>>> +	bool	err;
>>>>>>>>> +	void	*data;
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
>>>>>>>>> +
>>>>>>>>> +struct cmdq_task;
>>>>>>>>> +struct cmdq;
>>>>>>>>> +
>>>>>>>>> +struct cmdq_rec {
>>>>>>>>> +	struct cmdq		*cmdq;
>>>>>>>>> +	u64			engine_flag;
>>>>>>>>> +	size_t			command_size;
>>>>>>>>> +	void			*buf;
>>>>>>>>> +	size_t			buf_size;
>>>>>>>>> +	bool			finalized;
>>>>>>>>> +};
>>>>>>
>>>>>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task for that
>>>>>> and this way make the driver less complex?
>>>>>
>>>>> There are two main reasons for cmdq_rec.
>>>>> 1. It is slow to access dma too frequently.
>>>>>       So, we append commands to cacheable memory, and then flush to dma.
>>>>> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
>>>>>       If we merge them, we need to take care of some synchronization
>>>>>       issues.
>>>>>
>>>>>>>>> +
>>>>>>>>> +struct cmdq_base {
>>>>>>>>> +	int	subsys;
>>>>>>>>> +	u32	base;
>>>>>>>>
>>>>>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base >> 16)
>>>>>>>> so we can get rid of the struct, right?
>>>>>>>
>>>>>>> Current subsys method is based on previous comment from Daniel Kurtz.
>>>>>>> Please take a look of our previous discussion.
>>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>>>>> Thanks.
>>>>>>>
>>>>>>
>>>>>> I have to look deeper into this, but from what I read, the proposal from
>>>>>> Daniel [1] seems good to me.
>>>>>>
>>>>>> [1] https://patchwork.kernel.org/patch/8068311/
>>>>>
>>>>> The initial proposal has some problem, so please see the follow-up
>>>>> discussions. Thanks.
>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>>>
>>>>>
>>>>>>>>> +};
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_register_device() - register device which needs CMDQ
>>>>>>>>> + * @dev:		device
>>>>>>>>> + *
>>>>>>>>> + * Return: cmdq_base pointer or NULL for failed
>>>>>>>>> + */
>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_create() - create command queue record
>>>>>>>>> + * @dev:		device
>>>>>>>>> + * @engine_flag:	command queue engine flag
>>>>>>>>> + * @rec_ptr:		command queue record pointer to retrieve cmdq_rec
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>>>> +		    struct cmdq_rec **rec_ptr);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_write() - append write command to the command queue record
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + * @value:	the specified target register value
>>>>>>>>> + * @base:	the command queue base
>>>>>>>>> + * @offset:	register offset from module base
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
>>>>>>>>> +		   struct cmdq_base *base, u32 offset);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_write_mask() - append write command with mask to the command
>>>>>>>>> + *			   queue record
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + * @value:	the specified target register value
>>>>>>>>> + * @base:	the command queue base
>>>>>>>>> + * @offset:	register offset from module base
>>>>>>>>> + * @mask:	the specified target register mask
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>>>> +			struct cmdq_base *base, u32 offset, u32 mask);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_wfe() - append wait for event command to the command queue reco	rd
>>>>>>>>
>>>>>>>> reco rd -> record
>>>>>>>
>>>>>>> Will fix it.
>>>>>>>
>>>>>>>> Regards,
>>>>>>>> Matthias
>>>>>>>>
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + * @event:	the desired event type to "wait and CLEAR"
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_clear_event() - append clear event command to the command queue
>>>>>>>>> + *			    record
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + * @event:	the desired event to be cleared
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + *
>>>>>>>>> + * Trigger CMDQ to execute the recorded commands. Note that this is a
>>>>>>>>> + * synchronous flush function. When the function returned, the recorded
>>>>>>>>> + * commands have been done.
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
>>>>>>>>> + *			    commands and call back after ISR is finished
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + * @cb:		called in the end of CMDQ ISR
>>>>>>>>> + * @data:	this data will pass back to cb
>>>>>>>>> + *
>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>> + *
>>>>>>>>> + * Trigger CMDQ to asynchronously execute the recorded commands and call back
>>>>>>>>> + * after ISR is finished. Note that this is an ASYNC function. When the function
>>>>>>>>> + * returned, it may or may not be finished. The ISR callback function is called
>>>>>>>>> + * in the end of ISR.
>>>>>>
>>>>>> "The callback is called from the ISR."
>>>>>>
>>>>>> Regards,
>>>>>> Matthias
>>>>>>
>>>>>>>>> + */
>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec, cmdq_async_flush_cb cb,
>>>>>>>>> +			 void *data);
>>>>>>>>> +
>>>>>>>>> +/**
>>>>>>>>> + * cmdq_rec_destroy() - destroy command queue record
>>>>>>>>> + * @rec:	the command queue record
>>>>>>>>> + */
>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
>>>>>>>>> +
>>>>>>>>> +#endif	/* __MTK_CMDQ_H__ */
>>>>>>>>>
>>>>>>>
>>>>>>> Thanks,
>>>>>>> HS
>>>>>>>
>>>>>
>>>>> Thanks,
>>>>> HS
>>>>>
>>>
>>>
>
>


From jassisinghbrar at gmail.com  Fri Jun  3 06:11:52 2016
From: jassisinghbrar at gmail.com (Jassi Brar)
Date: Fri, 3 Jun 2016 18:41:52 +0530
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <57516774.5080008@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com>
 <1464683762.14604.59.camel@mtksdaap41> <574DEE40.9010008@gmail.com>
 <1464775020.11122.40.camel@mtksdaap41> <574FF264.7050209@gmail.com>
 <1464934356.15175.31.camel@mtksdaap41> <57516774.5080008@gmail.com>
Message-ID: <CABb+yY2aOncqM-fOiq_ZBink0Fth6Drd30ZMe3-bXNkodW2dVA@mail.gmail.com>

On Fri, Jun 3, 2016 at 4:48 PM, Matthias Brugger <matthias.bgg at gmail.com> wrote:
> On 03/06/16 08:12, Horng-Shyang Liao wrote:
>> On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:

>>> I keep thinking about how to get rid of the two data structures,
>>> task_busy_list and the task_release_wq. We need the latter for the only
>>> sake of getting a timeout.
>>>
>>> Did you have a look on how the mailbox framework handles this?
>>> By the way, what is the reason to not implement the whole driver as a
>>> mailbox controller? For me, this driver looks like a good fit.
>>
>>
>> CMDQ needs to encode commands for GCE hardware. We think this behavior
>> should be put in CMDQ driver, and client just call CMDQ functions.
>> Therefore, if we want to use mailbox framework, cmdq_rec must be
>> mailbox client, and the others must be mailbox controller.
>>
>
> You mean the functions to fill the cmdq_rec and execute it?
> I think this should be part of the driver.
>
> Jassi, can you have a look on the interface this driver exports [0].
> They are needed to actually create the message which will be send.
> Could something like this be part of a mailbox driver?
>
> [0] https://patchwork.kernel.org/patch/9140221/
>
Packet creating/parsing should not be a part of controller driver. As
the log of this patch says, today it is used for only display but in
future it could work with other h/w as well, so it makes sense to have
mailbox api do the message queuing, the controller driver do the
send/receive and client drivers implement display and other h/w
specific packaging of data (protocol handling).

So yes, I think this could use mailbox api.

Cheers.


From matthias.bgg at gmail.com  Fri Jun  3 06:13:00 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 15:13:00 +0200
Subject: [PATCH] iommu: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8jzj-00046r-Pd@rmk-PC.armlinux.org.uk>
References: <E1b8jzj-00046r-Pd@rmk-PC.armlinux.org.uk>
Message-ID: <5751825C.40703@gmail.com>



On 03/06/16 09:58, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
>
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---

Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

>   drivers/iommu/mtk_iommu.c | 13 ++++++++++---
>   1 file changed, 10 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
> index c3043d8754e3..c036df1c49ca 100644
> --- a/drivers/iommu/mtk_iommu.c
> +++ b/drivers/iommu/mtk_iommu.c
> @@ -557,6 +557,11 @@ static int compare_of(struct device *dev, void *data)
>   	return dev->of_node == data;
>   }
>
> +static void release_of(struct device *dev, void *data)
> +{
> +	of_node_put(data);
> +}
> +
>   static int mtk_iommu_bind(struct device *dev)
>   {
>   	struct mtk_iommu_data *data = dev_get_drvdata(dev);
> @@ -630,17 +635,19 @@ static int mtk_iommu_probe(struct platform_device *pdev)
>   			continue;
>
>   		plarbdev = of_find_device_by_node(larbnode);
> -		of_node_put(larbnode);
>   		if (!plarbdev) {
>   			plarbdev = of_platform_device_create(
>   						larbnode, NULL,
>   						platform_bus_type.dev_root);
> -			if (!plarbdev)
> +			if (!plarbdev) {
> +				of_node_put(larbnode);
>   				return -EPROBE_DEFER;
> +			}
>   		}
>   		data->smi_imu.larb_imu[i].dev = &plarbdev->dev;
>
> -		component_match_add(dev, &match, compare_of, larbnode);
> +		component_match_add_release(dev, &match, release_of,
> +					    compare_of, larbnode);
>   	}
>
>   	platform_set_drvdata(pdev, data);
>


From matthias.bgg at gmail.com  Fri Jun  3 06:34:04 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 15:34:04 +0200
Subject: [PATCH v2,3/5] dt-bindings: mtu3: add devicetree bindings
In-Reply-To: <1464673962-11506-4-git-send-email-chunfeng.yun@mediatek.com>
References: <1464673962-11506-1-git-send-email-chunfeng.yun@mediatek.com>
 <1464673962-11506-4-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <5751874C.9070100@gmail.com>



On 31/05/16 07:52, Chunfeng Yun wrote:
> add a DT binding doc for MediaTek USB3 DRD driver
>
> Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> ---
>   Documentation/devicetree/bindings/usb/mtu3.txt |   85 ++++++++++++++++++++++++
>   1 file changed, 85 insertions(+)
>   create mode 100644 Documentation/devicetree/bindings/usb/mtu3.txt
>
> diff --git a/Documentation/devicetree/bindings/usb/mtu3.txt b/Documentation/devicetree/bindings/usb/mtu3.txt
> new file mode 100644
> index 0000000..571ae8b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/usb/mtu3.txt

The file should be called mt8173-mtu3.txt to be in line with mt8173-xhci.txt

> @@ -0,0 +1,85 @@
> +The device node for Mediatek USB3.0 DRD controller
> +
> +Required properties:
> + - compatible : should be "mediatek,mt8173-mtu3"
> + - reg : specifies physical base address and size of the registers
> + - reg-names: should be "mac" for device IP and "ippc" for IP port control
> + - interrupts : interrupt used by the device IP
> + - power-domains : a phandle to USB power domain node to control USB's
> +	mtcmos
> + - vusb33-supply : regulator of USB avdd3.3v
> + - clocks : a list of phandle + clock-specifier pairs, one for each
> +	entry in clock-names
> + - clock-names : must contain "sys_ck" for clock of controller;
> +	"wakeup_deb_p0" and "wakeup_deb_p1" are optional, they are
> +	depends on "mediatek,enable-wakeup"
> + - phys : a list of phandle + phy specifier pairs
> + - dr_mode : should be one of "host", "peripheral" or "otg",
> +	refer to usb/generic.txt
> +
> +Optional properties:
> + - #address-cells, #size-cells : should be '2' if the device has sub-nodes
> +	with 'reg' property
> + - ranges : allows valid 1:1 translation between child's address space and
> +	parent's address space
> + - extcon : external connector for vbus and idpin changes detection, needed
> +	when supports dual-role mode.
> + - vbus-supply : reference to the VBUS regulator, needed when supports
> +	dual-role mode.
> + - pinctl-names : a pinctrl state named "default" must be defined,
> +	"id_float" and "id_ground" are optinal which depends on
> +	"mediatek,enable-manual-drd"

So pinctrl is optional, but if you put it, you need at least "default"?

Apart from that, DT maintainers prefer to have the binding patches at 
the beginning of a series, so it's easier for them to not oversee any.

Regards,
Matthias

> + - pinctrl-0 : pin control group
> +	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
> +
> + - maximum-speed : valid arguments are "super-speed", "high-speed" and
> +	"full-speed"; refer to usb/generic.txt
> + - enable-manual-drd : supports manual dual-role switch via sysfs; only used
> +	when receptacle is TYPE-A and also wants to support dual-role mode.
> + - mediatek,enable-wakeup : supports ip sleep wakeup used by host mode
> + - mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
> +	control register, it depends on "mediatek,enable-wakeup".
> +
> +Sub-nodes:
> +The xhci should be added as subnode to mtu3 as shown in the following example
> +if host mode is enabled. The DT binding details of xhci can be found in:
> +Documentation/devicetree/bindings/usb/mt8173-xhci.txt
> +
> +Example:
> +ssusb: usb at 11271000 {
> +	compatible = "mediatek,mt8173-mtu3";
> +	reg = <0 0x11271000 0 0x3000>,
> +	      <0 0x11280700 0 0x0100>;
> +	reg-names = "mac", "ippc";
> +	interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
> +	phys = <&phy_port0 PHY_TYPE_USB3>,
> +	       <&phy_port1 PHY_TYPE_USB2>;
> +	power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
> +	clocks = <&topckgen CLK_TOP_USB30_SEL>,
> +		 <&pericfg CLK_PERI_USB0>,
> +		 <&pericfg CLK_PERI_USB1>;
> +	clock-names = "sys_ck",
> +		      "wakeup_deb_p0",
> +		      "wakeup_deb_p1";
> +	vusb33-supply = <&mt6397_vusb_reg>;
> +	vbus-supply = <&usb_p0_vbus>;
> +	extcon = <&extcon_usb>;
> +	dr_mode = "otg";
> +	mediatek,enable-wakeup;
> +	mediatek,syscon-wakeup = <&pericfg>;
> +	#address-cells = <2>;
> +	#size-cells = <2>;
> +	ranges;
> +	status = "disabled";
> +
> +	usb_host: xhci at 11270000 {
> +		compatible = "mediatek,mt8173-xhci";
> +		reg = <0 0x11270000 0 0x1000>;
> +		interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
> +		power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
> +		clocks = <&topckgen CLK_TOP_USB30_SEL>;
> +		clock-names = "sys_ck";
> +		vusb33-supply = <&mt6397_vusb_reg>;
> +		status = "disabled";
> +	};
> +};
>


From rmk+kernel at armlinux.org.uk  Fri Jun  3 07:21:19 2016
From: rmk+kernel at armlinux.org.uk (Russell King)
Date: Fri, 03 Jun 2016 15:21:19 +0100
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <20160603141548.GC1041@n2100.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
Message-ID: <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>

Add common OF-based component functionality for matching devices by
device node, and releasing the device node at the appropraite time.

Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
---
 drivers/of/Makefile          |  2 +-
 drivers/of/of_component.c    | 41 +++++++++++++++++++++++++++++++++++++++++
 include/linux/of_component.h | 14 ++++++++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)
 create mode 100644 drivers/of/of_component.c
 create mode 100644 include/linux/of_component.h

diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d458aa..6a4a5e2c0839 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,4 @@
-obj-y = base.o device.o platform.o
+obj-y = base.o of_component.o device.o platform.o
 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/of_component.c b/drivers/of/of_component.c
new file mode 100644
index 000000000000..41e6e817d264
--- /dev/null
+++ b/drivers/of/of_component.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_component.h>
+#include <linux/of_graph.h>
+
+static void component_release_of(struct device *dev, void *data)
+{
+	of_node_put(data);
+}
+
+void component_match_add_of_compare(struct device *master,
+	struct component_match **matchptr,
+	int (*compare)(struct device *, void *), struct device_node *node)
+{
+	of_node_get(node);
+	component_match_add_release(master, matchptr, component_release_of,
+				    compare, node);
+}
+EXPORT_SYMBOL_GPL(component_match_add_of_compare);
+
+static void component_compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+void component_match_add_of(struct device *master,
+	struct component_match **matchptr, struct device_node *node)
+{
+	of_node_get(node);
+	component_match_add_release(master, matchptr, component_release_of,
+				    component_compare_of, node);
+}
+EXPORT_SYMBOL_GPL(component_match_add_of);
diff --git a/include/linux/of_component.h b/include/linux/of_component.h
new file mode 100644
index 000000000000..a8170ba3b786
--- /dev/null
+++ b/include/linux/of_component.h
@@ -0,0 +1,14 @@
+#ifndef __LINUX_COMPONENT_OF_H
+#define __LINUX_COMPONENT_OF_H
+
+struct component_match;
+struct device;
+struct device_node;
+
+void component_match_add_of_compare(struct device *master,
+	struct component_match **matchptr,
+	int (*compare)(struct device *, void *), struct device_node *node);
+void component_match_add_of(struct device *master,
+	struct component_match **matchptr, struct device_node *node);
+
+#endif
-- 
2.1.0



From rmk+kernel at armlinux.org.uk  Fri Jun  3 07:21:30 2016
From: rmk+kernel at armlinux.org.uk (Russell King)
Date: Fri, 03 Jun 2016 15:21:30 +0100
Subject: [PATCH v2 3/3] iommu: convert DT component matching to
 component_match_add_release()
In-Reply-To: <20160603141548.GC1041@n2100.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
Message-ID: <E1b8pyc-0005OV-7X@rmk-PC.armlinux.org.uk>

Convert DT component matching to use component_match_add_release().

Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
---
 drivers/iommu/mtk_iommu.c | 14 ++++++--------
 1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index c3043d8754e3..71cf62af4e24 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -24,6 +24,7 @@
 #include <linux/iopoll.h>
 #include <linux/list.h>
 #include <linux/of_address.h>
+#include <linux/of_component.h>
 #include <linux/of_iommu.h>
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
@@ -552,11 +553,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
 	return 0;
 }
 
-static int compare_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 static int mtk_iommu_bind(struct device *dev)
 {
 	struct mtk_iommu_data *data = dev_get_drvdata(dev);
@@ -630,17 +626,19 @@ static int mtk_iommu_probe(struct platform_device *pdev)
 			continue;
 
 		plarbdev = of_find_device_by_node(larbnode);
-		of_node_put(larbnode);
 		if (!plarbdev) {
 			plarbdev = of_platform_device_create(
 						larbnode, NULL,
 						platform_bus_type.dev_root);
-			if (!plarbdev)
+			if (!plarbdev) {
+				of_node_put(larbnode);
 				return -EPROBE_DEFER;
+			}
 		}
 		data->smi_imu.larb_imu[i].dev = &plarbdev->dev;
 
-		component_match_add(dev, &match, compare_of, larbnode);
+		component_match_add_of(dev, &match, larbnode);
+		of_node_put(larbnode);
 	}
 
 	platform_set_drvdata(pdev, data);
-- 
2.1.0



From rmk+kernel at armlinux.org.uk  Fri Jun  3 07:21:25 2016
From: rmk+kernel at armlinux.org.uk (Russell King)
Date: Fri, 03 Jun 2016 15:21:25 +0100
Subject: [PATCH v2 2/3] drm: convert DT component matching to
 component_match_add_release()
In-Reply-To: <20160603141548.GC1041@n2100.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
Message-ID: <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>

Convert DT component matching to use component_match_add_release().

Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
---
 drivers/gpu/drm/arm/hdlcd_drv.c             |  9 +++------
 drivers/gpu/drm/armada/armada_drv.c         |  8 ++------
 drivers/gpu/drm/drm_of.c                    |  6 ++++--
 drivers/gpu/drm/etnaviv/etnaviv_drv.c       | 11 ++---------
 drivers/gpu/drm/msm/msm_drv.c               |  8 ++------
 drivers/gpu/drm/msm/msm_drv.h               |  1 +
 drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 +++---------
 drivers/gpu/drm/sti/sti_drv.c               |  9 ++-------
 drivers/gpu/drm/tilcdc/tilcdc_external.c    |  8 ++------
 9 files changed, 21 insertions(+), 51 deletions(-)

diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
index b987c63ba8d6..f00845c25795 100644
--- a/drivers/gpu/drm/arm/hdlcd_drv.c
+++ b/drivers/gpu/drm/arm/hdlcd_drv.c
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/list.h>
+#include <linux/of_component.h>
 #include <linux/of_graph.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/pm_runtime.h>
@@ -443,11 +444,6 @@ static const struct component_master_ops hdlcd_master_ops = {
 	.unbind		= hdlcd_drm_unbind,
 };
 
-static int compare_dev(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 static int hdlcd_probe(struct platform_device *pdev)
 {
 	struct device_node *port, *ep;
@@ -474,7 +470,8 @@ static int hdlcd_probe(struct platform_device *pdev)
 		return -EAGAIN;
 	}
 
-	component_match_add(&pdev->dev, &match, compare_dev, port);
+	component_match_add_of(&pdev->dev, &match, port);
+	of_node_put(port);
 
 	return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
 					       match);
diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
index 439824a61aa5..f2716d20c257 100644
--- a/drivers/gpu/drm/armada/armada_drv.c
+++ b/drivers/gpu/drm/armada/armada_drv.c
@@ -9,6 +9,7 @@
 #include <linux/component.h>
 #include <linux/module.h>
 #include <linux/of_graph.h>
+#include <linux/of_component.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_of.h>
@@ -227,11 +228,6 @@ static void armada_drm_unbind(struct device *dev)
 	drm_put_dev(dev_get_drvdata(dev));
 }
 
-static int compare_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 static int compare_dev_name(struct device *dev, void *data)
 {
 	const char *name = data;
@@ -255,7 +251,7 @@ static void armada_add_endpoints(struct device *dev,
 			continue;
 		}
 
-		component_match_add(dev, match, compare_of, remote);
+		component_match_add_of(dev, match, remote);
 		of_node_put(remote);
 	}
 }
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index bc98bb94264d..d13fc633aeea 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -1,6 +1,7 @@
 #include <linux/component.h>
 #include <linux/export.h>
 #include <linux/list.h>
+#include <linux/of_component.h>
 #include <linux/of_graph.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
@@ -101,7 +102,7 @@ int drm_of_component_probe(struct device *dev,
 			continue;
 		}
 
-		component_match_add(dev, &match, compare_of, port);
+		component_match_add_of(dev, &match, compare_of, port);
 		of_node_put(port);
 	}
 
@@ -140,7 +141,8 @@ int drm_of_component_probe(struct device *dev,
 				continue;
 			}
 
-			component_match_add(dev, &match, compare_of, remote);
+			component_match_add_of_compare(dev, &match, compare_of,
+						       remote);
 			of_node_put(remote);
 		}
 		of_node_put(port);
diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
index 3d4f56df8359..a0e0764a6269 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
@@ -15,6 +15,7 @@
  */
 
 #include <linux/component.h>
+#include <linux/of_component.h>
 #include <linux/of_platform.h>
 
 #include "etnaviv_drv.h"
@@ -606,13 +607,6 @@ static const struct component_master_ops etnaviv_master_ops = {
 	.unbind = etnaviv_unbind,
 };
 
-static int compare_of(struct device *dev, void *data)
-{
-	struct device_node *np = data;
-
-	return dev->of_node == np;
-}
-
 static int compare_str(struct device *dev, void *data)
 {
 	return !strcmp(dev_name(dev), data);
@@ -635,8 +629,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
 			if (!core_node)
 				break;
 
-			component_match_add(&pdev->dev, &match, compare_of,
-					    core_node);
+			component_match_add_of(&pdev->dev, &match, core_node);
 			of_node_put(core_node);
 		}
 	} else if (dev->platform_data) {
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 9c654092ef78..5293aa775c5a 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -800,11 +800,6 @@ static const struct dev_pm_ops msm_pm_ops = {
  * NOTE: duplication of the same code as exynos or imx (or probably any other).
  * so probably some room for some helpers
  */
-static int compare_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 static int add_components(struct device *dev, struct component_match **matchptr,
 		const char *name)
 {
@@ -818,7 +813,8 @@ static int add_components(struct device *dev, struct component_match **matchptr,
 		if (!node)
 			break;
 
-		component_match_add(dev, matchptr, compare_of, node);
+		component_match_add_of(dev, matchptr, node);
+		of_node_put(node);
 	}
 
 	return 0;
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index 5b2963f32291..66dafecaf115 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -30,6 +30,7 @@
 #include <linux/list.h>
 #include <linux/iommu.h>
 #include <linux/types.h>
+#include <linux/of_component.h>
 #include <linux/of_graph.h>
 #include <linux/of_device.h>
 #include <asm/sizes.h>
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index a409d1f703cb..84c50f6770c3 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -22,6 +22,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/module.h>
+#include <linux/of_component.h>
 #include <linux/of_graph.h>
 #include <linux/component.h>
 
@@ -414,13 +415,6 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = {
 				rockchip_drm_sys_resume)
 };
 
-static int compare_of(struct device *dev, void *data)
-{
-	struct device_node *np = data;
-
-	return dev->of_node == np;
-}
-
 static void rockchip_add_endpoints(struct device *dev,
 				   struct component_match **match,
 				   struct device_node *port)
@@ -439,7 +433,7 @@ static void rockchip_add_endpoints(struct device *dev,
 			continue;
 		}
 
-		component_match_add(dev, match, compare_of, remote);
+		component_match_add_of(dev, match, remote);
 		of_node_put(remote);
 	}
 }
@@ -518,7 +512,7 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
 			is_support_iommu = false;
 		}
 
-		component_match_add(dev, &match, compare_of, port->parent);
+		component_match_add_of(dev, &match, port->parent);
 		of_node_put(port);
 	}
 
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
index 872495e72294..aeb2787c9bd4 100644
--- a/drivers/gpu/drm/sti/sti_drv.c
+++ b/drivers/gpu/drm/sti/sti_drv.c
@@ -10,6 +10,7 @@
 #include <linux/debugfs.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of_component.h>
 #include <linux/of_platform.h>
 
 #include <drm/drm_atomic.h>
@@ -341,11 +342,6 @@ static struct drm_driver sti_driver = {
 	.minor = DRIVER_MINOR,
 };
 
-static int compare_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 static int sti_bind(struct device *dev)
 {
 	return drm_platform_init(&sti_driver, to_platform_device(dev));
@@ -375,8 +371,7 @@ static int sti_platform_probe(struct platform_device *pdev)
 	child_np = of_get_next_available_child(node, NULL);
 
 	while (child_np) {
-		component_match_add(dev, &match, compare_of, child_np);
-		of_node_put(child_np);
+		component_match_add_of(dev, &match, child_np);
 		child_np = of_get_next_available_child(node, child_np);
 	}
 
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
index 03acb4f99982..1b3fd9fa4bef 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/component.h>
+#include <linux/of_component.h>
 #include <linux/of_graph.h>
 
 #include "tilcdc_drv.h"
@@ -130,11 +131,6 @@ void tilcdc_remove_external_encoders(struct drm_device *dev)
 						 priv->connector_funcs[i]);
 }
 
-static int dev_match_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
 int tilcdc_get_external_components(struct device *dev,
 				   struct component_match **match)
 {
@@ -152,7 +148,7 @@ int tilcdc_get_external_components(struct device *dev,
 
 		dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
 		if (match)
-			component_match_add(dev, match, dev_match_of, node);
+			component_match_add_of(dev, match, node);
 		of_node_put(node);
 		count++;
 	}
-- 
2.1.0



From p.zabel at pengutronix.de  Fri Jun  3 07:52:20 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Fri, 03 Jun 2016 16:52:20 +0200
Subject: [PATCH v14 8/8] arm64: dts: mt8173: Add display subsystem
 related nodes
In-Reply-To: <1460387376-11799-9-git-send-email-p.zabel@pengutronix.de>
References: <1460387376-11799-1-git-send-email-p.zabel@pengutronix.de>
 <1460387376-11799-9-git-send-email-p.zabel@pengutronix.de>
Message-ID: <1464965540.3543.39.camel@pengutronix.de>

Hi Matthias,

Am Montag, den 11.04.2016, 17:09 +0200 schrieb Philipp Zabel:
> From: CK Hu <ck.hu at mediatek.com>
> 
> This patch adds the device nodes for the DISP function blocks
> comprising the display subsystem.
> 
> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> Signed-off-by: Cawa Cheng <cawa.cheng at mediatek.com>
> Signed-off-by: Jie Qiu <jie.qiu at mediatek.com>
> Signed-off-by: Daniel Kurtz <djkurtz at chromium.org>
> Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
> ---
> Changes since v13:
>  - Remove HDMI DDC I2C node, this should be added by the HDMI patch

Since the corresponding drivers are now merged into v4.7-rc1, could you
take this patch [1] into your tree?

[1] https://patchwork.kernel.org/patch/8803401/

regards
Philipp

> ---
>  arch/arm64/boot/dts/mediatek/mt8173.dtsi | 223 +++++++++++++++++++++++++++++++
>  1 file changed, 223 insertions(+)
> 
> diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> index eab7efc..2734694 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> @@ -26,6 +26,23 @@
>  	#address-cells = <2>;
>  	#size-cells = <2>;
>  
> +	aliases {
> +		ovl0 = &ovl0;
> +		ovl1 = &ovl1;
> +		rdma0 = &rdma0;
> +		rdma1 = &rdma1;
> +		rdma2 = &rdma2;
> +		wdma0 = &wdma0;
> +		wdma1 = &wdma1;
> +		color0 = &color0;
> +		color1 = &color1;
> +		split0 = &split0;
> +		split1 = &split1;
> +		dpi0 = &dpi0;
> +		dsi0 = &dsi0;
> +		dsi1 = &dsi1;
> +	};
> +
>  	cpus {
>  		#address-cells = <1>;
>  		#size-cells = <0>;
> @@ -300,6 +317,26 @@
>  			#clock-cells = <1>;
>  		};
>  
> +		mipi_tx0: mipi-dphy at 10215000 {
> +			compatible = "mediatek,mt8173-mipi-tx";
> +			reg = <0 0x10215000 0 0x1000>;
> +			clocks = <&clk26m>;
> +			clock-output-names = "mipi_tx0_pll";
> +			#clock-cells = <0>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +
> +		mipi_tx1: mipi-dphy at 10216000 {
> +			compatible = "mediatek,mt8173-mipi-tx";
> +			reg = <0 0x10216000 0 0x1000>;
> +			clocks = <&clk26m>;
> +			clock-output-names = "mipi_tx1_pll";
> +			#clock-cells = <0>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +
>  		gic: interrupt-controller at 10220000 {
>  			compatible = "arm,gic-400";
>  			#interrupt-cells = <3>;
> @@ -592,9 +629,181 @@
>  		mmsys: clock-controller at 14000000 {
>  			compatible = "mediatek,mt8173-mmsys", "syscon";
>  			reg = <0 0x14000000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
>  			#clock-cells = <1>;
>  		};
>  
> +		ovl0: ovl at 1400c000 {
> +			compatible = "mediatek,mt8173-disp-ovl";
> +			reg = <0 0x1400c000 0 0x1000>;
> +			interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_OVL0>;
> +			iommus = <&iommu M4U_PORT_DISP_OVL0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		ovl1: ovl at 1400d000 {
> +			compatible = "mediatek,mt8173-disp-ovl";
> +			reg = <0 0x1400d000 0 0x1000>;
> +			interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_OVL1>;
> +			iommus = <&iommu M4U_PORT_DISP_OVL1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		rdma0: rdma at 1400e000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x1400e000 0 0x1000>;
> +			interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA0>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		rdma1: rdma at 1400f000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x1400f000 0 0x1000>;
> +			interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA1>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		rdma2: rdma at 14010000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x14010000 0 0x1000>;
> +			interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA2>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA2>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		wdma0: wdma at 14011000 {
> +			compatible = "mediatek,mt8173-disp-wdma";
> +			reg = <0 0x14011000 0 0x1000>;
> +			interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_WDMA0>;
> +			iommus = <&iommu M4U_PORT_DISP_WDMA0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		wdma1: wdma at 14012000 {
> +			compatible = "mediatek,mt8173-disp-wdma";
> +			reg = <0 0x14012000 0 0x1000>;
> +			interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_WDMA1>;
> +			iommus = <&iommu M4U_PORT_DISP_WDMA1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		color0: color at 14013000 {
> +			compatible = "mediatek,mt8173-disp-color";
> +			reg = <0 0x14013000 0 0x1000>;
> +			interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_COLOR0>;
> +		};
> +
> +		color1: color at 14014000 {
> +			compatible = "mediatek,mt8173-disp-color";
> +			reg = <0 0x14014000 0 0x1000>;
> +			interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_COLOR1>;
> +		};
> +
> +		aal at 14015000 {
> +			compatible = "mediatek,mt8173-disp-aal";
> +			reg = <0 0x14015000 0 0x1000>;
> +			interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_AAL>;
> +		};
> +
> +		gamma at 14016000 {
> +			compatible = "mediatek,mt8173-disp-gamma";
> +			reg = <0 0x14016000 0 0x1000>;
> +			interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_GAMMA>;
> +		};
> +
> +		merge at 14017000 {
> +			compatible = "mediatek,mt8173-disp-merge";
> +			reg = <0 0x14017000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_MERGE>;
> +		};
> +
> +		split0: split at 14018000 {
> +			compatible = "mediatek,mt8173-disp-split";
> +			reg = <0 0x14018000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_SPLIT0>;
> +		};
> +
> +		split1: split at 14019000 {
> +			compatible = "mediatek,mt8173-disp-split";
> +			reg = <0 0x14019000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_SPLIT1>;
> +		};
> +
> +		ufoe at 1401a000 {
> +			compatible = "mediatek,mt8173-disp-ufoe";
> +			reg = <0 0x1401a000 0 0x1000>;
> +			interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_UFOE>;
> +		};
> +
> +		dsi0: dsi at 1401b000 {
> +			compatible = "mediatek,mt8173-dsi";
> +			reg = <0 0x1401b000 0 0x1000>;
> +			interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DSI0_ENGINE>,
> +				 <&mmsys CLK_MM_DSI0_DIGITAL>,
> +				 <&mipi_tx0>;
> +			clock-names = "engine", "digital", "hs";
> +			phys = <&mipi_tx0>;
> +			phy-names = "dphy";
> +			status = "disabled";
> +		};
> +
> +		dsi1: dsi at 1401c000 {
> +			compatible = "mediatek,mt8173-dsi";
> +			reg = <0 0x1401c000 0 0x1000>;
> +			interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DSI1_ENGINE>,
> +				 <&mmsys CLK_MM_DSI1_DIGITAL>,
> +				 <&mipi_tx1>;
> +			clock-names = "engine", "digital", "hs";
> +			phy = <&mipi_tx1>;
> +			phy-names = "dphy";
> +			status = "disabled";
> +		};
> +
> +		dpi0: dpi at 1401d000 {
> +			compatible = "mediatek,mt8173-dpi";
> +			reg = <0 0x1401d000 0 0x1000>;
> +			interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DPI_PIXEL>,
> +				 <&mmsys CLK_MM_DPI_ENGINE>,
> +				 <&apmixedsys CLK_APMIXED_TVDPLL>;
> +			clock-names = "pixel", "engine", "pll";
> +			status = "disabled";
> +		};
> +
>  		pwm0: pwm at 1401e000 {
>  			compatible = "mediatek,mt8173-disp-pwm",
>  				     "mediatek,mt6595-disp-pwm";
> @@ -617,6 +826,14 @@
>  			status = "disabled";
>  		};
>  
> +		mutex: mutex at 14020000 {
> +			compatible = "mediatek,mt8173-disp-mutex";
> +			reg = <0 0x14020000 0 0x1000>;
> +			interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_MUTEX_32K>;
> +		};
> +
>  		larb0: larb at 14021000 {
>  			compatible = "mediatek,mt8173-smi-larb";
>  			reg = <0 0x14021000 0 0x1000>;
> @@ -636,6 +853,12 @@
>  			clock-names = "apb", "smi";
>  		};
>  
> +		od at 14023000 {
> +			compatible = "mediatek,mt8173-disp-od";
> +			reg = <0 0x14023000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_DISP_OD>;
> +		};
> +
>  		larb4: larb at 14027000 {
>  			compatible = "mediatek,mt8173-smi-larb";
>  			reg = <0 0x14027000 0 0x1000>;




From p.zabel at pengutronix.de  Fri Jun  3 07:59:29 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Fri,  3 Jun 2016 16:59:29 +0200
Subject: [PATCH v15] arm64: dts: mt8173: Add display subsystem related nodes
Message-ID: <1464965969-5243-1-git-send-email-p.zabel@pengutronix.de>

From: CK Hu <ck.hu at mediatek.com>

This patch adds the device nodes for the DISP function blocks
comprising the display subsystem.

Signed-off-by: CK Hu <ck.hu at mediatek.com>
Signed-off-by: Cawa Cheng <cawa.cheng at mediatek.com>
Signed-off-by: Jie Qiu <jie.qiu at mediatek.com>
Signed-off-by: Daniel Kurtz <djkurtz at chromium.org>
Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
---
Changes since v14:
 - Rebased onto v4.7-rc1
---
 arch/arm64/boot/dts/mediatek/mt8173.dtsi | 223 +++++++++++++++++++++++++++++++
 1 file changed, 223 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 05f89c4..78529e4 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -26,6 +26,23 @@
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	aliases {
+		ovl0 = &ovl0;
+		ovl1 = &ovl1;
+		rdma0 = &rdma0;
+		rdma1 = &rdma1;
+		rdma2 = &rdma2;
+		wdma0 = &wdma0;
+		wdma1 = &wdma1;
+		color0 = &color0;
+		color1 = &color1;
+		split0 = &split0;
+		split1 = &split1;
+		dpi0 = &dpi0;
+		dsi0 = &dsi0;
+		dsi1 = &dsi1;
+	};
+
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -343,6 +360,26 @@
 			#clock-cells = <1>;
 		};
 
+		mipi_tx0: mipi-dphy at 10215000 {
+			compatible = "mediatek,mt8173-mipi-tx";
+			reg = <0 0x10215000 0 0x1000>;
+			clocks = <&clk26m>;
+			clock-output-names = "mipi_tx0_pll";
+			#clock-cells = <0>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+
+		mipi_tx1: mipi-dphy at 10216000 {
+			compatible = "mediatek,mt8173-mipi-tx";
+			reg = <0 0x10216000 0 0x1000>;
+			clocks = <&clk26m>;
+			clock-output-names = "mipi_tx1_pll";
+			#clock-cells = <0>;
+			#phy-cells = <0>;
+			status = "disabled";
+		};
+
 		gic: interrupt-controller at 10220000 {
 			compatible = "arm,gic-400";
 			#interrupt-cells = <3>;
@@ -652,9 +689,181 @@
 		mmsys: clock-controller at 14000000 {
 			compatible = "mediatek,mt8173-mmsys", "syscon";
 			reg = <0 0x14000000 0 0x1000>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			#clock-cells = <1>;
 		};
 
+		ovl0: ovl at 1400c000 {
+			compatible = "mediatek,mt8173-disp-ovl";
+			reg = <0 0x1400c000 0 0x1000>;
+			interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_OVL0>;
+			iommus = <&iommu M4U_PORT_DISP_OVL0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		ovl1: ovl at 1400d000 {
+			compatible = "mediatek,mt8173-disp-ovl";
+			reg = <0 0x1400d000 0 0x1000>;
+			interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_OVL1>;
+			iommus = <&iommu M4U_PORT_DISP_OVL1>;
+			mediatek,larb = <&larb4>;
+		};
+
+		rdma0: rdma at 1400e000 {
+			compatible = "mediatek,mt8173-disp-rdma";
+			reg = <0 0x1400e000 0 0x1000>;
+			interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_RDMA0>;
+			iommus = <&iommu M4U_PORT_DISP_RDMA0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		rdma1: rdma at 1400f000 {
+			compatible = "mediatek,mt8173-disp-rdma";
+			reg = <0 0x1400f000 0 0x1000>;
+			interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_RDMA1>;
+			iommus = <&iommu M4U_PORT_DISP_RDMA1>;
+			mediatek,larb = <&larb4>;
+		};
+
+		rdma2: rdma at 14010000 {
+			compatible = "mediatek,mt8173-disp-rdma";
+			reg = <0 0x14010000 0 0x1000>;
+			interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_RDMA2>;
+			iommus = <&iommu M4U_PORT_DISP_RDMA2>;
+			mediatek,larb = <&larb4>;
+		};
+
+		wdma0: wdma at 14011000 {
+			compatible = "mediatek,mt8173-disp-wdma";
+			reg = <0 0x14011000 0 0x1000>;
+			interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_WDMA0>;
+			iommus = <&iommu M4U_PORT_DISP_WDMA0>;
+			mediatek,larb = <&larb0>;
+		};
+
+		wdma1: wdma at 14012000 {
+			compatible = "mediatek,mt8173-disp-wdma";
+			reg = <0 0x14012000 0 0x1000>;
+			interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_WDMA1>;
+			iommus = <&iommu M4U_PORT_DISP_WDMA1>;
+			mediatek,larb = <&larb4>;
+		};
+
+		color0: color at 14013000 {
+			compatible = "mediatek,mt8173-disp-color";
+			reg = <0 0x14013000 0 0x1000>;
+			interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_COLOR0>;
+		};
+
+		color1: color at 14014000 {
+			compatible = "mediatek,mt8173-disp-color";
+			reg = <0 0x14014000 0 0x1000>;
+			interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_COLOR1>;
+		};
+
+		aal at 14015000 {
+			compatible = "mediatek,mt8173-disp-aal";
+			reg = <0 0x14015000 0 0x1000>;
+			interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_AAL>;
+		};
+
+		gamma at 14016000 {
+			compatible = "mediatek,mt8173-disp-gamma";
+			reg = <0 0x14016000 0 0x1000>;
+			interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_GAMMA>;
+		};
+
+		merge at 14017000 {
+			compatible = "mediatek,mt8173-disp-merge";
+			reg = <0 0x14017000 0 0x1000>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_MERGE>;
+		};
+
+		split0: split at 14018000 {
+			compatible = "mediatek,mt8173-disp-split";
+			reg = <0 0x14018000 0 0x1000>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_SPLIT0>;
+		};
+
+		split1: split at 14019000 {
+			compatible = "mediatek,mt8173-disp-split";
+			reg = <0 0x14019000 0 0x1000>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_SPLIT1>;
+		};
+
+		ufoe at 1401a000 {
+			compatible = "mediatek,mt8173-disp-ufoe";
+			reg = <0 0x1401a000 0 0x1000>;
+			interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DISP_UFOE>;
+		};
+
+		dsi0: dsi at 1401b000 {
+			compatible = "mediatek,mt8173-dsi";
+			reg = <0 0x1401b000 0 0x1000>;
+			interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DSI0_ENGINE>,
+				 <&mmsys CLK_MM_DSI0_DIGITAL>,
+				 <&mipi_tx0>;
+			clock-names = "engine", "digital", "hs";
+			phys = <&mipi_tx0>;
+			phy-names = "dphy";
+			status = "disabled";
+		};
+
+		dsi1: dsi at 1401c000 {
+			compatible = "mediatek,mt8173-dsi";
+			reg = <0 0x1401c000 0 0x1000>;
+			interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DSI1_ENGINE>,
+				 <&mmsys CLK_MM_DSI1_DIGITAL>,
+				 <&mipi_tx1>;
+			clock-names = "engine", "digital", "hs";
+			phy = <&mipi_tx1>;
+			phy-names = "dphy";
+			status = "disabled";
+		};
+
+		dpi0: dpi at 1401d000 {
+			compatible = "mediatek,mt8173-dpi";
+			reg = <0 0x1401d000 0 0x1000>;
+			interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_DPI_PIXEL>,
+				 <&mmsys CLK_MM_DPI_ENGINE>,
+				 <&apmixedsys CLK_APMIXED_TVDPLL>;
+			clock-names = "pixel", "engine", "pll";
+			status = "disabled";
+		};
+
 		pwm0: pwm at 1401e000 {
 			compatible = "mediatek,mt8173-disp-pwm",
 				     "mediatek,mt6595-disp-pwm";
@@ -677,6 +886,14 @@
 			status = "disabled";
 		};
 
+		mutex: mutex at 14020000 {
+			compatible = "mediatek,mt8173-disp-mutex";
+			reg = <0 0x14020000 0 0x1000>;
+			interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_LOW>;
+			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+			clocks = <&mmsys CLK_MM_MUTEX_32K>;
+		};
+
 		larb0: larb at 14021000 {
 			compatible = "mediatek,mt8173-smi-larb";
 			reg = <0 0x14021000 0 0x1000>;
@@ -696,6 +913,12 @@
 			clock-names = "apb", "smi";
 		};
 
+		od at 14023000 {
+			compatible = "mediatek,mt8173-disp-od";
+			reg = <0 0x14023000 0 0x1000>;
+			clocks = <&mmsys CLK_MM_DISP_OD>;
+		};
+
 		larb4: larb at 14027000 {
 			compatible = "mediatek,mt8173-smi-larb";
 			reg = <0 0x14027000 0 0x1000>;
-- 
2.8.1



From matthias.bgg at gmail.com  Fri Jun  3 08:03:53 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 17:03:53 +0200
Subject: [PATCH 1/3] dt-bindings: pwm: Add MediaTek display PWM bindings
In-Reply-To: <1464936349.5209.9.camel@mhfsdcap03>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-2-git-send-email-weiqing.kong@mediatek.com>
 <20160602224550.GA19256@rob-hp-laptop> <1464936349.5209.9.camel@mhfsdcap03>
Message-ID: <57519C59.5040107@gmail.com>



On 03/06/16 08:45, weiqing kong wrote:
> On Thu, 2016-06-02 at 17:45 -0500, Rob Herring wrote:
>> On Mon, May 30, 2016 at 04:41:50PM +0800, Weiqing Kong wrote:
>>> Add MT2701 compatible string.
>>>
>>> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
>>> ---
>>>   Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt | 5 +++--
>>>   1 file changed, 3 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
>>> index f8f59ba..f2f2fa9 100644
>>> --- a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
>>> +++ b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
>>> @@ -1,9 +1,10 @@
>>>   MediaTek display PWM controller
>>>
>>>   Required properties:
>>> - - compatible: should be "mediatek,<name>-disp-pwm":
>>> -   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
>>> + - compatible: should be like these:
>>> +   - "mediatek,mt2701-disp-bls": found on mt2701 SoC.
>>
>> Why bls instead of pwm?
>
> In mt6595/mt8173, we have a hardware module named pwm.
> In mt2701, we have bls hardware module which includes disp pwm function,
> so we named it mt2701-disp-bls rather than mt2701-disp-pwm in order to
> match with hardware spec.
> thanks.
>

 From what I understand pwm module and disp pwm in mt6589/mt8173 is 
independent from the disp pwm. Actually the disp pwm in mt6589 is part 
of the bls module, so this should me mediatek,mt2701-disp-pwm as well

Thanks,
Matthias

>>
>>>      - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
>>> +   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
>>>    - reg: physical base address and length of the controller's registers.
>>>    - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
>>>      the cell format.
>>> --
>>> 1.8.1.1.dirty
>>>
>
>


From l.stach at pengutronix.de  Fri Jun  3 08:10:32 2016
From: l.stach at pengutronix.de (Lucas Stach)
Date: Fri, 03 Jun 2016 17:10:32 +0200
Subject: [PATCH v2 2/3] drm: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
Message-ID: <1464966632.32235.4.camel@pengutronix.de>

Am Freitag, den 03.06.2016, 15:21 +0100 schrieb Russell King:
> Convert DT component matching to use component_match_add_release().
> 
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
>  drivers/gpu/drm/arm/hdlcd_drv.c             |  9 +++------
>  drivers/gpu/drm/armada/armada_drv.c         |  8 ++------
>  drivers/gpu/drm/drm_of.c                    |  6 ++++--
>  drivers/gpu/drm/etnaviv/etnaviv_drv.c       | 11 ++---------

For the etnaviv part:

Acked-by: Lucas Stach <l.stach at pengutronix.de>



From matthias.bgg at gmail.com  Fri Jun  3 08:20:31 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 17:20:31 +0200
Subject: [PATCH v2 3/3] iommu: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8pyc-0005OV-7X@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyc-0005OV-7X@rmk-PC.armlinux.org.uk>
Message-ID: <5751A03F.9050800@gmail.com>



On 03/06/16 16:21, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
>
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>


Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

> ---
>   drivers/iommu/mtk_iommu.c | 14 ++++++--------
>   1 file changed, 6 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
> index c3043d8754e3..71cf62af4e24 100644
> --- a/drivers/iommu/mtk_iommu.c
> +++ b/drivers/iommu/mtk_iommu.c
> @@ -24,6 +24,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/list.h>
>   #include <linux/of_address.h>
> +#include <linux/of_component.h>
>   #include <linux/of_iommu.h>
>   #include <linux/of_irq.h>
>   #include <linux/of_platform.h>
> @@ -552,11 +553,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
>   	return 0;
>   }
>
> -static int compare_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>   static int mtk_iommu_bind(struct device *dev)
>   {
>   	struct mtk_iommu_data *data = dev_get_drvdata(dev);
> @@ -630,17 +626,19 @@ static int mtk_iommu_probe(struct platform_device *pdev)
>   			continue;
>
>   		plarbdev = of_find_device_by_node(larbnode);
> -		of_node_put(larbnode);
>   		if (!plarbdev) {
>   			plarbdev = of_platform_device_create(
>   						larbnode, NULL,
>   						platform_bus_type.dev_root);
> -			if (!plarbdev)
> +			if (!plarbdev) {
> +				of_node_put(larbnode);
>   				return -EPROBE_DEFER;
> +			}
>   		}
>   		data->smi_imu.larb_imu[i].dev = &plarbdev->dev;
>
> -		component_match_add(dev, &match, compare_of, larbnode);
> +		component_match_add_of(dev, &match, larbnode);
> +		of_node_put(larbnode);
>   	}
>
>   	platform_set_drvdata(pdev, data);
>


From robh+dt at kernel.org  Fri Jun  3 08:29:40 2016
From: robh+dt at kernel.org (Rob Herring)
Date: Fri, 3 Jun 2016 10:29:40 -0500
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
Message-ID: <CAL_JsqKaRGk6RMdDWUFYH8_J1gp3pcNeuvx-r=JSg7nc6jh_eg@mail.gmail.com>

On Fri, Jun 3, 2016 at 9:21 AM, Russell King <rmk+kernel at armlinux.org.uk> wrote:
> Add common OF-based component functionality for matching devices by
> device node, and releasing the device node at the appropraite time.
>
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
>  drivers/of/Makefile          |  2 +-
>  drivers/of/of_component.c    | 41 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/of_component.h | 14 ++++++++++++++

I'd prefer this to go into drivers/base/component.c. That's the
general direction we've been moving.

I'd expect this would cause some build failures unless the cases you
converted all depend on CONFIG_OF.

Rob


From matthias.bgg at gmail.com  Fri Jun  3 08:35:57 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 17:35:57 +0200
Subject: [PATCH 2/3] pwm: Add MediaTek MT2701 display PWM driver support
In-Reply-To: <1464597712-5399-3-git-send-email-weiqing.kong@mediatek.com>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-3-git-send-email-weiqing.kong@mediatek.com>
Message-ID: <5751A3DD.7020402@gmail.com>



On 30/05/16 10:41, Weiqing Kong wrote:
> Use the mtk_pwm_data struction to define different registers
> and add MT2701 specific register operations, such as MT2701
> doesn't have commit register, needs to disable double buffer
> before writing register, and needs to select manual mode
> and use PWM_PERIOD/PWM_HIGH_WIDTH.
>
> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> ---
>   drivers/pwm/pwm-mtk-disp.c | 89 +++++++++++++++++++++++++++++++++++++---------
>   1 file changed, 72 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
> index 0ad3385..03b9c9e 100644
> --- a/drivers/pwm/pwm-mtk-disp.c
> +++ b/drivers/pwm/pwm-mtk-disp.c
> @@ -18,33 +18,44 @@
>   #include <linux/io.h>
>   #include <linux/module.h>
>   #include <linux/of.h>
> +#include <linux/of_device.h>
>   #include <linux/platform_device.h>
>   #include <linux/pwm.h>
>   #include <linux/slab.h>
>
>   #define DISP_PWM_EN		0x00
> -#define PWM_ENABLE_MASK		BIT(0)
>
>   #define DISP_PWM_COMMIT		0x08
>   #define PWM_COMMIT_MASK		BIT(0)
>
> -#define DISP_PWM_CON_0		0x10
>   #define PWM_CLKDIV_SHIFT	16
>   #define PWM_CLKDIV_MAX		0x3ff
>   #define PWM_CLKDIV_MASK		(PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
>
> -#define DISP_PWM_CON_1		0x14
>   #define PWM_PERIOD_BIT_WIDTH	12
>   #define PWM_PERIOD_MASK		((1 << PWM_PERIOD_BIT_WIDTH) - 1)
>
>   #define PWM_HIGH_WIDTH_SHIFT	16
>   #define PWM_HIGH_WIDTH_MASK	(0x1fff << PWM_HIGH_WIDTH_SHIFT)
>
> +#define MT2701_PWM_MANUAL_SEL_MASK	BIT(1)
> +#define MT2701_PWM_BLS_DEBUG		0xb0

Do we need to set magic 0x3 in the debug register? If so, this should be 
part of mtk_pwm_data, just as con0_sel.

> +#define MT2701_PWM_BLS_DEBUG_MASK	0x3
> +
> +struct mtk_pwm_data {
> +	unsigned int enable_bit;
> +	unsigned int con0;
> +	unsigned int con0_sel;
> +	unsigned int con1;
> +	bool have_commit_reg;
> +};
> +
>   struct mtk_disp_pwm {
>   	struct pwm_chip chip;
>   	struct clk *clk_main;
>   	struct clk *clk_mm;
>   	void __iomem *base;
> +	const struct mtk_pwm_data *data;

Couldn't that just be the offset for the commit register and set it to 
0x0 if the commit register is not present. Right now, we suppose the 
DISP_PWM_EN is at offset 0x0, so this should not be a problem.

>   };
>
>   static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
> @@ -106,12 +117,18 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
>   		return err;
>   	}
>
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> +				 PWM_CLKDIV_MASK,
>   				 clk_div << PWM_CLKDIV_SHIFT);
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
> -				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
> +				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
> +				 value);
> +	if (mdp->data->have_commit_reg) {
> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> +					 PWM_COMMIT_MASK, 0x1);
> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> +					 PWM_COMMIT_MASK, 0x0);

should be renamed to MT8173_PWM_COMMIT_MASK, although I'm not really a 
friend of mixing up SoC specific defines with a Soc specific 
mtk_disp_pwm structure.

Regards,
Matthias

> +	}
>
>   	clk_disable(mdp->clk_mm);
>   	clk_disable(mdp->clk_main);
> @@ -134,7 +151,9 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
>   		return err;
>   	}
>
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> +				 mdp->data->enable_bit,
> +				 mdp->data->enable_bit);
>
>   	return 0;
>   }
> @@ -143,7 +162,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>   {
>   	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
>
> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> +				 mdp->data->enable_bit, 0x0);
>
>   	clk_disable(mdp->clk_mm);
>   	clk_disable(mdp->clk_main);
> @@ -156,12 +176,41 @@ static const struct pwm_ops mtk_disp_pwm_ops = {
>   	.owner = THIS_MODULE,
>   };
>
> +static const struct mtk_pwm_data mt8173_pwm_data = {
> +	.enable_bit = BIT(0),
> +	.con0 = 0x10,
> +	.con0_sel = 0x0,
> +	.con1 = 0x14,
> +	.have_commit_reg = true,
> +};
> +
> +static const struct mtk_pwm_data mt2701_pwm_data = {
> +	.enable_bit = BIT(16),
> +	.con0 = 0xa8,
> +	.con0_sel = 0x2,
> +	.con1 = 0xac,
> +	.have_commit_reg = false,
> +};
> +
> +static const struct of_device_id mtk_disp_pwm_of_match[] = {
> +	{ .compatible = "mediatek,mt2701-disp-bls", .data = &mt2701_pwm_data},
> +	{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
> +	{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> +
>   static int mtk_disp_pwm_probe(struct platform_device *pdev)
>   {
> +	const struct of_device_id *id;
>   	struct mtk_disp_pwm *mdp;
>   	struct resource *r;
>   	int ret;
>
> +	id = of_match_device(mtk_disp_pwm_of_match, &pdev->dev);
> +	if (!id)
> +		return -EINVAL;
> +
>   	mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL);
>   	if (!mdp)
>   		return -ENOMEM;
> @@ -191,6 +240,7 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
>   	mdp->chip.ops = &mtk_disp_pwm_ops;
>   	mdp->chip.base = -1;
>   	mdp->chip.npwm = 1;
> +	mdp->data = id->data;
>
>   	ret = pwmchip_add(&mdp->chip);
>   	if (ret < 0) {
> @@ -200,6 +250,18 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
>
>   	platform_set_drvdata(pdev, mdp);
>
> +	/*
> +	 * For MT2701, disable double buffer before writing register
> +	 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
> +	 */
> +	if (!mdp->data->have_commit_reg) {
> +		mtk_disp_pwm_update_bits(mdp, MT2701_PWM_BLS_DEBUG,
> +					 MT2701_PWM_BLS_DEBUG_MASK, 0x3);
> +		mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> +					 MT2701_PWM_MANUAL_SEL_MASK,
> +					 mdp->data->con0_sel);
> +	}
> +
>   	return 0;
>
>   disable_clk_mm:
> @@ -221,13 +283,6 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
>   	return ret;
>   }
>
> -static const struct of_device_id mtk_disp_pwm_of_match[] = {
> -	{ .compatible = "mediatek,mt8173-disp-pwm" },
> -	{ .compatible = "mediatek,mt6595-disp-pwm" },
> -	{ }
> -};
> -MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> -
>   static struct platform_driver mtk_disp_pwm_driver = {
>   	.driver = {
>   		.name = "mediatek-disp-pwm",
>


From linux at armlinux.org.uk  Fri Jun  3 08:36:30 2016
From: linux at armlinux.org.uk (Russell King - ARM Linux)
Date: Fri, 3 Jun 2016 16:36:30 +0100
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <CAL_JsqKaRGk6RMdDWUFYH8_J1gp3pcNeuvx-r=JSg7nc6jh_eg@mail.gmail.com>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
 <CAL_JsqKaRGk6RMdDWUFYH8_J1gp3pcNeuvx-r=JSg7nc6jh_eg@mail.gmail.com>
Message-ID: <20160603153630.GE1041@n2100.armlinux.org.uk>

On Fri, Jun 03, 2016 at 10:29:40AM -0500, Rob Herring wrote:
> On Fri, Jun 3, 2016 at 9:21 AM, Russell King <rmk+kernel at armlinux.org.uk> wrote:
> > Add common OF-based component functionality for matching devices by
> > device node, and releasing the device node at the appropraite time.
> >
> > Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> > ---
> >  drivers/of/Makefile          |  2 +-
> >  drivers/of/of_component.c    | 41 +++++++++++++++++++++++++++++++++++++++++
> >  include/linux/of_component.h | 14 ++++++++++++++
> 
> I'd prefer this to go into drivers/base/component.c. That's the
> general direction we've been moving.

I'd prefer not to, I don't want to turn the component helpers into
something OF specific.  They aren't OF specific.

> I'd expect this would cause some build failures unless the cases you
> converted all depend on CONFIG_OF.

Okay, I'll stick with v1 then, and the duplication that v1 involves.

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


From matthias.bgg at gmail.com  Fri Jun  3 08:42:34 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 3 Jun 2016 17:42:34 +0200
Subject: [PATCH v15] arm64: dts: mt8173: Add display subsystem related
 nodes
In-Reply-To: <1464965969-5243-1-git-send-email-p.zabel@pengutronix.de>
References: <1464965969-5243-1-git-send-email-p.zabel@pengutronix.de>
Message-ID: <5751A56A.6080004@gmail.com>



On 03/06/16 16:59, Philipp Zabel wrote:
> From: CK Hu <ck.hu at mediatek.com>
>
> This patch adds the device nodes for the DISP function blocks
> comprising the display subsystem.
>
> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> Signed-off-by: Cawa Cheng <cawa.cheng at mediatek.com>
> Signed-off-by: Jie Qiu <jie.qiu at mediatek.com>
> Signed-off-by: Daniel Kurtz <djkurtz at chromium.org>
> Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
> ---

applied, thanks.

> Changes since v14:
>   - Rebased onto v4.7-rc1
> ---
>   arch/arm64/boot/dts/mediatek/mt8173.dtsi | 223 +++++++++++++++++++++++++++++++
>   1 file changed, 223 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> index 05f89c4..78529e4 100644
> --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
> @@ -26,6 +26,23 @@
>   	#address-cells = <2>;
>   	#size-cells = <2>;
>
> +	aliases {
> +		ovl0 = &ovl0;
> +		ovl1 = &ovl1;
> +		rdma0 = &rdma0;
> +		rdma1 = &rdma1;
> +		rdma2 = &rdma2;
> +		wdma0 = &wdma0;
> +		wdma1 = &wdma1;
> +		color0 = &color0;
> +		color1 = &color1;
> +		split0 = &split0;
> +		split1 = &split1;
> +		dpi0 = &dpi0;
> +		dsi0 = &dsi0;
> +		dsi1 = &dsi1;
> +	};
> +
>   	cpus {
>   		#address-cells = <1>;
>   		#size-cells = <0>;
> @@ -343,6 +360,26 @@
>   			#clock-cells = <1>;
>   		};
>
> +		mipi_tx0: mipi-dphy at 10215000 {
> +			compatible = "mediatek,mt8173-mipi-tx";
> +			reg = <0 0x10215000 0 0x1000>;
> +			clocks = <&clk26m>;
> +			clock-output-names = "mipi_tx0_pll";
> +			#clock-cells = <0>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +
> +		mipi_tx1: mipi-dphy at 10216000 {
> +			compatible = "mediatek,mt8173-mipi-tx";
> +			reg = <0 0x10216000 0 0x1000>;
> +			clocks = <&clk26m>;
> +			clock-output-names = "mipi_tx1_pll";
> +			#clock-cells = <0>;
> +			#phy-cells = <0>;
> +			status = "disabled";
> +		};
> +
>   		gic: interrupt-controller at 10220000 {
>   			compatible = "arm,gic-400";
>   			#interrupt-cells = <3>;
> @@ -652,9 +689,181 @@
>   		mmsys: clock-controller at 14000000 {
>   			compatible = "mediatek,mt8173-mmsys", "syscon";
>   			reg = <0 0x14000000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
>   			#clock-cells = <1>;
>   		};
>
> +		ovl0: ovl at 1400c000 {
> +			compatible = "mediatek,mt8173-disp-ovl";
> +			reg = <0 0x1400c000 0 0x1000>;
> +			interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_OVL0>;
> +			iommus = <&iommu M4U_PORT_DISP_OVL0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		ovl1: ovl at 1400d000 {
> +			compatible = "mediatek,mt8173-disp-ovl";
> +			reg = <0 0x1400d000 0 0x1000>;
> +			interrupts = <GIC_SPI 181 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_OVL1>;
> +			iommus = <&iommu M4U_PORT_DISP_OVL1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		rdma0: rdma at 1400e000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x1400e000 0 0x1000>;
> +			interrupts = <GIC_SPI 182 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA0>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		rdma1: rdma at 1400f000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x1400f000 0 0x1000>;
> +			interrupts = <GIC_SPI 183 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA1>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		rdma2: rdma at 14010000 {
> +			compatible = "mediatek,mt8173-disp-rdma";
> +			reg = <0 0x14010000 0 0x1000>;
> +			interrupts = <GIC_SPI 184 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_RDMA2>;
> +			iommus = <&iommu M4U_PORT_DISP_RDMA2>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		wdma0: wdma at 14011000 {
> +			compatible = "mediatek,mt8173-disp-wdma";
> +			reg = <0 0x14011000 0 0x1000>;
> +			interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_WDMA0>;
> +			iommus = <&iommu M4U_PORT_DISP_WDMA0>;
> +			mediatek,larb = <&larb0>;
> +		};
> +
> +		wdma1: wdma at 14012000 {
> +			compatible = "mediatek,mt8173-disp-wdma";
> +			reg = <0 0x14012000 0 0x1000>;
> +			interrupts = <GIC_SPI 186 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_WDMA1>;
> +			iommus = <&iommu M4U_PORT_DISP_WDMA1>;
> +			mediatek,larb = <&larb4>;
> +		};
> +
> +		color0: color at 14013000 {
> +			compatible = "mediatek,mt8173-disp-color";
> +			reg = <0 0x14013000 0 0x1000>;
> +			interrupts = <GIC_SPI 187 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_COLOR0>;
> +		};
> +
> +		color1: color at 14014000 {
> +			compatible = "mediatek,mt8173-disp-color";
> +			reg = <0 0x14014000 0 0x1000>;
> +			interrupts = <GIC_SPI 188 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_COLOR1>;
> +		};
> +
> +		aal at 14015000 {
> +			compatible = "mediatek,mt8173-disp-aal";
> +			reg = <0 0x14015000 0 0x1000>;
> +			interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_AAL>;
> +		};
> +
> +		gamma at 14016000 {
> +			compatible = "mediatek,mt8173-disp-gamma";
> +			reg = <0 0x14016000 0 0x1000>;
> +			interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_GAMMA>;
> +		};
> +
> +		merge at 14017000 {
> +			compatible = "mediatek,mt8173-disp-merge";
> +			reg = <0 0x14017000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_MERGE>;
> +		};
> +
> +		split0: split at 14018000 {
> +			compatible = "mediatek,mt8173-disp-split";
> +			reg = <0 0x14018000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_SPLIT0>;
> +		};
> +
> +		split1: split at 14019000 {
> +			compatible = "mediatek,mt8173-disp-split";
> +			reg = <0 0x14019000 0 0x1000>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_SPLIT1>;
> +		};
> +
> +		ufoe at 1401a000 {
> +			compatible = "mediatek,mt8173-disp-ufoe";
> +			reg = <0 0x1401a000 0 0x1000>;
> +			interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DISP_UFOE>;
> +		};
> +
> +		dsi0: dsi at 1401b000 {
> +			compatible = "mediatek,mt8173-dsi";
> +			reg = <0 0x1401b000 0 0x1000>;
> +			interrupts = <GIC_SPI 192 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DSI0_ENGINE>,
> +				 <&mmsys CLK_MM_DSI0_DIGITAL>,
> +				 <&mipi_tx0>;
> +			clock-names = "engine", "digital", "hs";
> +			phys = <&mipi_tx0>;
> +			phy-names = "dphy";
> +			status = "disabled";
> +		};
> +
> +		dsi1: dsi at 1401c000 {
> +			compatible = "mediatek,mt8173-dsi";
> +			reg = <0 0x1401c000 0 0x1000>;
> +			interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DSI1_ENGINE>,
> +				 <&mmsys CLK_MM_DSI1_DIGITAL>,
> +				 <&mipi_tx1>;
> +			clock-names = "engine", "digital", "hs";
> +			phy = <&mipi_tx1>;
> +			phy-names = "dphy";
> +			status = "disabled";
> +		};
> +
> +		dpi0: dpi at 1401d000 {
> +			compatible = "mediatek,mt8173-dpi";
> +			reg = <0 0x1401d000 0 0x1000>;
> +			interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_DPI_PIXEL>,
> +				 <&mmsys CLK_MM_DPI_ENGINE>,
> +				 <&apmixedsys CLK_APMIXED_TVDPLL>;
> +			clock-names = "pixel", "engine", "pll";
> +			status = "disabled";
> +		};
> +
>   		pwm0: pwm at 1401e000 {
>   			compatible = "mediatek,mt8173-disp-pwm",
>   				     "mediatek,mt6595-disp-pwm";
> @@ -677,6 +886,14 @@
>   			status = "disabled";
>   		};
>
> +		mutex: mutex at 14020000 {
> +			compatible = "mediatek,mt8173-disp-mutex";
> +			reg = <0 0x14020000 0 0x1000>;
> +			interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_LOW>;
> +			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
> +			clocks = <&mmsys CLK_MM_MUTEX_32K>;
> +		};
> +
>   		larb0: larb at 14021000 {
>   			compatible = "mediatek,mt8173-smi-larb";
>   			reg = <0 0x14021000 0 0x1000>;
> @@ -696,6 +913,12 @@
>   			clock-names = "apb", "smi";
>   		};
>
> +		od at 14023000 {
> +			compatible = "mediatek,mt8173-disp-od";
> +			reg = <0 0x14023000 0 0x1000>;
> +			clocks = <&mmsys CLK_MM_DISP_OD>;
> +		};
> +
>   		larb4: larb at 14027000 {
>   			compatible = "mediatek,mt8173-smi-larb";
>   			reg = <0 0x14027000 0 0x1000>;
>


From thierry.reding at gmail.com  Fri Jun  3 08:44:30 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Fri, 3 Jun 2016 17:44:30 +0200
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
Message-ID: <20160603154430.GA21013@ulmo.ba.sec>

On Fri, Jun 03, 2016 at 03:21:19PM +0100, Russell King wrote:
[...]
> diff --git a/drivers/of/of_component.c b/drivers/of/of_component.c
[...]
> +static void component_compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}

The return statement here doesn't match the return value. Didn't GCC
complain about this?

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-mediatek/attachments/20160603/196b25a6/attachment.sig>

From linux at armlinux.org.uk  Fri Jun  3 09:11:29 2016
From: linux at armlinux.org.uk (Russell King - ARM Linux)
Date: Fri, 3 Jun 2016 17:11:29 +0100
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <20160603154430.GA21013@ulmo.ba.sec>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
 <20160603154430.GA21013@ulmo.ba.sec>
Message-ID: <20160603161129.GF1041@n2100.armlinux.org.uk>

On Fri, Jun 03, 2016 at 05:44:30PM +0200, Thierry Reding wrote:
> On Fri, Jun 03, 2016 at 03:21:19PM +0100, Russell King wrote:
> [...]
> > diff --git a/drivers/of/of_component.c b/drivers/of/of_component.c
> [...]
> > +static void component_compare_of(struct device *dev, void *data)
> > +{
> > +	return dev->of_node == data;
> > +}
> 
> The return statement here doesn't match the return value. Didn't GCC
> complain about this?

I didn't build-test it, because I wanted people's opinions on it first
(building means rebuilding my entire tree...)

Anyway, the patch series is dead because I'm not prepared to make the
changes which Rob mentioned, so we're back to v1 instead.

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


From daniel at ffwll.ch  Fri Jun  3 12:01:51 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Fri, 3 Jun 2016 21:01:51 +0200
Subject: [PATCH 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <20160603093743.000ef95b@bbrezillon>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-2-git-send-email-boris.brezillon@free-electrons.com>
 <8671722.LHaiaBNqfE@avalon>
 <CAKMK7uHyqNbA8Scw2fE0geAyNt0a0XVtzf81nwWjEhbZ-gAvKw@mail.gmail.com>
 <20160603093743.000ef95b@bbrezillon>
Message-ID: <20160603190151.GD7231@phenom.ffwll.local>

On Fri, Jun 03, 2016 at 09:37:43AM +0200, Boris Brezillon wrote:
> On Thu, 2 Jun 2016 23:57:02 +0200
> Daniel Vetter <daniel at ffwll.ch> wrote:
> 
> > On Thu, Jun 2, 2016 at 11:05 PM, Laurent Pinchart
> > <laurent.pinchart at ideasonboard.com> wrote:
> > > Hi Boris,
> > >
> > > Thank you for the patch.
> > >
> > > On Thursday 02 Jun 2016 16:31:28 Boris Brezillon wrote:  
> > >> Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
> > >> drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
> > >> that DRM drivers can leave this hook unassigned if they know they want
> > >> to use drm_atomic_helper_best_encoder().  
> > >
> > > Could you please update include/drm/drm_modeset_helper_vtables.h to document
> > > this new behaviour ?  
> > 
> > Thanks for reminding me. Please update hooks for both
> > atomic_best_encoder and best_encoder. Also mention that it's only
> > optional for atomic drivers. There's lots of examples in that file for
> > the wording usually used.
> 
> Hm, I haven't changed anything for the ->atomic_best_encoder() hook, or
> am I missing something?

Before you needed atomic_best_encoder or best_encoder (well that's the
idea at least), now both are optional. Kerneldoc needs to be adjusted in
both places to match new reality after your patch.
-Daniel

> 
> > 
> > > The only drawback I see in this patch is the additional object lookup
> > > performed by drm_atomic_helper_best_encoder() at runtime. I wonder if we could
> > > somehow cache the information, as the assignment can't change when there's a
> > > 1:1 correspondence between encoders and connectors.  
> > 
> > drm_encoder_find is an idr lookup. That should be plenty fast,
> > especially for modeset code. Usually what's too expensive even for
> > modeset code is linear list walks. But Chris just submitted patches to
> > convert most of them into simple lookups.
> > -Daniel
> > 
> > >>> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>  
> > >> ---
> > >>  drivers/gpu/drm/drm_atomic_helper.c |  4 +++-
> > >>  drivers/gpu/drm/drm_fb_helper.c     | 13 ++++++++++++-
> > >>  2 files changed, 15 insertions(+), 2 deletions(-)
> > >>
> > >> diff --git a/drivers/gpu/drm/drm_atomic_helper.c
> > >> b/drivers/gpu/drm/drm_atomic_helper.c index f6a3350..849d029 100644
> > >> --- a/drivers/gpu/drm/drm_atomic_helper.c
> > >> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> > >> @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state
> > >> *state, if (funcs->atomic_best_encoder)
> > >>               new_encoder = funcs->atomic_best_encoder(connector,
> > >>                                                        connector_state);
> > >> -     else
> > >> +     else if (funcs->best_encoder)
> > >>               new_encoder = funcs->best_encoder(connector);
> > >> +     else
> > >> +             new_encoder = drm_atomic_helper_best_encoder(connector);
> > >>
> > >>       if (!new_encoder) {
> > >>               DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
> > >> diff --git a/drivers/gpu/drm/drm_fb_helper.c
> > >> b/drivers/gpu/drm/drm_fb_helper.c index 7c2eb75..d44389a 100644
> > >> --- a/drivers/gpu/drm/drm_fb_helper.c
> > >> +++ b/drivers/gpu/drm/drm_fb_helper.c
> > >> @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper
> > >> *fb_helper, my_score++;
> > >>
> > >>       connector_funcs = connector->helper_private;
> > >> -     encoder = connector_funcs->best_encoder(connector);
> > >> +
> > >> +     /*
> > >> +      * If the DRM device implements atomic hooks and ->best_encoder() is
> > >> +      * NULL we fallback to the default drm_atomic_helper_best_encoder()
> > >> +      * helper.
> > >> +      */
> > >> +     if (fb_helper->dev->mode_config.funcs->atomic_commit &&
> > >> +         !connector_funcs->best_encoder)
> > >> +             encoder = drm_atomic_helper_best_encoder(connector);
> > >> +     else
> > >> +             encoder = connector_funcs->best_encoder(connector);
> > >> +
> > >>       if (!encoder)
> > >>               goto out;  
> > >
> > > --
> > > Regards,
> > >
> > > Laurent Pinchart
> > >  
> > 
> > 
> > 
> 
> 
> 
> -- 
> Boris Brezillon, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From robh+dt at kernel.org  Fri Jun  3 12:52:28 2016
From: robh+dt at kernel.org (Rob Herring)
Date: Fri, 3 Jun 2016 14:52:28 -0500
Subject: [PATCH v2 1/3] of: add common OF-based component functionality
In-Reply-To: <20160603153630.GE1041@n2100.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyR-0005OJ-V3@rmk-PC.armlinux.org.uk>
 <CAL_JsqKaRGk6RMdDWUFYH8_J1gp3pcNeuvx-r=JSg7nc6jh_eg@mail.gmail.com>
 <20160603153630.GE1041@n2100.armlinux.org.uk>
Message-ID: <CAL_Jsq+=iUtXXHQRe3kM-zMJUo4iTODEiSMmp7nXHaG2QdxL_Q@mail.gmail.com>

On Fri, Jun 3, 2016 at 10:36 AM, Russell King - ARM Linux
<linux at armlinux.org.uk> wrote:
> On Fri, Jun 03, 2016 at 10:29:40AM -0500, Rob Herring wrote:
>> On Fri, Jun 3, 2016 at 9:21 AM, Russell King <rmk+kernel at armlinux.org.uk> wrote:
>> > Add common OF-based component functionality for matching devices by
>> > device node, and releasing the device node at the appropraite time.
>> >
>> > Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
>> > ---
>> >  drivers/of/Makefile          |  2 +-
>> >  drivers/of/of_component.c    | 41 +++++++++++++++++++++++++++++++++++++++++
>> >  include/linux/of_component.h | 14 ++++++++++++++
>>
>> I'd prefer this to go into drivers/base/component.c. That's the
>> general direction we've been moving.
>
> I'd prefer not to, I don't want to turn the component helpers into
> something OF specific.  They aren't OF specific.

Fine, not enough code to argue about...

>> I'd expect this would cause some build failures unless the cases you
>> converted all depend on CONFIG_OF.
>
> Okay, I'll stick with v1 then, and the duplication that v1 involves.

Why? You don't want to add empty functions? Seems like good clean-up.

Rob


From davem at davemloft.net  Fri Jun  3 16:54:43 2016
From: davem at davemloft.net (David Miller)
Date: Fri, 03 Jun 2016 19:54:43 -0400 (EDT)
Subject: [PATCH V4 0/4] net-next: mediatek: improve phy support
In-Reply-To: <1464941829-38712-1-git-send-email-john@phrozen.org>
References: <1464941829-38712-1-git-send-email-john@phrozen.org>
Message-ID: <20160603.195443.1166932614450106227.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Fri,  3 Jun 2016 10:17:05 +0200

> The current driver did not handle the RGMII delay modes and asymmetric flow
> control properly. The mii_bus is not freed properly. Also add support for
> fixed-phy allowing the driver to work on SoCs that have an internal gigabit
> switch.

Series applied, thanks John.


From john at phrozen.org  Sat Jun  4 23:32:53 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:53 +0200
Subject: [PATCH 00/12] net: mediatek: various small fixes
Message-ID: <1465108385-38286-1-git-send-email-john@phrozen.org>

This series contains various small fixes that we stumbled across while
doing thorough testing and code level reviewing of the driver. The only
patch that sticks out is the first one, which addresses a DQL related
issue. The rest are just minor fixes.

John Crispin (12):
  net: mediatek: fix DQL support
  net: mediatek: add missing return code check
  net: mediatek: fix missing free of scratch memory
  net: mediatek: invalid buffer lookup in mtk_tx_map()
  net: mediatek: dropped rx packets are not being counted properly
  net: mediatek: add next data pointer coherency protection
  net: mediatek: disable all interrupts during probe
  net: mediatek: fix threshold value
  net: mediatek: increase watchdog_timeo
  net: mediatek: fix off by one in the TX ring allocation
  net: mediatek: only wake the queue if it is stopped
  net: mediatek: remove superfluous queue wake up call

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   94 ++++++++++++++++++---------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    3 +
 2 files changed, 65 insertions(+), 32 deletions(-)

-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:56 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:56 +0200
Subject: [PATCH 03/12] net: mediatek: fix missing free of scratch memory
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-4-git-send-email-john@phrozen.org>

Scratch memory gets allocated in mtk_init_fq_dma() but the corresponding
code to free it is missing inside mtk_dma_free() causing a memory leak.
With this patch applied, we can run ifconfig up/down several thousand
times without any problems.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   18 +++++++++++++-----
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    2 ++
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 6780886..8d79bbf 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -453,14 +453,14 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
 /* the qdma core needs scratch memory to be setup */
 static int mtk_init_fq_dma(struct mtk_eth *eth)
 {
-	dma_addr_t phy_ring_head, phy_ring_tail;
+	dma_addr_t phy_ring_tail;
 	int cnt = MTK_DMA_SIZE;
 	dma_addr_t dma_addr;
 	int i;
 
 	eth->scratch_ring = dma_alloc_coherent(eth->dev,
 					       cnt * sizeof(struct mtk_tx_dma),
-					       &phy_ring_head,
+					       &eth->phy_scratch_ring,
 					       GFP_ATOMIC | __GFP_ZERO);
 	if (unlikely(!eth->scratch_ring))
 		return -ENOMEM;
@@ -477,19 +477,19 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
 		return -ENOMEM;
 
 	memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
-	phy_ring_tail = phy_ring_head +
+	phy_ring_tail = eth->phy_scratch_ring +
 			(sizeof(struct mtk_tx_dma) * (cnt - 1));
 
 	for (i = 0; i < cnt; i++) {
 		eth->scratch_ring[i].txd1 =
 					(dma_addr + (i * MTK_QDMA_PAGE_SIZE));
 		if (i < cnt - 1)
-			eth->scratch_ring[i].txd2 = (phy_ring_head +
+			eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
 				((i + 1) * sizeof(struct mtk_tx_dma)));
 		eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
 	}
 
-	mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+	mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
 	mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
 	mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
 	mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
@@ -1189,6 +1189,14 @@ static void mtk_dma_free(struct mtk_eth *eth)
 	for (i = 0; i < MTK_MAC_COUNT; i++)
 		if (eth->netdev[i])
 			netdev_reset_queue(eth->netdev[i]);
+	if (eth->scratch_ring) {
+		dma_free_coherent(eth->dev,
+				  MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
+				  eth->scratch_ring,
+				  eth->phy_scratch_ring);
+		eth->scratch_ring = NULL;
+		eth->phy_scratch_ring = 0;
+	}
 	mtk_tx_clean(eth);
 	mtk_rx_clean(eth);
 	kfree(eth->scratch_head);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index eed626d..57f7e8a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -357,6 +357,7 @@ struct mtk_rx_ring {
  * @rx_ring:		Pointer to the memore holding info about the RX ring
  * @rx_napi:		The NAPI struct
  * @scratch_ring:	Newer SoCs need memory for a second HW managed TX ring
+ * @phy_scratch_ring:	physical address of scratch_ring
  * @scratch_head:	The scratch memory that scratch_ring points to.
  * @clk_ethif:		The ethif clock
  * @clk_esw:		The switch clock
@@ -384,6 +385,7 @@ struct mtk_eth {
 	struct mtk_rx_ring		rx_ring;
 	struct napi_struct		rx_napi;
 	struct mtk_tx_dma		*scratch_ring;
+	dma_addr_t			phy_scratch_ring;
 	void				*scratch_head;
 	struct clk			*clk_ethif;
 	struct clk			*clk_esw;
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:59 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:59 +0200
Subject: [PATCH 06/12] net: mediatek: add next data pointer coherency
 protection
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-7-git-send-email-john@phrozen.org>

The QDMA engine can fail to update the register pointing to the next TX
descriptor if this bit does not get set in the QDMA configuration register.
Not setting this bit can result in invalid values inside the TX rings
registers which will causes TX stalls.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 0610262..bfac376 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1261,7 +1261,7 @@ static int mtk_start_dma(struct mtk_eth *eth)
 	mtk_w32(eth,
 		MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
 		MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
-		MTK_RX_BT_32DWORDS,
+		MTK_RX_BT_32DWORDS | MTK_NDP_CO_PRO,
 		MTK_QDMA_GLO_CFG);
 
 	return 0;
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 57f7e8a..a5eb7c6 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -91,6 +91,7 @@
 #define MTK_QDMA_GLO_CFG	0x1A04
 #define MTK_RX_2B_OFFSET	BIT(31)
 #define MTK_RX_BT_32DWORDS	(3 << 11)
+#define MTK_NDP_CO_PRO		BIT(10)
 #define MTK_TX_WB_DDONE		BIT(6)
 #define MTK_DMA_SIZE_16DWORDS	(2 << 4)
 #define MTK_RX_DMA_BUSY		BIT(3)
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:00 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:00 +0200
Subject: [PATCH 07/12] net: mediatek: disable all interrupts during probe
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-8-git-send-email-john@phrozen.org>

The current code only disables those IRQs that we will later use. To
ensure that we have a predefined state, we really want to disable all IRQs.
Change the code to disable all IRQs to achieve this.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index bfac376..93af4e3 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1375,7 +1375,7 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 
 	/* disable delay and normal interrupt */
 	mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
-	mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+	mtk_irq_disable(eth, ~0);
 	mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
 	mtk_w32(eth, 0, MTK_RST_GL);
 
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:54 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:54 +0200
Subject: [PATCH 01/12] net: mediatek: fix DQL support
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-2-git-send-email-john@phrozen.org>

The MTK ethernet core has 2 MACs both sitting on the same DMA ring. For
DQL to be deterministic it needs to track the amount of data in the DMA
ring and not the amount of data enqueued on each device. The current code
is incorrect, fix it by making it each device track its own traffic aswell
as the traffic of the other device.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   33 ++++++++++++++++-----------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index c984462..45f8dbf 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -625,7 +625,16 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
 	WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
 				(!nr_frags * TX_DMA_LS0)));
 
-	netdev_sent_queue(dev, skb->len);
+	/* we have a single DMA ring so BQL needs to be updated for all devices
+	 * sitting on this ring
+	 */
+	for (i = 0; i < MTK_MAC_COUNT; i++) {
+		if (!eth->netdev[i])
+			continue;
+
+		netdev_sent_queue(eth->netdev[i], skb->len);
+	}
+
 	skb_tx_timestamp(skb);
 
 	ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2);
@@ -853,21 +862,18 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 	struct mtk_tx_dma *desc;
 	struct sk_buff *skb;
 	struct mtk_tx_buf *tx_buf;
-	int total = 0, done[MTK_MAX_DEVS];
-	unsigned int bytes[MTK_MAX_DEVS];
+	int total = 0, done = 0;
+	unsigned int bytes = 0;
 	u32 cpu, dma;
 	static int condition;
 	int i;
 
-	memset(done, 0, sizeof(done));
-	memset(bytes, 0, sizeof(bytes));
-
 	cpu = mtk_r32(eth, MTK_QTX_CRX_PTR);
 	dma = mtk_r32(eth, MTK_QTX_DRX_PTR);
 
 	desc = mtk_qdma_phys_to_virt(ring, cpu);
 
-	while ((cpu != dma) && budget) {
+	while ((cpu != dma) && done < budget) {
 		u32 next_cpu = desc->txd2;
 		int mac;
 
@@ -887,9 +893,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 		}
 
 		if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) {
-			bytes[mac] += skb->len;
-			done[mac]++;
-			budget--;
+			bytes += skb->len;
+			done++;
 		}
 		mtk_tx_unmap(eth->dev, tx_buf);
 
@@ -902,11 +907,13 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 
 	mtk_w32(eth, cpu, MTK_QTX_CRX_PTR);
 
+	/* we have a single DMA ring so BQL needs to be updated for all devices
+	 * sitting on this ring
+	 */
 	for (i = 0; i < MTK_MAC_COUNT; i++) {
-		if (!eth->netdev[i] || !done[i])
+		if (!eth->netdev[i])
 			continue;
-		netdev_completed_queue(eth->netdev[i], done[i], bytes[i]);
-		total += done[i];
+		netdev_completed_queue(eth->netdev[i], done, bytes);
 	}
 
 	/* read hw index again make sure no new tx packet */
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:58 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:58 +0200
Subject: [PATCH 05/12] net: mediatek: dropped rx packets are not being counted
 properly
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-6-git-send-email-john@phrozen.org>

There are two places inside mtk_poll_rx where rx_dropped is not being
incremented properly. Fix this by adding the missing code to increment
the counter.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 4f22ee9..0610262 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -810,6 +810,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
 					  DMA_FROM_DEVICE);
 		if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
 			skb_free_frag(new_data);
+			netdev->stats.rx_dropped++;
 			goto release_desc;
 		}
 
@@ -817,6 +818,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
 		skb = build_skb(data, ring->frag_size);
 		if (unlikely(!skb)) {
 			put_page(virt_to_head_page(new_data));
+			netdev->stats.rx_dropped++;
 			goto release_desc;
 		}
 		skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:02 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:02 +0200
Subject: [PATCH 09/12] net: mediatek: increase watchdog_timeo
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-10-git-send-email-john@phrozen.org>

During stress testing, after reducing the threshold value, we have seen
TX timeouts that were caused by the watchdog_timeo value being too low.
Increase the value to 5 * HZ which is a value commonly used by many other
drivers.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8b289e1..24714ab 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1688,7 +1688,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 	mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
 
 	SET_NETDEV_DEV(eth->netdev[id], eth->dev);
-	eth->netdev[id]->watchdog_timeo = HZ;
+	eth->netdev[id]->watchdog_timeo = 5 * HZ;
 	eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
 	eth->netdev[id]->base_addr = (unsigned long)eth->base;
 	eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:01 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:01 +0200
Subject: [PATCH 08/12] net: mediatek: fix threshold value
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-9-git-send-email-john@phrozen.org>

The logic to calculate the threshold value for stopping the TX queue is
bad. Currently it will always use 1/2 of the rings size, which is way too
much. Set the threshold to MAX_SKB_FRAGS. This makes sure that the queue
is stopped when there is not enough room to accept an additional segment.?

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 93af4e3..8b289e1 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1012,8 +1012,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
 	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
 	ring->next_free = &ring->dma[0];
 	ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
-	ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
-			      MAX_SKB_FRAGS);
+	ring->thresh = MAX_SKB_FRAGS;
 
 	/* make sure that all changes to the dma ring are flushed before we
 	 * continue
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:55 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:55 +0200
Subject: [PATCH 02/12] net: mediatek: add missing return code check
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-3-git-send-email-john@phrozen.org>

The code fails to check if the scratch memory was properly allocated. Add
this check and return with an error if the allocation failed.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 45f8dbf..6780886 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -467,6 +467,9 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
 
 	eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
 				    GFP_KERNEL);
+	if (unlikely(!eth->scratch_head))
+		return -ENOMEM;
+
 	dma_addr = dma_map_single(eth->dev,
 				  eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
 				  DMA_FROM_DEVICE);
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:32:57 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:32:57 +0200
Subject: [PATCH 04/12] net: mediatek: invalid buffer lookup in mtk_tx_map()
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-5-git-send-email-john@phrozen.org>

The lookup of the tx_buffer in the error path inside mtk_tx_map() uses the
wrong descriptor pointer. This looks like a copy & paste error. Change the
code to use the correct pointer.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8d79bbf..4f22ee9 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -655,7 +655,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
 
 err_dma:
 	do {
-		tx_buf = mtk_desc_to_tx_buf(ring, txd);
+		tx_buf = mtk_desc_to_tx_buf(ring, itxd);
 
 		/* unmap dma */
 		mtk_tx_unmap(&dev->dev, tx_buf);
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:05 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:05 +0200
Subject: [PATCH 12/12] net: mediatek: remove superfluous queue wake up call
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-13-git-send-email-john@phrozen.org>

The code checks if the queue should be stopped because we are below the
threshold of free descriptors only to check if it should be started again.
If we do end up in a state where we are at the threshold limit, it makes
more sense to just stop the queue and wait for the next IRQ to trigger the
TX housekeeping again. There is no rush in enqueuing the next packet, it
needs to wait for all the others in the queue to be dispatched first
anyway.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 40d3cfd..ebfca7d 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -764,12 +764,9 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
 		goto drop;
 
-	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
 		mtk_stop_queue(eth);
-		if (unlikely(atomic_read(&ring->free_count) >
-			     ring->thresh))
-			mtk_wake_queue(eth);
-	}
+
 	spin_unlock_irqrestore(&eth->page_lock, flags);
 
 	return NETDEV_TX_OK;
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:04 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:04 +0200
Subject: [PATCH 11/12] net: mediatek: only wake the queue if it is stopped
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-12-git-send-email-john@phrozen.org>

The current code unconditionally wakes up the queue at the end of each
tx_poll action. Change the code to only wake up the queues if any of
them have actually been stopped before.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 6daf48b..40d3cfd 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -685,6 +685,20 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
 	return nfrags;
 }
 
+static int mtk_queue_stopped(struct mtk_eth *eth)
+{
+	int i;
+
+	for (i = 0; i < MTK_MAC_COUNT; i++) {
+		if (!eth->netdev[i])
+			continue;
+		if (netif_queue_stopped(eth->netdev[i]))
+			return 1;
+	}
+
+	return 0;
+}
+
 static void mtk_wake_queue(struct mtk_eth *eth)
 {
 	int i;
@@ -929,7 +943,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 	if (!total)
 		return 0;
 
-	if (atomic_read(&ring->free_count) > ring->thresh)
+	if (mtk_queue_stopped(eth) &&
+	    (atomic_read(&ring->free_count) > ring->thresh))
 		mtk_wake_queue(eth);
 
 	return total;
-- 
1.7.10.4



From john at phrozen.org  Sat Jun  4 23:33:03 2016
From: john at phrozen.org (John Crispin)
Date: Sun,  5 Jun 2016 08:33:03 +0200
Subject: [PATCH 10/12] net: mediatek: fix off by one in the TX ring allocation
In-Reply-To: <1465108385-38286-1-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
Message-ID: <1465108385-38286-11-git-send-email-john@phrozen.org>

The TX ring setup has an off by one error causing it to not utilise all
descriptors. This has the side effect that we need to reset the next
pointer at runtime to make it work. Fix the off by one and remove the
code fixing the ring at runtime.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 24714ab..6daf48b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -903,7 +903,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 		}
 		mtk_tx_unmap(eth->dev, tx_buf);
 
-		ring->last_free->txd2 = next_cpu;
 		ring->last_free = desc;
 		atomic_inc(&ring->free_count);
 
@@ -1011,7 +1010,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
 
 	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
 	ring->next_free = &ring->dma[0];
-	ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
+	ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
 	ring->thresh = MAX_SKB_FRAGS;
 
 	/* make sure that all changes to the dma ring are flushed before we
-- 
1.7.10.4



From davem at davemloft.net  Sun Jun  5 00:32:42 2016
From: davem at davemloft.net (David Miller)
Date: Sun, 05 Jun 2016 00:32:42 -0700 (PDT)
Subject: [PATCH 01/12] net: mediatek: fix DQL support
In-Reply-To: <1465108385-38286-2-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-2-git-send-email-john@phrozen.org>
Message-ID: <20160605.003242.796780762112643713.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Sun,  5 Jun 2016 08:32:54 +0200

> @@ -625,7 +625,16 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
>  	WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
>  				(!nr_frags * TX_DMA_LS0)));
>  
> -	netdev_sent_queue(dev, skb->len);
> +	/* we have a single DMA ring so BQL needs to be updated for all devices
> +	 * sitting on this ring
> +	 */
> +	for (i = 0; i < MTK_MAC_COUNT; i++) {
> +		if (!eth->netdev[i])
> +			continue;
> +
> +		netdev_sent_queue(eth->netdev[i], skb->len);
> +	}
> +
>  	skb_tx_timestamp(skb);

Sorry, this is very far from working.

You cannot asynchronously touch the DQL state of another netdevice.

You have to hold the TX lock of a queue while changing it's DQL state,
otherwise you'll corrupt the state.

This "loop over all possible devices on this DMA ring" is pretty
expensive for the problem you're trying to solve.

You'll have to find another way to fix this bug, which BTW I'm not too
clear about.  The commit message doesn't explain sufficiently what the
actual problem is.  "not deterministic" doesn't give enough details.


From chunfeng.yun at mediatek.com  Sun Jun  5 02:33:32 2016
From: chunfeng.yun at mediatek.com (chunfeng yun)
Date: Sun, 5 Jun 2016 17:33:32 +0800
Subject: [PATCH v2,3/5] dt-bindings: mtu3: add devicetree bindings
In-Reply-To: <5751874C.9070100@gmail.com>
References: <1464673962-11506-1-git-send-email-chunfeng.yun@mediatek.com>
 <1464673962-11506-4-git-send-email-chunfeng.yun@mediatek.com>
 <5751874C.9070100@gmail.com>
Message-ID: <1465119212.11651.141.camel@mhfsdcap03>

On Fri, 2016-06-03 at 15:34 +0200, Matthias Brugger wrote:
> 
> On 31/05/16 07:52, Chunfeng Yun wrote:
> > add a DT binding doc for MediaTek USB3 DRD driver
> >
> > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> > ---
> >   Documentation/devicetree/bindings/usb/mtu3.txt |   85 ++++++++++++++++++++++++
> >   1 file changed, 85 insertions(+)
> >   create mode 100644 Documentation/devicetree/bindings/usb/mtu3.txt
> >
> > diff --git a/Documentation/devicetree/bindings/usb/mtu3.txt b/Documentation/devicetree/bindings/usb/mtu3.txt
> > new file mode 100644
> > index 0000000..571ae8b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/usb/mtu3.txt
> 
> The file should be called mt8173-mtu3.txt to be in line with mt8173-xhci.txt
> Ok, I will rename it
> 
> > @@ -0,0 +1,85 @@
> > +The device node for Mediatek USB3.0 DRD controller
> > +
> > +Required properties:
> > + - compatible : should be "mediatek,mt8173-mtu3"
> > + - reg : specifies physical base address and size of the registers
> > + - reg-names: should be "mac" for device IP and "ippc" for IP port control
> > + - interrupts : interrupt used by the device IP
> > + - power-domains : a phandle to USB power domain node to control USB's
> > +	mtcmos
> > + - vusb33-supply : regulator of USB avdd3.3v
> > + - clocks : a list of phandle + clock-specifier pairs, one for each
> > +	entry in clock-names
> > + - clock-names : must contain "sys_ck" for clock of controller;
> > +	"wakeup_deb_p0" and "wakeup_deb_p1" are optional, they are
> > +	depends on "mediatek,enable-wakeup"
> > + - phys : a list of phandle + phy specifier pairs
> > + - dr_mode : should be one of "host", "peripheral" or "otg",
> > +	refer to usb/generic.txt
> > +
> > +Optional properties:
> > + - #address-cells, #size-cells : should be '2' if the device has sub-nodes
> > +	with 'reg' property
> > + - ranges : allows valid 1:1 translation between child's address space and
> > +	parent's address space
> > + - extcon : external connector for vbus and idpin changes detection, needed
> > +	when supports dual-role mode.
> > + - vbus-supply : reference to the VBUS regulator, needed when supports
> > +	dual-role mode.
> > + - pinctl-names : a pinctrl state named "default" must be defined,
> > +	"id_float" and "id_ground" are optinal which depends on
> > +	"mediatek,enable-manual-drd"
> 
> So pinctrl is optional, but if you put it, you need at least "default"?
Yes, the first one is "default".

> 
> Apart from that, DT maintainers prefer to have the binding patches at 
> the beginning of a series, so it's easier for them to not oversee any.
> Ok.

Thanks a lot.
> Regards,
> Matthias





From andrew at lunn.ch  Sun Jun  5 07:56:32 2016
From: andrew at lunn.ch (Andrew Lunn)
Date: Sun, 5 Jun 2016 16:56:32 +0200
Subject: [PATCH 09/12] net: mediatek: increase watchdog_timeo
In-Reply-To: <1465108385-38286-10-git-send-email-john@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-10-git-send-email-john@phrozen.org>
Message-ID: <20160605145632.GI12165@lunn.ch>

On Sun, Jun 05, 2016 at 08:33:02AM +0200, John Crispin wrote:
> During stress testing, after reducing the threshold value, we have seen
> TX timeouts that were caused by the watchdog_timeo value being too low.
> Increase the value to 5 * HZ which is a value commonly used by many other
> drivers.

I've never studied what watchdog_timeo actually means. Does it mean a
transmit has not completed in that amount of time? Would this imply
you have 5 seconds worth of packets in your transmit queue? Do you
know what the driver is doing during this 5 seconds?

Thanks
    Andrew


From honghui.zhang at mediatek.com  Sun Jun  5 18:30:10 2016
From: honghui.zhang at mediatek.com (Honghui Zhang)
Date: Mon, 6 Jun 2016 09:30:10 +0800
Subject: [PATCH v3 0/5] MT2701 iommu support
In-Reply-To: <1464339400-22559-1-git-send-email-honghui.zhang@mediatek.com>
References: <1464339400-22559-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465176610.14476.2.camel@mtksdaap41>

On Fri, 2016-05-27 at 16:56 +0800, honghui.zhang at mediatek.com wrote:
> From: Honghui Zhang <honghui.zhang at mediatek.com>
> 

Is there will be some comments for this version patchset?
Any comments is welcome.
Thanks.

>   Mediatek's m4u(Multimedia Memory Management Unit) and SMI(Smart
> Multimedia Interface)have two generations HW. They basically sharing the
> same hardware block diagram, but have some difference as below:
> 
>   Generation one m4u only supports one layer, flat pagetable addressing,
> and only supports 4K size page mapping. While generation two m4u supports 2
> levels of pagetable which uses the ARM short-descriptor translation table
> format for address translation.
> They have slight different register base and register offset.
> They have very different HW ports defines.
> 
>   Generaion one SMI has additional "async" clock which transform the smi
> clock into emi clock domain, this clock should be prepared and enabled for
> SMI generation one HW.
> The register which control the iommu need to translation the address or not
> for a particular port is located at smi ao base(smi always on register
> base) for generation one SMI HW, but located at each larb's register base
> for generation two HW.
> 
> This patch set add mt2701 iommu support, it's based on 4.6-rc1 and James
> Liao's "Add clock support for Mediatek MT2701 v8[1]" and "Mediatek MT2701
> SCPSYS power domain support v7[2]" patch.
> 
> v3:
> -Rebase on "of: Implement iterator for phandles[3]" and take use of
>  of_for_each_phandle.
> -Forward-declare mtk_iommu_domain and implement the struct separately.
> -Free the pagetable memory in mtk_iommu_domain_free
> -Roll back the mapping in error case.
> -Minor cleanups.
> 
> v2: https://lists.linuxfoundation.org/pipermail/iommu/2016-May/017068.html
> -Fix syntax errors in dt-bindings.
> -Use dma_alloc/free_coherent to allocate pagetable memory and reduce the
>  streaming DMA stuff.
> -Make the mtk_iommu_ops.pgsize_bitmap as ~0UL << MT2701_IOMMU_PAGE_SHIFT.
> -Use macro instead of variable to indicate the pagetable size.
> -Change some macro name from MTK_XXX to MT2701_XXX.
> 
> v1: http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005301.html
> -initial version
> 
> [1] http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005439.html
> [2] http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005429.html
> [3] https://lists.linuxfoundation.org/pipermail/iommu/2016-April/016300.html
> 
> Honghui Zhang (5):
>   dt-bindings: mediatek: add descriptions for mediatek mt2701 iommu and
>     smi
>   iommu/mediatek: move the common struct into header file
>   memory/mediatek: add support for mt2701
>   iommu/mediatek: add support for mtk iommu generation one HW
>   ARM: dts: mt2701: add iommu/smi dtsi node for mt2701
> 
>  .../devicetree/bindings/iommu/mediatek,iommu.txt   |  13 +-
>  .../memory-controllers/mediatek,smi-common.txt     |  21 +-
>  .../memory-controllers/mediatek,smi-larb.txt       |   4 +-
>  arch/arm/boot/dts/mt2701.dtsi                      |  51 ++
>  drivers/iommu/Kconfig                              |  18 +
>  drivers/iommu/Makefile                             |   1 +
>  drivers/iommu/mtk_iommu.c                          |  48 +-
>  drivers/iommu/mtk_iommu.h                          |  77 +++
>  drivers/iommu/mtk_iommu_v1.c                       | 728 +++++++++++++++++++++
>  drivers/memory/mtk-smi.c                           | 168 ++++-
>  include/dt-bindings/memory/mt2701-larb-port.h      |  85 +++
>  11 files changed, 1140 insertions(+), 74 deletions(-)
>  create mode 100644 drivers/iommu/mtk_iommu.h
>  create mode 100644 drivers/iommu/mtk_iommu_v1.c
>  create mode 100644 include/dt-bindings/memory/mt2701-larb-port.h
> 




From john at phrozen.org  Sun Jun  5 23:24:08 2016
From: john at phrozen.org (John Crispin)
Date: Mon, 6 Jun 2016 08:24:08 +0200
Subject: [PATCH 09/12] net: mediatek: increase watchdog_timeo
In-Reply-To: <20160605145632.GI12165@lunn.ch>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-10-git-send-email-john@phrozen.org>
 <20160605145632.GI12165@lunn.ch>
Message-ID: <0da64a49-d100-f83d-e6d3-91b706710660@phrozen.org>



On 05/06/2016 16:56, Andrew Lunn wrote:
> On Sun, Jun 05, 2016 at 08:33:02AM +0200, John Crispin wrote:
>> During stress testing, after reducing the threshold value, we have seen
>> TX timeouts that were caused by the watchdog_timeo value being too low.
>> Increase the value to 5 * HZ which is a value commonly used by many other
>> drivers.
> 
> I've never studied what watchdog_timeo actually means. Does it mean a
> transmit has not completed in that amount of time? Would this imply
> you have 5 seconds worth of packets in your transmit queue? Do you
> know what the driver is doing during this 5 seconds?

Hi Andrew,

it is waiting for the watchdog to trigger :-) TBH the 1s seems to be too
short to for the dma ring length to be flushed and i had to pick some
value and 5 is used most places.

it really depends on the amount of packets in the queue, their length
and the mac setting. the timeout needs to be large enough that it would
not trigger incorrectly even if the mac is on 10mbit half duplex and all
frames in the queue were maximum size.

	John


From john at phrozen.org  Sun Jun  5 23:43:13 2016
From: john at phrozen.org (John Crispin)
Date: Mon, 6 Jun 2016 08:43:13 +0200
Subject: [PATCH 01/12] net: mediatek: fix DQL support
In-Reply-To: <20160605.003242.796780762112643713.davem@davemloft.net>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-2-git-send-email-john@phrozen.org>
 <20160605.003242.796780762112643713.davem@davemloft.net>
Message-ID: <e1b52f75-edb7-8504-3e6d-40fcb03e39df@phrozen.org>



On 05/06/2016 09:32, David Miller wrote:
> From: John Crispin <john at phrozen.org>
> Date: Sun,  5 Jun 2016 08:32:54 +0200
> 
>> @@ -625,7 +625,16 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
>>  	WRITE_ONCE(itxd->txd3, (TX_DMA_SWC | TX_DMA_PLEN0(skb_headlen(skb)) |
>>  				(!nr_frags * TX_DMA_LS0)));
>>  
>> -	netdev_sent_queue(dev, skb->len);
>> +	/* we have a single DMA ring so BQL needs to be updated for all devices
>> +	 * sitting on this ring
>> +	 */
>> +	for (i = 0; i < MTK_MAC_COUNT; i++) {
>> +		if (!eth->netdev[i])
>> +			continue;
>> +
>> +		netdev_sent_queue(eth->netdev[i], skb->len);
>> +	}
>> +
>>  	skb_tx_timestamp(skb);
> 
> Sorry, this is very far from working.
> 
> You cannot asynchronously touch the DQL state of another netdevice.
> 
> You have to hold the TX lock of a queue while changing it's DQL state,
> otherwise you'll corrupt the state.
> 
> This "loop over all possible devices on this DMA ring" is pretty
> expensive for the problem you're trying to solve.
> 
> You'll have to find another way to fix this bug, which BTW I'm not too
> clear about.  The commit message doesn't explain sufficiently what the
> actual problem is.  "not deterministic" doesn't give enough details.
> 

Hi David,

DQL is supposed to measure how much data is enqueued on a netdev. the
problem here is that two devices share the same hardware queue. fq_codel
for example uses the values from dql to base its QoS judgement on. if we
track the dql of the 2 devices separately then the values will not take
the actual amount of data enqueued into account but only parts of it.
this will make the queue length used as a basis for fq_codel
calculations non deterministic thus breaking qos. dql needs to track the
amount of data in the physical queue underlying the netdev to be useful.
hope that explanation is better to understand.

i think one solution would be to add some code to have 2 devices share
the same dql instance. would that be an acceptable solution ?

anyhow, i will resend the series without the dql patch today and then
worry about it afterwards.

	John


From egtvedt at samfundet.no  Sun Jun  5 23:45:12 2016
From: egtvedt at samfundet.no (Hans-Christian Noren Egtvedt)
Date: Mon, 6 Jun 2016 08:45:12 +0200
Subject: [RFC v3 44/45] dma-mapping: Remove dma_get_attr
In-Reply-To: <1464881987-13203-45-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-45-git-send-email-k.kozlowski@samsung.com>
Message-ID: <20160606064512.GB7998@samfundet.no>

Around Thu 02 Jun 2016 17:39:46 +0200 or thereabout, Krzysztof Kozlowski wrote:
> After switching DMA attributes to unsigned long it is easier to just
> compare the bits.
> 
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
> ---
>  Documentation/DMA-API.txt                      |  4 +--
>  arch/arc/mm/dma.c                              |  4 +--
>  arch/arm/mm/dma-mapping.c                      | 36 ++++++++++++--------------
>  arch/arm/xen/mm.c                              |  4 +--
>  arch/arm64/mm/dma-mapping.c                    | 10 +++----
>  arch/avr32/mm/dma-coherent.c                   |  4 +--

For the AVR32 related change

Acked-by: Hans-Christian Noren Egtvedt <egtvedt at samfundet.no>

>  arch/ia64/sn/pci/pci_dma.c                     | 10 ++-----
>  arch/metag/kernel/dma.c                        |  2 +-
>  arch/mips/mm/dma-default.c                     |  6 ++---
>  arch/openrisc/kernel/dma.c                     |  4 +--
>  arch/parisc/kernel/pci-dma.c                   |  2 +-
>  arch/powerpc/platforms/cell/iommu.c            | 10 +++----
>  drivers/gpu/drm/rockchip/rockchip_drm_gem.c    |  2 +-
>  drivers/iommu/dma-iommu.c                      |  2 +-
>  drivers/media/v4l2-core/videobuf2-dma-contig.c |  2 +-
>  include/linux/dma-mapping.h                    | 13 ----------
>  16 files changed, 46 insertions(+), 69 deletions(-)

<snipp non-AVR32>

> diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
> index fc51f4421933..58610d0df7ed 100644
> --- a/arch/avr32/mm/dma-coherent.c
> +++ b/arch/avr32/mm/dma-coherent.c
> @@ -109,7 +109,7 @@ static void *avr32_dma_alloc(struct device *dev, size_t size,
>  		return NULL;
>  	phys = page_to_phys(page);
>  
> -	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
> +	if (attrs & DMA_ATTR_WRITE_COMBINE) {
>  		/* Now, map the page into P3 with write-combining turned on */
>  		*handle = phys;
>  		return __ioremap(phys, size, _PAGE_BUFFER);
> @@ -123,7 +123,7 @@ static void avr32_dma_free(struct device *dev, size_t size,
>  {
>  	struct page *page;
>  
> -	if (dma_get_attr(DMA_ATTR_WRITE_COMBINE, attrs)) {
> +	if (attrs & DMA_ATTR_WRITE_COMBINE) {
>  		iounmap(cpu_addr);
>  
>  		page = phys_to_page(handle);

<snipp non-AVR32>

-- 
mvh
Hans-Christian Noren Egtvedt


From egtvedt at samfundet.no  Sun Jun  5 23:43:57 2016
From: egtvedt at samfundet.no (Hans-Christian Noren Egtvedt)
Date: Mon, 6 Jun 2016 08:43:57 +0200
Subject: [RFC v3 07/45] avr32: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-8-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-8-git-send-email-k.kozlowski@samsung.com>
Message-ID: <20160606064357.GA7998@samfundet.no>

Around Thu 02 Jun 2016 17:39:09 +0200 or thereabout, Krzysztof Kozlowski wrote:
> Split out subsystem specific changes for easier reviews. This will be
> squashed with main commit.
> 
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

Acked-by: Hans-Christian Noren Egtvedt <egtvedt at samfundet.no>

> ---
>  arch/avr32/mm/dma-coherent.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/arch/avr32/mm/dma-coherent.c b/arch/avr32/mm/dma-coherent.c
> index 92cf1fb2b3e6..fc51f4421933 100644
> --- a/arch/avr32/mm/dma-coherent.c
> +++ b/arch/avr32/mm/dma-coherent.c
> @@ -99,7 +99,7 @@ static void __dma_free(struct device *dev, size_t size,
>  }
>  
>  static void *avr32_dma_alloc(struct device *dev, size_t size,
> -		dma_addr_t *handle, gfp_t gfp, struct dma_attrs *attrs)
> +		dma_addr_t *handle, gfp_t gfp, unsigned long attrs)
>  {
>  	struct page *page;
>  	dma_addr_t phys;
> @@ -119,7 +119,7 @@ static void *avr32_dma_alloc(struct device *dev, size_t size,
>  }
>  
>  static void avr32_dma_free(struct device *dev, size_t size,
> -		void *cpu_addr, dma_addr_t handle, struct dma_attrs *attrs)
> +		void *cpu_addr, dma_addr_t handle, unsigned long attrs)
>  {
>  	struct page *page;
>  
> @@ -142,7 +142,7 @@ static void avr32_dma_free(struct device *dev, size_t size,
>  
>  static dma_addr_t avr32_dma_map_page(struct device *dev, struct page *page,
>  		unsigned long offset, size_t size,
> -		enum dma_data_direction direction, struct dma_attrs *attrs)
> +		enum dma_data_direction direction, unsigned long attrs)
>  {
>  	void *cpu_addr = page_address(page) + offset;
>  
> @@ -152,7 +152,7 @@ static dma_addr_t avr32_dma_map_page(struct device *dev, struct page *page,
>  
>  static int avr32_dma_map_sg(struct device *dev, struct scatterlist *sglist,
>  		int nents, enum dma_data_direction direction,
> -		struct dma_attrs *attrs)
> +		unsigned long attrs)
>  {
>  	int i;
>  	struct scatterlist *sg;
-- 
mvh
Hans-Christian Noren Egtvedt


From hs.liao at mediatek.com  Mon Jun  6 02:33:53 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Mon, 6 Jun 2016 17:33:53 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <CABb+yY2aOncqM-fOiq_ZBink0Fth6Drd30ZMe3-bXNkodW2dVA@mail.gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com>
 <CABb+yY2aOncqM-fOiq_ZBink0Fth6Drd30ZMe3-bXNkodW2dVA@mail.gmail.com>
Message-ID: <1465205633.25607.33.camel@mtksdaap41>

Hi Matthias, Jassi,

On Fri, 2016-06-03 at 18:41 +0530, Jassi Brar wrote:
> On Fri, Jun 3, 2016 at 4:48 PM, Matthias Brugger <matthias.bgg at gmail.com> wrote:
> > On 03/06/16 08:12, Horng-Shyang Liao wrote:
> >> On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
> 
> >>> I keep thinking about how to get rid of the two data structures,
> >>> task_busy_list and the task_release_wq. We need the latter for the only
> >>> sake of getting a timeout.
> >>>
> >>> Did you have a look on how the mailbox framework handles this?
> >>> By the way, what is the reason to not implement the whole driver as a
> >>> mailbox controller? For me, this driver looks like a good fit.
> >>
> >>
> >> CMDQ needs to encode commands for GCE hardware. We think this behavior
> >> should be put in CMDQ driver, and client just call CMDQ functions.
> >> Therefore, if we want to use mailbox framework, cmdq_rec must be
> >> mailbox client, and the others must be mailbox controller.
> >>
> >
> > You mean the functions to fill the cmdq_rec and execute it?
> > I think this should be part of the driver.
> >
> > Jassi, can you have a look on the interface this driver exports [0].
> > They are needed to actually create the message which will be send.
> > Could something like this be part of a mailbox driver?
> >
> > [0] https://patchwork.kernel.org/patch/9140221/
> >
> Packet creating/parsing should not be a part of controller driver. As
> the log of this patch says, today it is used for only display but in
> future it could work with other h/w as well, so it makes sense to have
> mailbox api do the message queuing, the controller driver do the
> send/receive and client drivers implement display and other h/w
> specific packaging of data (protocol handling).
> 
> So yes, I think this could use mailbox api.
> 
> Cheers.


Let me use display as an example to do some further explanation
about CMDQ in advance. You can think CMDQ is a shadow register
replacement. Therefore, we use cmdq_rec_write(_mask), cmdq_rec_wfe, and
cmdq_rec_clear_event instead of accessing registers, and use
cmdq_rec_flush(_async) instead of atomic swap.

If we use mailbox to do the message queue, we can use mailbox framework
to implement flush and callback. However, I don't think mailbox is
suitable for cmdq_rec_write(_mask), cmdq_rec_wfe, and
cmdq_rec_clear_event since they are just record some commands. Is this
the same as your comment "Packet creating/parsing should not be a part
of controller driver."?

Therefore, do you mean we use mailbox framework to implement flush and
callback and keep other interfaces? Just want to confirm that I get the
correct idea from you. Many thanks for your kindly reply.

Thanks,
HS




From yamada.masahiro at socionext.com  Mon Jun  6 02:40:59 2016
From: yamada.masahiro at socionext.com (Masahiro Yamada)
Date: Mon,  6 Jun 2016 18:40:59 +0900
Subject: [PATCH 0/4] serial: earlycon: keep earlycon symbols for non-module
 and driver clean-ups
Message-ID: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>


1/4: keep earlycon symbols only when CONFIG_SERIAL_EARLYCON is defined
     and the driver is built-in.

2/4 thru 4/4: driver clean-ups.
              (partially reverts build fixes by Arnd Bergmann)



Masahiro Yamada (4):
  earlycon: mark earlycon code as __used iif the caller is built-in
  serial: 8250_uniphier: drop !defined(MODULE) conditional
  serial: 8250_mtk: drop !defined(MODULE) conditional
  serial: 8250_ingenic: drop #if conditional surrounding earlycon code

 drivers/tty/serial/8250/8250_ingenic.c  | 2 --
 drivers/tty/serial/8250/8250_mtk.c      | 2 +-
 drivers/tty/serial/8250/8250_uniphier.c | 2 +-
 drivers/tty/serial/8250/Kconfig         | 2 +-
 include/linux/serial_core.h             | 8 +++++++-
 5 files changed, 10 insertions(+), 6 deletions(-)

-- 
1.9.1



From yamada.masahiro at socionext.com  Mon Jun  6 02:41:02 2016
From: yamada.masahiro at socionext.com (Masahiro Yamada)
Date: Mon,  6 Jun 2016 18:41:02 +0900
Subject: [PATCH 3/4] serial: 8250_mtk: drop !defined(MODULE) conditional
In-Reply-To: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>
References: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>
Message-ID: <1465206063-1018-4-git-send-email-yamada.masahiro@socionext.com>

The !defined(MODULE) conditional has been added to the
OF_EARLYCON_DECLARE() define.

This commit partially reverts commit 3f5921a60f74 ("serial:
8250/mediatek: fix building with SERIAL_8250=m").

Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
---

 drivers/tty/serial/8250/8250_mtk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
index 3489fbc..3611ec9 100644
--- a/drivers/tty/serial/8250/8250_mtk.c
+++ b/drivers/tty/serial/8250/8250_mtk.c
@@ -301,7 +301,7 @@ static struct platform_driver mtk8250_platform_driver = {
 };
 module_platform_driver(mtk8250_platform_driver);
 
-#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE)
+#ifdef CONFIG_SERIAL_8250_CONSOLE
 static int __init early_mtk8250_setup(struct earlycon_device *device,
 					const char *options)
 {
-- 
1.9.1



From arnd at arndb.de  Mon Jun  6 03:25:36 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Mon, 06 Jun 2016 12:25:36 +0200
Subject: [PATCH 0/4] serial: earlycon: keep earlycon symbols for
 non-module and driver clean-ups
In-Reply-To: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>
References: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>
Message-ID: <3280512.PAaBGkzban@wuerfel>

On Monday, June 6, 2016 6:40:59 PM CEST Masahiro Yamada wrote:
> 1/4: keep earlycon symbols only when CONFIG_SERIAL_EARLYCON is defined
>      and the driver is built-in.
> 
> 2/4 thru 4/4: driver clean-ups.
>               (partially reverts build fixes by Arnd Bergmann)
> 
> 
> 


Looks good to me,

Acked-by: Arnd Bergmann <arnd at arndb.de>


From matthias.bgg at gmail.com  Mon Jun  6 03:27:50 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 6 Jun 2016 12:27:50 +0200
Subject: [PATCH 3/4] serial: 8250_mtk: drop !defined(MODULE) conditional
In-Reply-To: <1465206063-1018-4-git-send-email-yamada.masahiro@socionext.com>
References: <1465206063-1018-1-git-send-email-yamada.masahiro@socionext.com>
 <1465206063-1018-4-git-send-email-yamada.masahiro@socionext.com>
Message-ID: <57555026.8030400@gmail.com>



On 06/06/16 11:41, Masahiro Yamada wrote:
> The !defined(MODULE) conditional has been added to the
> OF_EARLYCON_DECLARE() define.
>
> This commit partially reverts commit 3f5921a60f74 ("serial:
> 8250/mediatek: fix building with SERIAL_8250=m").
>
> Signed-off-by: Masahiro Yamada <yamada.masahiro at socionext.com>
> ---

Acked-by: Matthias Brugger <matthias.bgg at gmail.com>

>
>   drivers/tty/serial/8250/8250_mtk.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c
> index 3489fbc..3611ec9 100644
> --- a/drivers/tty/serial/8250/8250_mtk.c
> +++ b/drivers/tty/serial/8250/8250_mtk.c
> @@ -301,7 +301,7 @@ static struct platform_driver mtk8250_platform_driver = {
>   };
>   module_platform_driver(mtk8250_platform_driver);
>
> -#if defined(CONFIG_SERIAL_8250_CONSOLE) && !defined(MODULE)
> +#ifdef CONFIG_SERIAL_8250_CONSOLE
>   static int __init early_mtk8250_setup(struct earlycon_device *device,
>   					const char *options)
>   {
>


From andrew at lunn.ch  Mon Jun  6 05:21:32 2016
From: andrew at lunn.ch (Andrew Lunn)
Date: Mon, 6 Jun 2016 14:21:32 +0200
Subject: [PATCH 09/12] net: mediatek: increase watchdog_timeo
In-Reply-To: <0da64a49-d100-f83d-e6d3-91b706710660@phrozen.org>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-10-git-send-email-john@phrozen.org>
 <20160605145632.GI12165@lunn.ch>
 <0da64a49-d100-f83d-e6d3-91b706710660@phrozen.org>
Message-ID: <20160606122132.GM12165@lunn.ch>

> Hi Andrew,
> 
> it is waiting for the watchdog to trigger :-) TBH the 1s seems to be too
> short to for the dma ring length to be flushed and i had to pick some
> value and 5 is used most places.
> 
> it really depends on the amount of packets in the queue, their length
> and the mac setting. the timeout needs to be large enough that it would
> not trigger incorrectly even if the mac is on 10mbit half duplex and all
> frames in the queue were maximum size.

So you are saying there is 5 seconds worth of traffic in the transmit ring.

As a general point, not specific to this driver, is that wise? Isn't
that really bad buffer bloat?

I just wondered what happened to cause it to have 5 seconds worth of
traffic in the transmit ring. Did downstream signal a pause?  But i
thought the byte queue limit was designed to prevent a big backlog in
the transmit queue? At 10/Half, is it not reacting fast enough?  Since
it is half duplex, do you have a lot of traffic coming the other way
and something is not being fair at distributing up and down traffic?

I'm just wondering if by increasing the watchdog to 5 seconds, you are
just hiding a problem.

    Andrew


From john at phrozen.org  Mon Jun  6 05:38:43 2016
From: john at phrozen.org (John Crispin)
Date: Mon, 6 Jun 2016 14:38:43 +0200
Subject: [PATCH 09/12] net: mediatek: increase watchdog_timeo
In-Reply-To: <20160606122132.GM12165@lunn.ch>
References: <1465108385-38286-1-git-send-email-john@phrozen.org>
 <1465108385-38286-10-git-send-email-john@phrozen.org>
 <20160605145632.GI12165@lunn.ch>
 <0da64a49-d100-f83d-e6d3-91b706710660@phrozen.org>
 <20160606122132.GM12165@lunn.ch>
Message-ID: <5d1b3fe3-4577-207a-3e28-831218158144@phrozen.org>



On 06/06/2016 14:21, Andrew Lunn wrote:
>> Hi Andrew,
>>
>> it is waiting for the watchdog to trigger :-) TBH the 1s seems to be too
>> short to for the dma ring length to be flushed and i had to pick some
>> value and 5 is used most places.
>>
>> it really depends on the amount of packets in the queue, their length
>> and the mac setting. the timeout needs to be large enough that it would
>> not trigger incorrectly even if the mac is on 10mbit half duplex and all
>> frames in the queue were maximum size.
> 
> So you are saying there is 5 seconds worth of traffic in the transmit ring.
> 
> As a general point, not specific to this driver, is that wise? Isn't
> that really bad buffer bloat?
> 
> I just wondered what happened to cause it to have 5 seconds worth of
> traffic in the transmit ring. Did downstream signal a pause?  But i
> thought the byte queue limit was designed to prevent a big backlog in
> the transmit queue? At 10/Half, is it not reacting fast enough?  Since
> it is half duplex, do you have a lot of traffic coming the other way
> and something is not being fair at distributing up and down traffic?
> 
> I'm just wondering if by increasing the watchdog to 5 seconds, you are
> just hiding a problem.
> 
>     Andrew

Hi Andrew,

running the driver without any QoS and using the typical ringsize for
gigabit devices, 1s is not enough. we were seeing false positive
watchdog events. then i grepped to see what other drivers do and most
set 5seconds. ideally the watchdog never triggers as the driver is
functional an does not suffer from deadlocks.

at gbit ethernet can transmit 83 packets that are 1500 bytes long /
second, if there are no pause gaps. at 10Mbit that would be 6 packets.

so assuming we have a ring of 128 and napi set to 64, we would want at
least 2 seconds, 3 if there are a lot of pause gaps, 4-5 if it is half
duplex ... so i took the value commonly used, which is 5 according to grep.

figuring out when the queue is stuck seems to be a little bit more
complicated. imho the trigger should not be based on how long it took to
send a packet, but how long since the last packet was dequeued from dma.

personally i'd rather fix the deadlocks that can happen, which is what
we did, than rely on the watchdog to reset the queue. right now we can
hammer the driver with several streams on both macs utilizing all 4 cpu
cores for several days without seeing any hickups.

		John


From mark.yao at rock-chips.com  Mon Jun  6 17:51:54 2016
From: mark.yao at rock-chips.com (Mark yao)
Date: Tue, 7 Jun 2016 08:51:54 +0800
Subject: [PATCH 10/20] drm: rockchip: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1464877907-27723-11-git-send-email-boris.brezillon@free-electrons.com>
References: <1464877907-27723-1-git-send-email-boris.brezillon@free-electrons.com>
 <1464877907-27723-11-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <57561AAA.5060500@rock-chips.com>

On 2016?06?02? 22:31, Boris Brezillon wrote:
> All outputss have a 1:1 relationship between connectors and encoders
> and the driver is relying on the atomic helpers: we can drop the custom
> ->best_encoder() implementations  and let the core call
> drm_atomic_helper_best_encoder() for us.

Good, All connectors and encoders is 1:1 relationship on rockchip 
outputs, so

Acked-by: Mark Yao <mark.yao at rock-chips.com>

>
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>   drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 9 ---------
>   drivers/gpu/drm/rockchip/inno_hdmi.c   | 9 ---------
>   2 files changed, 18 deletions(-)
>
> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> index dedc65b..ca22e5e 100644
> --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
> @@ -964,18 +964,9 @@ static enum drm_mode_status dw_mipi_dsi_mode_valid(
>   	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
> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
> index f8b4feb..006260d 100644
> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c
> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
> @@ -579,14 +579,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
>   	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)
> @@ -613,7 +605,6 @@ static struct drm_connector_funcs inno_hdmi_connector_funcs = {
>   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)


-- 
?ark Yao




From hs.liao at mediatek.com  Mon Jun  6 19:45:22 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Tue, 7 Jun 2016 10:45:22 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <CABb+yY2aOncqM-fOiq_ZBink0Fth6Drd30ZMe3-bXNkodW2dVA@mail.gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com>
 <CABb+yY2aOncqM-fOiq_ZBink0Fth6Drd30ZMe3-bXNkodW2dVA@mail.gmail.com>
Message-ID: <1465267522.15317.0.camel@mtksdaap41>

On Fri, 2016-06-03 at 18:41 +0530, Jassi Brar wrote:
> On Fri, Jun 3, 2016 at 4:48 PM, Matthias Brugger <matthias.bgg at gmail.com> wrote:
> > On 03/06/16 08:12, Horng-Shyang Liao wrote:
> >> On Thu, 2016-06-02 at 10:46 +0200, Matthias Brugger wrote:
> 
> >>> I keep thinking about how to get rid of the two data structures,
> >>> task_busy_list and the task_release_wq. We need the latter for the only
> >>> sake of getting a timeout.
> >>>
> >>> Did you have a look on how the mailbox framework handles this?
> >>> By the way, what is the reason to not implement the whole driver as a
> >>> mailbox controller? For me, this driver looks like a good fit.
> >>
> >>
> >> CMDQ needs to encode commands for GCE hardware. We think this behavior
> >> should be put in CMDQ driver, and client just call CMDQ functions.
> >> Therefore, if we want to use mailbox framework, cmdq_rec must be
> >> mailbox client, and the others must be mailbox controller.
> >>
> >
> > You mean the functions to fill the cmdq_rec and execute it?
> > I think this should be part of the driver.
> >
> > Jassi, can you have a look on the interface this driver exports [0].
> > They are needed to actually create the message which will be send.
> > Could something like this be part of a mailbox driver?
> >
> > [0] https://patchwork.kernel.org/patch/9140221/
> >
> Packet creating/parsing should not be a part of controller driver. As
> the log of this patch says, today it is used for only display but in
> future it could work with other h/w as well, so it makes sense to have
> mailbox api do the message queuing, the controller driver do the
> send/receive and client drivers implement display and other h/w
> specific packaging of data (protocol handling).
> 
> So yes, I think this could use mailbox api.
> 
> Cheers.

I will try to rewrite CMDQ driver by mailbox framework and discuss with
you if I encounter other problems.

Thanks,
HS



From david.vrabel at citrix.com  Tue Jun  7 02:06:19 2016
From: david.vrabel at citrix.com (David Vrabel)
Date: Tue, 7 Jun 2016 10:06:19 +0100
Subject: [RFC v3 20/45] xen: dma-mapping: Use unsigned long for dma_attrs
In-Reply-To: <1464881987-13203-21-git-send-email-k.kozlowski@samsung.com>
References: <1464881987-13203-1-git-send-email-k.kozlowski@samsung.com>
 <1464881987-13203-21-git-send-email-k.kozlowski@samsung.com>
Message-ID: <57568E8B.4000707@citrix.com>

On 02/06/16 16:39, Krzysztof Kozlowski wrote:
> Split out subsystem specific changes for easier reviews. This will be
> squashed with main commit.

Acked-by: David Vrabel <david.vrabel at citrix.com>

David


From boris.brezillon at free-electrons.com  Tue Jun  7 04:47:58 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:47:58 +0200
Subject: [PATCH v2 03/20] drm: atmel-hlcdc: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-4-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
index 3d34fc4..6119b50 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c
@@ -113,21 +113,9 @@ static int atmel_hlcdc_rgb_mode_valid(struct drm_connector *connector,
 	return atmel_hlcdc_dc_mode_valid(rgb->dc, mode);
 }
 
-
-
-static struct drm_encoder *
-atmel_hlcdc_rgb_best_encoder(struct drm_connector *connector)
-{
-	struct atmel_hlcdc_rgb_output *rgb =
-			drm_connector_to_atmel_hlcdc_rgb_output(connector);
-
-	return &rgb->encoder;
-}
-
 static const struct drm_connector_helper_funcs atmel_hlcdc_panel_connector_helper_funcs = {
 	.get_modes = atmel_hlcdc_panel_get_modes,
 	.mode_valid = atmel_hlcdc_rgb_mode_valid,
-	.best_encoder = atmel_hlcdc_rgb_best_encoder,
 };
 
 static enum drm_connector_status
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:47:57 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:47:57 +0200
Subject: [PATCH v2 02/20] drm: arc: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-3-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder(), and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/arc/arcpgu_hdmi.c | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/arc/arcpgu_hdmi.c b/drivers/gpu/drm/arc/arcpgu_hdmi.c
index 08b6bae..b7a8b2a 100644
--- a/drivers/gpu/drm/arc/arcpgu_hdmi.c
+++ b/drivers/gpu/drm/arc/arcpgu_hdmi.c
@@ -46,23 +46,6 @@ static int arcpgu_drm_connector_get_modes(struct drm_connector *connector)
 	return sfuncs->get_modes(&slave->base, connector);
 }
 
-struct drm_encoder *
-arcpgu_drm_connector_best_encoder(struct drm_connector *connector)
-{
-	struct drm_encoder_slave *slave;
-	struct arcpgu_drm_connector *con =
-		container_of(connector, struct arcpgu_drm_connector, connector);
-
-	slave = con->encoder_slave;
-	if (slave == NULL) {
-		dev_err(connector->dev->dev,
-			"connector_best_encoder: cannot find slave encoder for connector\n");
-		return NULL;
-	}
-
-	return &slave->base;
-}
-
 static enum drm_connector_status
 arcpgu_drm_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -97,7 +80,6 @@ static void arcpgu_drm_connector_destroy(struct drm_connector *connector)
 static const struct drm_connector_helper_funcs
 arcpgu_drm_connector_helper_funcs = {
 	.get_modes = arcpgu_drm_connector_get_modes,
-	.best_encoder = arcpgu_drm_connector_best_encoder,
 };
 
 static const struct drm_connector_funcs arcpgu_drm_connector_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:47:59 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:47:59 +0200
Subject: [PATCH v2 04/20] drm: exynos: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-5-git-send-email-boris.brezillon@free-electrons.com>

We have 1:1 relationship between connectors and encoders and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder()
implementations and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/exynos/exynos_drm_dpi.c  | 9 ---------
 drivers/gpu/drm/exynos/exynos_drm_dsi.c  | 9 ---------
 drivers/gpu/drm/exynos/exynos_drm_vidi.c | 8 --------
 drivers/gpu/drm/exynos/exynos_hdmi.c     | 8 --------
 4 files changed, 34 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
index 5e38e74..ad6b73c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c
@@ -93,17 +93,8 @@ static int exynos_dpi_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-exynos_dpi_best_encoder(struct drm_connector *connector)
-{
-	struct exynos_dpi *ctx = connector_to_dpi(connector);
-
-	return &ctx->encoder;
-}
-
 static const struct drm_connector_helper_funcs exynos_dpi_connector_helper_funcs = {
 	.get_modes = exynos_dpi_get_modes,
-	.best_encoder = exynos_dpi_best_encoder,
 };
 
 static int exynos_dpi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 601ecf8..e07cb1f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -1566,17 +1566,8 @@ static int exynos_dsi_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-exynos_dsi_best_encoder(struct drm_connector *connector)
-{
-	struct exynos_dsi *dsi = connector_to_dsi(connector);
-
-	return &dsi->encoder;
-}
-
 static const struct drm_connector_helper_funcs exynos_dsi_connector_helper_funcs = {
 	.get_modes = exynos_dsi_get_modes,
-	.best_encoder = exynos_dsi_best_encoder,
 };
 
 static int exynos_dsi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
index 608b0af..e8f6c92 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -378,16 +378,8 @@ static int vidi_get_modes(struct drm_connector *connector)
 	return drm_add_edid_modes(connector, edid);
 }
 
-static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector)
-{
-	struct vidi_context *ctx = ctx_from_connector(connector);
-
-	return &ctx->encoder;
-}
-
 static const struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
 	.get_modes = vidi_get_modes,
-	.best_encoder = vidi_best_encoder,
 };
 
 static int vidi_create_connector(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 58de5a4..1625d7c 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -937,17 +937,9 @@ static int hdmi_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector)
-{
-	struct hdmi_context *hdata = connector_to_hdmi(connector);
-
-	return &hdata->encoder;
-}
-
 static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = {
 	.get_modes = hdmi_get_modes,
 	.mode_valid = hdmi_mode_valid,
-	.best_encoder = hdmi_best_encoder,
 };
 
 static int hdmi_create_connector(struct drm_encoder *encoder)
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:47:55 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:47:55 +0200
Subject: [PATCH v2 00/20] drm/atomic: Provide default ->best_encoder() behavior
Message-ID: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>

Hello,

This patch series aims at replacing all dummy ->best_encoder()
implementations where we have a 1:1 relationship between encoders
and connectors.
The core already provides the drm_atomic_helper_best_encoder()
function which is taking the first encoder attached to the
connector (after making sure only one encoder was attached to the
connector), but it's not automatically used, and drivers wanting
to rely on this default behavior have to explicitly assign their
->best_encoder() hook to drm_atomic_helper_best_encoder().

The first patch fixes remaining places where
drm_atomic_helper_best_encoder() should be called when ->best_encoder()
is NULL, so that drivers using the atomic helpers can get rid of the
explicit ->best_encoder assignment if they need to rely on the default
drm_atomic_helper_best_encoder() implementation.

The following patches are killing all open coded ->best_encoder()
implementations that could be replaced by
drm_atomic_helper_best_encoder().

All modifications have been compile tested except for the changed on
the intel driver.
I've also tested on an atmel board, but I recommend waiting for DRM
driver maintainers feedback before applying the associated changes.

Note that once patch 1 is applied, the other patches can be applied
independently.

Best Regards,

Boris

Changes since v1:
- remove useless ->encoder backpointers in some implementations
- documented the default behavior in the vtable doc
- added R-b/A-b tags

Boris Brezillon (20):
  drm/atomic: Fix remaining places where !funcs->best_encoder is valid
  drm: arc: Rely on the default ->best_encoder() behavior
  drm: atmel-hlcdc: Rely on the default ->best_encoder() behavior
  drm: exynos: Rely on the default ->best_encoder() behavior
  drm: fsl-dcu: Rely on the default ->best_encoder() behavior
  drm: i915: Rely on the default ->best_encoder() behavior where
    appropriate
  drm: mediatek: Rely on the default ->best_encoder() behavior
  drm: msm: Rely on the default ->best_encoder() behavior where
    appropriate
  drm: rcar-du: Rely on the default ->best_encoder() behavior
  drm: rockchip: Rely on the default ->best_encoder() behavior
  drm: sti: Rely on the default ->best_encoder() behavior
  drm: sun4i: Rely on the default ->best_encoder() behavior
  drm: tegra: Rely on the default ->best_encoder() behavior
  drm: vc4: Rely on the default ->best_encoder() behavior
  drm: virtgpu: Rely on the default ->best_encoder() behavior
  drm: omap: Rely on the default ->best_encoder() behavior
  drm/bridge: anx78xx: Rely on the default ->best_encoder() behavior
  drm/bridge: ptn3460: Rely on the default ->best_encoder() behavior
  drm/bridge: ps8622: Rely on the default ->best_encoder() behavior
  drm/bridge: dw-hdmi: Use drm_atomic_helper_best_encoder()

 drivers/gpu/drm/arc/arcpgu_hdmi.c                  | 18 ------------------
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 12 ------------
 drivers/gpu/drm/bridge/analogix-anx78xx.c          |  8 --------
 drivers/gpu/drm/bridge/dw-hdmi.c                   | 11 +----------
 drivers/gpu/drm/bridge/nxp-ptn3460.c               |  8 --------
 drivers/gpu/drm/bridge/parade-ps8622.c             | 10 ----------
 drivers/gpu/drm/drm_atomic_helper.c                |  4 +++-
 drivers/gpu/drm/drm_fb_helper.c                    | 13 ++++++++++++-
 drivers/gpu/drm/exynos/exynos_drm_dpi.c            |  9 ---------
 drivers/gpu/drm/exynos/exynos_drm_dsi.c            |  9 ---------
 drivers/gpu/drm/exynos/exynos_drm_vidi.c           |  8 --------
 drivers/gpu/drm/exynos/exynos_hdmi.c               |  8 --------
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c          |  9 ---------
 drivers/gpu/drm/i915/intel_crt.c                   |  1 -
 drivers/gpu/drm/i915/intel_display.c               |  8 --------
 drivers/gpu/drm/i915/intel_dp.c                    |  1 -
 drivers/gpu/drm/i915/intel_drv.h                   |  1 -
 drivers/gpu/drm/i915/intel_dsi.c                   |  1 -
 drivers/gpu/drm/i915/intel_dvo.c                   |  1 -
 drivers/gpu/drm/i915/intel_hdmi.c                  |  1 -
 drivers/gpu/drm/i915/intel_lvds.c                  |  1 -
 drivers/gpu/drm/i915/intel_sdvo.c                  |  1 -
 drivers/gpu/drm/i915/intel_tv.c                    |  1 -
 drivers/gpu/drm/mediatek/mtk_dsi.c                 |  9 ---------
 drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
 drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
 drivers/gpu/drm/omapdrm/omap_connector.c           | 10 ----------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c          | 12 ------------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h          |  3 ---
 drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c          |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c          |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c           |  3 ---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c             |  9 ---------
 drivers/gpu/drm/rockchip/inno_hdmi.c               |  9 ---------
 drivers/gpu/drm/sti/sti_dvo.c                      | 10 ----------
 drivers/gpu/drm/sti/sti_hda.c                      | 10 ----------
 drivers/gpu/drm/sti/sti_hdmi.c                     | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_rgb.c                  | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_tv.c                   |  9 ---------
 drivers/gpu/drm/tegra/drm.h                        |  2 --
 drivers/gpu/drm/tegra/dsi.c                        |  1 -
 drivers/gpu/drm/tegra/hdmi.c                       |  1 -
 drivers/gpu/drm/tegra/output.c                     |  8 --------
 drivers/gpu/drm/tegra/rgb.c                        |  1 -
 drivers/gpu/drm/tegra/sor.c                        |  1 -
 drivers/gpu/drm/vc4/vc4_dpi.c                      |  9 ---------
 drivers/gpu/drm/vc4/vc4_hdmi.c                     |  9 ---------
 drivers/gpu/drm/virtio/virtgpu_display.c           | 10 ----------
 include/drm/drm_modeset_helper_vtables.h           | 10 ++++++++--
 50 files changed, 24 insertions(+), 305 deletions(-)

-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:47:56 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:47:56 +0200
Subject: [PATCH v2 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-2-git-send-email-boris.brezillon@free-electrons.com>

Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
that DRM drivers can leave this hook unassigned if they know they want
to use drm_atomic_helper_best_encoder().

Update the vtables documentation accordingly.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/drm_atomic_helper.c      |  4 +++-
 drivers/gpu/drm/drm_fb_helper.c          | 13 ++++++++++++-
 include/drm/drm_modeset_helper_vtables.h | 10 ++++++++--
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index f6a3350..849d029 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state *state,
 	if (funcs->atomic_best_encoder)
 		new_encoder = funcs->atomic_best_encoder(connector,
 							 connector_state);
-	else
+	else if (funcs->best_encoder)
 		new_encoder = funcs->best_encoder(connector);
+	else
+		new_encoder = drm_atomic_helper_best_encoder(connector);
 
 	if (!new_encoder) {
 		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 7c2eb75..d44389a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 		my_score++;
 
 	connector_funcs = connector->helper_private;
-	encoder = connector_funcs->best_encoder(connector);
+
+	/*
+	 * If the DRM device implements atomic hooks and ->best_encoder() is
+	 * NULL we fallback to the default drm_atomic_helper_best_encoder()
+	 * helper.
+	 */
+	if (fb_helper->dev->mode_config.funcs->atomic_commit &&
+	    !connector_funcs->best_encoder)
+		encoder = drm_atomic_helper_best_encoder(connector);
+	else
+		encoder = connector_funcs->best_encoder(connector);
+
 	if (!encoder)
 		goto out;
 
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index d4619dc..4e7a53b 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -736,6 +736,11 @@ struct drm_connector_helper_funcs {
 	 * inspect dynamic configuration state should instead use
 	 * @atomic_best_encoder.
 	 *
+	 * You can leave this function to NULL if the connector is only
+	 * attached to a single encoder and you are using the atomic helpers.
+	 * In this case, the core will call drm_atomic_helper_best_encoder()
+	 * for you.
+	 *
 	 * RETURNS:
 	 *
 	 * Encoder that should be used for the given connector and connector
@@ -752,8 +757,9 @@ struct drm_connector_helper_funcs {
 	 * need to select the best encoder depending upon the desired
 	 * configuration and can't select it statically.
 	 *
-	 * This function is used by drm_atomic_helper_check_modeset() and either
-	 * this or @best_encoder is required.
+	 * This function is used by drm_atomic_helper_check_modeset().
+	 * If it is not implemented, the core will fallback to @best_encoder
+	 * (or drm_atomic_helper_best_encoder() if @best_encoder is NULL).
 	 *
 	 * NOTE:
 	 *
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:02 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:02 +0200
Subject: [PATCH v2 07/20] drm: mediatek: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-8-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/mediatek/mtk_dsi.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
index 2d808e5..7343ffc 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -575,14 +575,6 @@ static int mtk_dsi_connector_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(dsi->panel);
 }
 
-static struct drm_encoder *mtk_dsi_connector_best_encoder(
-		struct drm_connector *connector)
-{
-	struct mtk_dsi *dsi = connector_to_dsi(connector);
-
-	return &dsi->encoder;
-}
-
 static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = {
 	.mode_fixup = mtk_dsi_encoder_mode_fixup,
 	.mode_set = mtk_dsi_encoder_mode_set,
@@ -603,7 +595,6 @@ static const struct drm_connector_funcs mtk_dsi_connector_funcs = {
 static const struct drm_connector_helper_funcs
 	mtk_dsi_connector_helper_funcs = {
 	.get_modes = mtk_dsi_connector_get_modes,
-	.best_encoder = mtk_dsi_connector_best_encoder,
 };
 
 static int mtk_drm_attach_bridge(struct drm_bridge *bridge,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:09 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:09 +0200
Subject: [PATCH v2 14/20] drm: vc4: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-15-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders and
the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/vc4/vc4_dpi.c  | 9 ---------
 drivers/gpu/drm/vc4/vc4_hdmi.c | 9 ---------
 2 files changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 9817dbf..dba1114 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -208,14 +208,6 @@ static int vc4_dpi_connector_get_modes(struct drm_connector *connector)
 	return 0;
 }
 
-static struct drm_encoder *
-vc4_dpi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct vc4_dpi_connector *dpi_connector =
-		to_vc4_dpi_connector(connector);
-	return dpi_connector->encoder;
-}
-
 static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = vc4_dpi_connector_detect,
@@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_dpi_connector_funcs = {
 
 static const struct drm_connector_helper_funcs vc4_dpi_connector_helper_funcs = {
 	.get_modes = vc4_dpi_connector_get_modes,
-	.best_encoder = vc4_dpi_connector_best_encoder,
 };
 
 static struct drm_connector *vc4_dpi_connector_init(struct drm_device *dev,
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index fd2644d..68df91c 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -208,14 +208,6 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
 	return ret;
 }
 
-static struct drm_encoder *
-vc4_hdmi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct vc4_hdmi_connector *hdmi_connector =
-		to_vc4_hdmi_connector(connector);
-	return hdmi_connector->encoder;
-}
-
 static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = vc4_hdmi_connector_detect,
@@ -228,7 +220,6 @@ static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 
 static const struct drm_connector_helper_funcs vc4_hdmi_connector_helper_funcs = {
 	.get_modes = vc4_hdmi_connector_get_modes,
-	.best_encoder = vc4_hdmi_connector_best_encoder,
 };
 
 static struct drm_connector *vc4_hdmi_connector_init(struct drm_device *dev,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:00 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:00 +0200
Subject: [PATCH v2 05/20] drm: fsl-dcu: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-6-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() and let the core call drm_atomic_helper_best_encoder()
for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 98c998d..0b0989e 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -102,14 +102,6 @@ static const struct drm_connector_funcs fsl_dcu_drm_connector_funcs = {
 	.reset = drm_atomic_helper_connector_reset,
 };
 
-static struct drm_encoder *
-fsl_dcu_drm_connector_best_encoder(struct drm_connector *connector)
-{
-	struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
-
-	return fsl_con->encoder;
-}
-
 static int fsl_dcu_drm_connector_get_modes(struct drm_connector *connector)
 {
 	struct fsl_dcu_drm_connector *fsl_connector;
@@ -136,7 +128,6 @@ static int fsl_dcu_drm_connector_mode_valid(struct drm_connector *connector,
 }
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
-	.best_encoder = fsl_dcu_drm_connector_best_encoder,
 	.get_modes = fsl_dcu_drm_connector_get_modes,
 	.mode_valid = fsl_dcu_drm_connector_mode_valid,
 };
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:07 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:07 +0200
Subject: [PATCH v2 12/20] drm: sun4i: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-13-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/sun4i/sun4i_rgb.c | 10 ----------
 drivers/gpu/drm/sun4i/sun4i_tv.c  |  9 ---------
 2 files changed, 19 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index ab64948..442cfe2 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -90,19 +90,9 @@ static int sun4i_rgb_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-sun4i_rgb_best_encoder(struct drm_connector *connector)
-{
-	struct sun4i_rgb *rgb =
-		drm_connector_to_sun4i_rgb(connector);
-
-	return &rgb->encoder;
-}
-
 static struct drm_connector_helper_funcs sun4i_rgb_con_helper_funcs = {
 	.get_modes	= sun4i_rgb_get_modes,
 	.mode_valid	= sun4i_rgb_mode_valid,
-	.best_encoder	= sun4i_rgb_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index bc047f9..b841478 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -526,18 +526,9 @@ static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-sun4i_tv_comp_best_encoder(struct drm_connector *connector)
-{
-	struct sun4i_tv *tv = drm_connector_to_sun4i_tv(connector);
-
-	return &tv->encoder;
-}
-
 static struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
 	.get_modes	= sun4i_tv_comp_get_modes,
 	.mode_valid	= sun4i_tv_comp_mode_valid,
-	.best_encoder	= sun4i_tv_comp_best_encoder,
 };
 
 static enum drm_connector_status
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:06 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:06 +0200
Subject: [PATCH v2 11/20] drm: sti: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-12-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/sti/sti_dvo.c  | 10 ----------
 drivers/gpu/drm/sti/sti_hda.c  | 10 ----------
 drivers/gpu/drm/sti/sti_hdmi.c | 10 ----------
 3 files changed, 30 deletions(-)

diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
index 25f7663..d5627d1 100644
--- a/drivers/gpu/drm/sti/sti_dvo.c
+++ b/drivers/gpu/drm/sti/sti_dvo.c
@@ -384,20 +384,10 @@ static int sti_dvo_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_dvo_best_encoder(struct drm_connector *connector)
-{
-	struct sti_dvo_connector *dvo_connector
-		= to_sti_dvo_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return dvo_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = {
 	.get_modes = sti_dvo_connector_get_modes,
 	.mode_valid = sti_dvo_connector_mode_valid,
-	.best_encoder = sti_dvo_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
index f7d3464..c4649f5 100644
--- a/drivers/gpu/drm/sti/sti_hda.c
+++ b/drivers/gpu/drm/sti/sti_hda.c
@@ -676,20 +676,10 @@ static int sti_hda_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector)
-{
-	struct sti_hda_connector *hda_connector
-		= to_sti_hda_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return hda_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = {
 	.get_modes = sti_hda_connector_get_modes,
 	.mode_valid = sti_hda_connector_mode_valid,
-	.best_encoder = sti_hda_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
index 6ef0715..dc9ab6e 100644
--- a/drivers/gpu/drm/sti/sti_hdmi.c
+++ b/drivers/gpu/drm/sti/sti_hdmi.c
@@ -897,20 +897,10 @@ static int sti_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector)
-{
-	struct sti_hdmi_connector *hdmi_connector
-		= to_sti_hdmi_connector(connector);
-
-	/* Best encoder is the one associated during connector creation */
-	return hdmi_connector->encoder;
-}
-
 static const
 struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = {
 	.get_modes = sti_hdmi_connector_get_modes,
 	.mode_valid = sti_hdmi_connector_mode_valid,
-	.best_encoder = sti_hdmi_best_encoder,
 };
 
 /* get detection status of display device */
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:10 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:10 +0200
Subject: [PATCH v2 15/20] drm: virtgpu: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-16-git-send-email-boris.brezillon@free-electrons.com>

The virtgpu output exposes a 1:1 relationship between connectors and
encoders and the driver is relying on the atomic helpers: we can drop
the custom ->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/virtio/virtgpu_display.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index d4305da..4d41dcb 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -341,15 +341,6 @@ static int virtio_gpu_conn_mode_valid(struct drm_connector *connector,
 	return MODE_BAD;
 }
 
-static struct drm_encoder*
-virtio_gpu_best_encoder(struct drm_connector *connector)
-{
-	struct virtio_gpu_output *virtio_gpu_output =
-		drm_connector_to_virtio_gpu_output(connector);
-
-	return &virtio_gpu_output->enc;
-}
-
 static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
 	.mode_set   = virtio_gpu_enc_mode_set,
 	.enable     = virtio_gpu_enc_enable,
@@ -359,7 +350,6 @@ static const struct drm_encoder_helper_funcs virtio_gpu_enc_helper_funcs = {
 static const struct drm_connector_helper_funcs virtio_gpu_conn_helper_funcs = {
 	.get_modes    = virtio_gpu_conn_get_modes,
 	.mode_valid   = virtio_gpu_conn_mode_valid,
-	.best_encoder = virtio_gpu_best_encoder,
 };
 
 static enum drm_connector_status virtio_gpu_conn_detect(
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:05 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:05 +0200
Subject: [PATCH v2 10/20] drm: rockchip: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-11-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations  and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
Acked-by: Mark Yao <mark.yao at rock-chips.com>
---
 drivers/gpu/drm/rockchip/dw-mipi-dsi.c | 9 ---------
 drivers/gpu/drm/rockchip/inno_hdmi.c   | 9 ---------
 2 files changed, 18 deletions(-)

diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
index dedc65b..ca22e5e 100644
--- a/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
+++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi.c
@@ -964,18 +964,9 @@ static enum drm_mode_status dw_mipi_dsi_mode_valid(
 	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
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c
index f8b4feb..006260d 100644
--- a/drivers/gpu/drm/rockchip/inno_hdmi.c
+++ b/drivers/gpu/drm/rockchip/inno_hdmi.c
@@ -579,14 +579,6 @@ inno_hdmi_connector_mode_valid(struct drm_connector *connector,
 	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)
@@ -613,7 +605,6 @@ static struct drm_connector_funcs inno_hdmi_connector_funcs = {
 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)
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:03 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:03 +0200
Subject: [PATCH v2 08/20] drm: msm: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-9-git-send-email-boris.brezillon@free-electrons.com>

For all outputs except DSI we have a 1:1 relationship between connectors
and encoders and the driver is relying on the atomic helpers: we can
drop the custom ->best_encoder() and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
 drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
 drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
 3 files changed, 27 deletions(-)

diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c
index 72360cd..5960628 100644
--- a/drivers/gpu/drm/msm/edp/edp_connector.c
+++ b/drivers/gpu/drm/msm/edp/edp_connector.c
@@ -91,15 +91,6 @@ static int edp_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-edp_connector_best_encoder(struct drm_connector *connector)
-{
-	struct edp_connector *edp_connector = to_edp_connector(connector);
-
-	DBG("");
-	return edp_connector->edp->encoder;
-}
-
 static const struct drm_connector_funcs edp_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = edp_connector_detect,
@@ -113,7 +104,6 @@ static const struct drm_connector_funcs edp_connector_funcs = {
 static const struct drm_connector_helper_funcs edp_connector_helper_funcs = {
 	.get_modes = edp_connector_get_modes,
 	.mode_valid = edp_connector_mode_valid,
-	.best_encoder = edp_connector_best_encoder,
 };
 
 /* initialize connector */
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
index b15d726..a2515b4 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
@@ -406,13 +406,6 @@ static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return 0;
 }
 
-static struct drm_encoder *
-msm_hdmi_connector_best_encoder(struct drm_connector *connector)
-{
-	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
-	return hdmi_connector->hdmi->encoder;
-}
-
 static const struct drm_connector_funcs hdmi_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = hdmi_connector_detect,
@@ -426,7 +419,6 @@ static const struct drm_connector_funcs hdmi_connector_funcs = {
 static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
 	.get_modes = msm_hdmi_connector_get_modes,
 	.mode_valid = msm_hdmi_connector_mode_valid,
-	.best_encoder = msm_hdmi_connector_best_encoder,
 };
 
 /* initialize connector */
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
index 2648cd7..353429b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
@@ -90,14 +90,6 @@ static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static struct drm_encoder *
-mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
-{
-	struct mdp4_lvds_connector *mdp4_lvds_connector =
-			to_mdp4_lvds_connector(connector);
-	return mdp4_lvds_connector->encoder;
-}
-
 static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.detect = mdp4_lvds_connector_detect,
@@ -111,7 +103,6 @@ static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
 static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
 	.get_modes = mdp4_lvds_connector_get_modes,
 	.mode_valid = mdp4_lvds_connector_mode_valid,
-	.best_encoder = mdp4_lvds_connector_best_encoder,
 };
 
 /* initialize connector */
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:04 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:04 +0200
Subject: [PATCH v2 09/20] drm: rcar-du: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-10-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders,
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementations and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
---
 drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 12 ------------
 drivers/gpu/drm/rcar-du/rcar_du_encoder.h |  3 ---
 drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c |  1 -
 drivers/gpu/drm/rcar-du/rcar_du_vgacon.c  |  3 ---
 5 files changed, 20 deletions(-)

diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
index 4e939e4..55149e9 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c
@@ -27,18 +27,6 @@
 #include "rcar_du_vgacon.h"
 
 /* -----------------------------------------------------------------------------
- * Common connector functions
- */
-
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector)
-{
-	struct rcar_du_connector *rcon = to_rcar_connector(connector);
-
-	return rcar_encoder_to_drm_encoder(rcon->encoder);
-}
-
-/* -----------------------------------------------------------------------------
  * Encoder
  */
 
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
index 719b6f2a..a8669c3 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
+++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h
@@ -49,9 +49,6 @@ struct rcar_du_connector {
 #define to_rcar_connector(c) \
 	container_of(c, struct rcar_du_connector, connector)
 
-struct drm_encoder *
-rcar_du_connector_best_encoder(struct drm_connector *connector);
-
 int rcar_du_encoder_init(struct rcar_du_device *rcdu,
 			 enum rcar_du_encoder_type type,
 			 enum rcar_du_output output,
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
index 6c92714..612b4d5 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c
@@ -52,7 +52,6 @@ static int rcar_du_hdmi_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_hdmi_connector_get_modes,
 	.mode_valid = rcar_du_hdmi_connector_mode_valid,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
index e905f5d..6afd0af 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c
@@ -59,7 +59,6 @@ static int rcar_du_lvds_connector_get_modes(struct drm_connector *connector)
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_lvds_connector_get_modes,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
index 9d7e5c9..8d6125c 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_vgacon.c
@@ -28,7 +28,6 @@ static int rcar_du_vga_connector_get_modes(struct drm_connector *connector)
 
 static const struct drm_connector_helper_funcs connector_helper_funcs = {
 	.get_modes = rcar_du_vga_connector_get_modes,
-	.best_encoder = rcar_du_connector_best_encoder,
 };
 
 static enum drm_connector_status
@@ -79,7 +78,5 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	rcon->encoder = renc;
-
 	return 0;
 }
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:11 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:11 +0200
Subject: [PATCH v2 16/20] drm: omap: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-17-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders and the
driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
---
 drivers/gpu/drm/omapdrm/omap_connector.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/gpu/drm/omapdrm/omap_connector.c b/drivers/gpu/drm/omapdrm/omap_connector.c
index ce2d67b..137fe69 100644
--- a/drivers/gpu/drm/omapdrm/omap_connector.c
+++ b/drivers/gpu/drm/omapdrm/omap_connector.c
@@ -32,7 +32,6 @@
 struct omap_connector {
 	struct drm_connector base;
 	struct omap_dss_device *dssdev;
-	struct drm_encoder *encoder;
 	bool hdmi_mode;
 };
 
@@ -256,13 +255,6 @@ static int omap_connector_mode_valid(struct drm_connector *connector,
 	return ret;
 }
 
-struct drm_encoder *omap_connector_attached_encoder(
-		struct drm_connector *connector)
-{
-	struct omap_connector *omap_connector = to_omap_connector(connector);
-	return omap_connector->encoder;
-}
-
 static const struct drm_connector_funcs omap_connector_funcs = {
 	.dpms = drm_atomic_helper_connector_dpms,
 	.reset = drm_atomic_helper_connector_reset,
@@ -276,7 +268,6 @@ static const struct drm_connector_funcs omap_connector_funcs = {
 static const struct drm_connector_helper_funcs omap_connector_helper_funcs = {
 	.get_modes = omap_connector_get_modes,
 	.mode_valid = omap_connector_mode_valid,
-	.best_encoder = omap_connector_attached_encoder,
 };
 
 /* initialize connector */
@@ -296,7 +287,6 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
 		goto fail;
 
 	omap_connector->dssdev = dssdev;
-	omap_connector->encoder = encoder;
 
 	connector = &omap_connector->base;
 
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:15 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:15 +0200
Subject: [PATCH v2 20/20] drm/bridge: dw-hdmi: Use
 drm_atomic_helper_best_encoder()
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-21-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, which means
we can rely on the drm_atomic_helper_best_encoder() behavior.

We still have to explicitly assign ->best_encoder() to
drm_atomic_helper_best_encoder(), because the automated fallback to
drm_atomic_helper_best_encoder() when ->best_encoder() is NULL is only
available when the DRM device is using the atomic helpers, and this bridge
is compatible with non-atomic and atomic devices.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/dw-hdmi.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
index c9d9412..70b1f7d 100644
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
@@ -1476,15 +1476,6 @@ dw_hdmi_connector_mode_valid(struct drm_connector *connector,
 	return mode_status;
 }
 
-static struct drm_encoder *dw_hdmi_connector_best_encoder(struct drm_connector
-							   *connector)
-{
-	struct dw_hdmi *hdmi = container_of(connector, struct dw_hdmi,
-					     connector);
-
-	return hdmi->encoder;
-}
-
 static void dw_hdmi_connector_destroy(struct drm_connector *connector)
 {
 	drm_connector_unregister(connector);
@@ -1525,7 +1516,7 @@ static const struct drm_connector_funcs dw_hdmi_atomic_connector_funcs = {
 static const struct drm_connector_helper_funcs dw_hdmi_connector_helper_funcs = {
 	.get_modes = dw_hdmi_connector_get_modes,
 	.mode_valid = dw_hdmi_connector_mode_valid,
-	.best_encoder = dw_hdmi_connector_best_encoder,
+	.best_encoder = drm_atomic_helper_best_encoder,
 };
 
 static const struct drm_bridge_funcs dw_hdmi_bridge_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:08 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:08 +0200
Subject: [PATCH v2 13/20] drm: tegra: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-14-git-send-email-boris.brezillon@free-electrons.com>

All outputs have a 1:1 relationship between connectors and encoders
and the driver is relying on the atomic helpers: we can drop the custom
->best_encoder() implementation and let the core call
drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/tegra/drm.h    | 2 --
 drivers/gpu/drm/tegra/dsi.c    | 1 -
 drivers/gpu/drm/tegra/hdmi.c   | 1 -
 drivers/gpu/drm/tegra/output.c | 8 --------
 drivers/gpu/drm/tegra/rgb.c    | 1 -
 drivers/gpu/drm/tegra/sor.c    | 1 -
 6 files changed, 14 deletions(-)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index f52d6cb2..0ddcce1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -239,8 +239,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output);
 void tegra_output_exit(struct tegra_output *output);
 
 int tegra_output_connector_get_modes(struct drm_connector *connector);
-struct drm_encoder *
-tegra_output_connector_best_encoder(struct drm_connector *connector);
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force);
 void tegra_output_connector_destroy(struct drm_connector *connector);
diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c
index d1239eb..099cccb 100644
--- a/drivers/gpu/drm/tegra/dsi.c
+++ b/drivers/gpu/drm/tegra/dsi.c
@@ -794,7 +794,6 @@ tegra_dsi_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_dsi_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index b7ef492..2fdb879 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -806,7 +806,6 @@ static const struct drm_connector_helper_funcs
 tegra_hdmi_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_hdmi_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 46664b6..1480f6a 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -42,14 +42,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 	return err;
 }
 
-struct drm_encoder *
-tegra_output_connector_best_encoder(struct drm_connector *connector)
-{
-	struct tegra_output *output = connector_to_output(connector);
-
-	return &output->encoder;
-}
-
 enum drm_connector_status
 tegra_output_connector_detect(struct drm_connector *connector, bool force)
 {
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index e246334..a131b44 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -112,7 +112,6 @@ tegra_rgb_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = {
 	.get_modes = tegra_output_connector_get_modes,
 	.mode_valid = tegra_rgb_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = {
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index 757c6e8..34958d7 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -1087,7 +1087,6 @@ tegra_sor_connector_mode_valid(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = {
 	.get_modes = tegra_sor_connector_get_modes,
 	.mode_valid = tegra_sor_connector_mode_valid,
-	.best_encoder = tegra_output_connector_best_encoder,
 };
 
 static const struct drm_encoder_funcs tegra_sor_encoder_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:01 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:01 +0200
Subject: [PATCH v2 06/20] drm: i915: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-7-git-send-email-boris.brezillon@free-electrons.com>

For all outputs except dp_mst, we have a 1:1 relationship between
connectors and encoders and the driver is relying on the atomic helpers:
we can drop the custom ->best_encoder() implementation and let the core
call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/i915/intel_crt.c     | 1 -
 drivers/gpu/drm/i915/intel_display.c | 8 --------
 drivers/gpu/drm/i915/intel_dp.c      | 1 -
 drivers/gpu/drm/i915/intel_drv.h     | 1 -
 drivers/gpu/drm/i915/intel_dsi.c     | 1 -
 drivers/gpu/drm/i915/intel_dvo.c     | 1 -
 drivers/gpu/drm/i915/intel_hdmi.c    | 1 -
 drivers/gpu/drm/i915/intel_lvds.c    | 1 -
 drivers/gpu/drm/i915/intel_sdvo.c    | 1 -
 drivers/gpu/drm/i915/intel_tv.c      | 1 -
 10 files changed, 17 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 3fbb6fc..bd0cd68 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -753,7 +753,6 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
 	.mode_valid = intel_crt_mode_valid,
 	.get_modes = intel_crt_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_crt_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2113f40..77026ce 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -16113,14 +16113,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
 	intel_teardown_gmbus(dev);
 }
 
-/*
- * Return which encoder is currently attached for connector.
- */
-struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
-{
-	return &intel_attached_encoder(connector)->base;
-}
-
 void intel_connector_attach_encoder(struct intel_connector *connector,
 				    struct intel_encoder *encoder)
 {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index f192f58..21b2833 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4935,7 +4935,6 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
 	.get_modes = intel_dp_get_modes,
 	.mode_valid = intel_dp_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_dp_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a28b4aa..79a4d6b 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1128,7 +1128,6 @@ struct intel_connector *intel_connector_alloc(void);
 bool intel_connector_get_hw_state(struct intel_connector *connector);
 void intel_connector_attach_encoder(struct intel_connector *connector,
 				    struct intel_encoder *encoder);
-struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
 struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
 					     struct drm_crtc *crtc);
 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 366ad6c..ec51952 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -1378,7 +1378,6 @@ static const struct drm_encoder_funcs intel_dsi_funcs = {
 static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = {
 	.get_modes = intel_dsi_get_modes,
 	.mode_valid = intel_dsi_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_connector_funcs intel_dsi_connector_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 286baec..34b7e3f 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -351,7 +351,6 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
 	.mode_valid = intel_dvo_mode_valid,
 	.get_modes = intel_dvo_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 2c3bd9c..aef4bc8 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2114,7 +2114,6 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
 	.get_modes = intel_hdmi_get_modes,
 	.mode_valid = intel_hdmi_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index bc53c0d..d4faa5a 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -547,7 +547,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
 static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
 	.get_modes = intel_lvds_get_modes,
 	.mode_valid = intel_lvds_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_connector_funcs intel_lvds_connector_funcs = {
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 2128fae..5cd48ff 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -2191,7 +2191,6 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
 	.get_modes = intel_sdvo_get_modes,
 	.mode_valid = intel_sdvo_mode_valid,
-	.best_encoder = intel_best_encoder,
 };
 
 static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 223129d..47fe241 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1512,7 +1512,6 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = {
 static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
 	.mode_valid = intel_tv_mode_valid,
 	.get_modes = intel_tv_get_modes,
-	.best_encoder = intel_best_encoder,
 };
 
 static const struct drm_encoder_funcs intel_tv_enc_funcs = {
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:12 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:12 +0200
Subject: [PATCH v2 17/20] drm/bridge: anx78xx: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-18-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/analogix-anx78xx.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c
index d087b05..f9f03bc 100644
--- a/drivers/gpu/drm/bridge/analogix-anx78xx.c
+++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c
@@ -986,16 +986,8 @@ unlock:
 	return num_modes;
 }
 
-static struct drm_encoder *anx78xx_best_encoder(struct drm_connector *connector)
-{
-	struct anx78xx *anx78xx = connector_to_anx78xx(connector);
-
-	return anx78xx->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs anx78xx_connector_helper_funcs = {
 	.get_modes = anx78xx_get_modes,
-	.best_encoder = anx78xx_best_encoder,
 };
 
 static enum drm_connector_status anx78xx_detect(struct drm_connector *connector,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:14 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:14 +0200
Subject: [PATCH v2 19/20] drm/bridge: ps8622: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-20-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/parade-ps8622.c | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c
index be881e9..5cd8dd7 100644
--- a/drivers/gpu/drm/bridge/parade-ps8622.c
+++ b/drivers/gpu/drm/bridge/parade-ps8622.c
@@ -474,18 +474,8 @@ static int ps8622_get_modes(struct drm_connector *connector)
 	return drm_panel_get_modes(ps8622->panel);
 }
 
-static struct drm_encoder *ps8622_best_encoder(struct drm_connector *connector)
-{
-	struct ps8622_bridge *ps8622;
-
-	ps8622 = connector_to_ps8622(connector);
-
-	return ps8622->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs ps8622_connector_helper_funcs = {
 	.get_modes = ps8622_get_modes,
-	.best_encoder = ps8622_best_encoder,
 };
 
 static enum drm_connector_status ps8622_detect(struct drm_connector *connector,
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun  7 04:48:13 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue,  7 Jun 2016 13:48:13 +0200
Subject: [PATCH v2 18/20] drm/bridge: ptn3460: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <1465300095-16971-19-git-send-email-boris.brezillon@free-electrons.com>

We have a 1:1 relationship between connectors and encoders, and the driver
is relying on the atomic helpers: we can drop the custom ->best_encoder(),
and let the core call drm_atomic_helper_best_encoder() for us.

Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
---
 drivers/gpu/drm/bridge/nxp-ptn3460.c | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c
index 7ecd59f..93f3dac 100644
--- a/drivers/gpu/drm/bridge/nxp-ptn3460.c
+++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c
@@ -235,16 +235,8 @@ out:
 	return num_modes;
 }
 
-static struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector)
-{
-	struct ptn3460_bridge *ptn_bridge = connector_to_ptn3460(connector);
-
-	return ptn_bridge->bridge.encoder;
-}
-
 static const struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = {
 	.get_modes = ptn3460_get_modes,
-	.best_encoder = ptn3460_best_encoder,
 };
 
 static enum drm_connector_status ptn3460_detect(struct drm_connector *connector,
-- 
2.7.4



From liviu.dudau at arm.com  Tue Jun  7 06:39:15 2016
From: liviu.dudau at arm.com (Liviu Dudau)
Date: Tue, 7 Jun 2016 14:39:15 +0100
Subject: [PATCH v2 2/3] drm: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
Message-ID: <20160607133915.GB1165@e106497-lin.cambridge.arm.com>

On Fri, Jun 03, 2016 at 03:21:25PM +0100, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
> 
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
>  drivers/gpu/drm/arm/hdlcd_drv.c             |  9 +++------
>  drivers/gpu/drm/armada/armada_drv.c         |  8 ++------
>  drivers/gpu/drm/drm_of.c                    |  6 ++++--
>  drivers/gpu/drm/etnaviv/etnaviv_drv.c       | 11 ++---------
>  drivers/gpu/drm/msm/msm_drv.c               |  8 ++------
>  drivers/gpu/drm/msm/msm_drv.h               |  1 +
>  drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 +++---------
>  drivers/gpu/drm/sti/sti_drv.c               |  9 ++-------
>  drivers/gpu/drm/tilcdc/tilcdc_external.c    |  8 ++------
>  9 files changed, 21 insertions(+), 51 deletions(-)
> 
> diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c
> index b987c63ba8d6..f00845c25795 100644
> --- a/drivers/gpu/drm/arm/hdlcd_drv.c
> +++ b/drivers/gpu/drm/arm/hdlcd_drv.c
> @@ -14,6 +14,7 @@
>  #include <linux/clk.h>
>  #include <linux/component.h>
>  #include <linux/list.h>
> +#include <linux/of_component.h>
>  #include <linux/of_graph.h>
>  #include <linux/of_reserved_mem.h>
>  #include <linux/pm_runtime.h>
> @@ -443,11 +444,6 @@ static const struct component_master_ops hdlcd_master_ops = {
>  	.unbind		= hdlcd_drm_unbind,
>  };
>  
> -static int compare_dev(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>  static int hdlcd_probe(struct platform_device *pdev)
>  {
>  	struct device_node *port, *ep;
> @@ -474,7 +470,8 @@ static int hdlcd_probe(struct platform_device *pdev)
>  		return -EAGAIN;
>  	}
>  
> -	component_match_add(&pdev->dev, &match, compare_dev, port);
> +	component_match_add_of(&pdev->dev, &match, port);
> +	of_node_put(port);
>  
>  	return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
>  					       match);

For the HDLCD part of v2:

Acked-by: Liviu Dudau <Liviu.Dudau at arm.com>


> diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c
> index 439824a61aa5..f2716d20c257 100644
> --- a/drivers/gpu/drm/armada/armada_drv.c
> +++ b/drivers/gpu/drm/armada/armada_drv.c
> @@ -9,6 +9,7 @@
>  #include <linux/component.h>
>  #include <linux/module.h>
>  #include <linux/of_graph.h>
> +#include <linux/of_component.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc_helper.h>
>  #include <drm/drm_of.h>
> @@ -227,11 +228,6 @@ static void armada_drm_unbind(struct device *dev)
>  	drm_put_dev(dev_get_drvdata(dev));
>  }
>  
> -static int compare_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>  static int compare_dev_name(struct device *dev, void *data)
>  {
>  	const char *name = data;
> @@ -255,7 +251,7 @@ static void armada_add_endpoints(struct device *dev,
>  			continue;
>  		}
>  
> -		component_match_add(dev, match, compare_of, remote);
> +		component_match_add_of(dev, match, remote);
>  		of_node_put(remote);
>  	}
>  }
> diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
> index bc98bb94264d..d13fc633aeea 100644
> --- a/drivers/gpu/drm/drm_of.c
> +++ b/drivers/gpu/drm/drm_of.c
> @@ -1,6 +1,7 @@
>  #include <linux/component.h>
>  #include <linux/export.h>
>  #include <linux/list.h>
> +#include <linux/of_component.h>
>  #include <linux/of_graph.h>
>  #include <drm/drmP.h>
>  #include <drm/drm_crtc.h>
> @@ -101,7 +102,7 @@ int drm_of_component_probe(struct device *dev,
>  			continue;
>  		}
>  
> -		component_match_add(dev, &match, compare_of, port);
> +		component_match_add_of(dev, &match, compare_of, port);
>  		of_node_put(port);
>  	}
>  
> @@ -140,7 +141,8 @@ int drm_of_component_probe(struct device *dev,
>  				continue;
>  			}
>  
> -			component_match_add(dev, &match, compare_of, remote);
> +			component_match_add_of_compare(dev, &match, compare_of,
> +						       remote);
>  			of_node_put(remote);
>  		}
>  		of_node_put(port);
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> index 3d4f56df8359..a0e0764a6269 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c
> @@ -15,6 +15,7 @@
>   */
>  
>  #include <linux/component.h>
> +#include <linux/of_component.h>
>  #include <linux/of_platform.h>
>  
>  #include "etnaviv_drv.h"
> @@ -606,13 +607,6 @@ static const struct component_master_ops etnaviv_master_ops = {
>  	.unbind = etnaviv_unbind,
>  };
>  
> -static int compare_of(struct device *dev, void *data)
> -{
> -	struct device_node *np = data;
> -
> -	return dev->of_node == np;
> -}
> -
>  static int compare_str(struct device *dev, void *data)
>  {
>  	return !strcmp(dev_name(dev), data);
> @@ -635,8 +629,7 @@ static int etnaviv_pdev_probe(struct platform_device *pdev)
>  			if (!core_node)
>  				break;
>  
> -			component_match_add(&pdev->dev, &match, compare_of,
> -					    core_node);
> +			component_match_add_of(&pdev->dev, &match, core_node);
>  			of_node_put(core_node);
>  		}
>  	} else if (dev->platform_data) {
> diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
> index 9c654092ef78..5293aa775c5a 100644
> --- a/drivers/gpu/drm/msm/msm_drv.c
> +++ b/drivers/gpu/drm/msm/msm_drv.c
> @@ -800,11 +800,6 @@ static const struct dev_pm_ops msm_pm_ops = {
>   * NOTE: duplication of the same code as exynos or imx (or probably any other).
>   * so probably some room for some helpers
>   */
> -static int compare_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>  static int add_components(struct device *dev, struct component_match **matchptr,
>  		const char *name)
>  {
> @@ -818,7 +813,8 @@ static int add_components(struct device *dev, struct component_match **matchptr,
>  		if (!node)
>  			break;
>  
> -		component_match_add(dev, matchptr, compare_of, node);
> +		component_match_add_of(dev, matchptr, node);
> +		of_node_put(node);
>  	}
>  
>  	return 0;
> diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
> index 5b2963f32291..66dafecaf115 100644
> --- a/drivers/gpu/drm/msm/msm_drv.h
> +++ b/drivers/gpu/drm/msm/msm_drv.h
> @@ -30,6 +30,7 @@
>  #include <linux/list.h>
>  #include <linux/iommu.h>
>  #include <linux/types.h>
> +#include <linux/of_component.h>
>  #include <linux/of_graph.h>
>  #include <linux/of_device.h>
>  #include <asm/sizes.h>
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index a409d1f703cb..84c50f6770c3 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -22,6 +22,7 @@
>  #include <linux/dma-mapping.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/module.h>
> +#include <linux/of_component.h>
>  #include <linux/of_graph.h>
>  #include <linux/component.h>
>  
> @@ -414,13 +415,6 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = {
>  				rockchip_drm_sys_resume)
>  };
>  
> -static int compare_of(struct device *dev, void *data)
> -{
> -	struct device_node *np = data;
> -
> -	return dev->of_node == np;
> -}
> -
>  static void rockchip_add_endpoints(struct device *dev,
>  				   struct component_match **match,
>  				   struct device_node *port)
> @@ -439,7 +433,7 @@ static void rockchip_add_endpoints(struct device *dev,
>  			continue;
>  		}
>  
> -		component_match_add(dev, match, compare_of, remote);
> +		component_match_add_of(dev, match, remote);
>  		of_node_put(remote);
>  	}
>  }
> @@ -518,7 +512,7 @@ static int rockchip_drm_platform_probe(struct platform_device *pdev)
>  			is_support_iommu = false;
>  		}
>  
> -		component_match_add(dev, &match, compare_of, port->parent);
> +		component_match_add_of(dev, &match, port->parent);
>  		of_node_put(port);
>  	}
>  
> diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
> index 872495e72294..aeb2787c9bd4 100644
> --- a/drivers/gpu/drm/sti/sti_drv.c
> +++ b/drivers/gpu/drm/sti/sti_drv.c
> @@ -10,6 +10,7 @@
>  #include <linux/debugfs.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> +#include <linux/of_component.h>
>  #include <linux/of_platform.h>
>  
>  #include <drm/drm_atomic.h>
> @@ -341,11 +342,6 @@ static struct drm_driver sti_driver = {
>  	.minor = DRIVER_MINOR,
>  };
>  
> -static int compare_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>  static int sti_bind(struct device *dev)
>  {
>  	return drm_platform_init(&sti_driver, to_platform_device(dev));
> @@ -375,8 +371,7 @@ static int sti_platform_probe(struct platform_device *pdev)
>  	child_np = of_get_next_available_child(node, NULL);
>  
>  	while (child_np) {
> -		component_match_add(dev, &match, compare_of, child_np);
> -		of_node_put(child_np);
> +		component_match_add_of(dev, &match, child_np);
>  		child_np = of_get_next_available_child(node, child_np);
>  	}
>  
> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> index 03acb4f99982..1b3fd9fa4bef 100644
> --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c
> +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c
> @@ -9,6 +9,7 @@
>   */
>  
>  #include <linux/component.h>
> +#include <linux/of_component.h>
>  #include <linux/of_graph.h>
>  
>  #include "tilcdc_drv.h"
> @@ -130,11 +131,6 @@ void tilcdc_remove_external_encoders(struct drm_device *dev)
>  						 priv->connector_funcs[i]);
>  }
>  
> -static int dev_match_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>  int tilcdc_get_external_components(struct device *dev,
>  				   struct component_match **match)
>  {
> @@ -152,7 +148,7 @@ int tilcdc_get_external_components(struct device *dev,
>  
>  		dev_dbg(dev, "Subdevice node '%s' found\n", node->name);
>  		if (match)
> -			component_match_add(dev, match, dev_match_of, node);
> +			component_match_add_of(dev, match, node);
>  		of_node_put(node);
>  		count++;
>  	}
> -- 
> 2.1.0
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ?\_(?)_/?


From mchehab at osg.samsung.com  Tue Jun  7 07:22:35 2016
From: mchehab at osg.samsung.com (Mauro Carvalho Chehab)
Date: Tue, 7 Jun 2016 11:22:35 -0300
Subject: [PATCH v3 0/9] Add MT8173 Video Decoder Driver
In-Reply-To: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
References: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
Message-ID: <20160607112235.475c2e4c@recife.lan>

Em Mon, 30 May 2016 20:29:14 +0800
Tiffany Lin <tiffany.lin at mediatek.com> escreveu:

> ==============
>  Introduction
> ==============
> 
> The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
> Mediatek Video Codec is able to handle video decoding of in a range of formats.
> 
> This patch series add Mediatek block format V4L2_PIX_FMT_MT21, the decoder driver will decoded bitstream to
> V4L2_PIX_FMT_MT21 format.
> 
> This patch series rely on MTK VPU driver in patch series "Add MT8173 Video Encoder Driver and VPU Driver"[1]
> and patch "CHROMIUM: v4l: Add V4L2_PIX_FMT_VP9 definition"[2] for VP9 support.
> Mediatek Video Decoder driver rely on VPU driver to load, communicate with VPU.
> 
> Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI both have been merged in v4.6-rc1.
> 
> [1]https://patchwork.linuxtv.org/patch/33734/
> [2]https://chromium-review.googlesource.com/#/c/245241/

Hmm... I'm not seeing the firmware for this driver at the
linux-firmware tree:
	https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/log/

Nor I'm seeing any pull request for them. Did you send it?
I'll only merge the driver upstream after seeing such pull request.

Regards,
Mauro



From vincent.abriou at st.com  Tue Jun  7 07:26:13 2016
From: vincent.abriou at st.com (Vincent ABRIOU)
Date: Tue, 7 Jun 2016 16:26:13 +0200
Subject: [PATCH v2 2/3] drm: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyX-0005OP-2s@rmk-PC.armlinux.org.uk>
Message-ID: <5756D985.80107@st.com>



On 06/03/2016 04:21 PM, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
>
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
>   drivers/gpu/drm/arm/hdlcd_drv.c             |  9 +++------
>   drivers/gpu/drm/armada/armada_drv.c         |  8 ++------
>   drivers/gpu/drm/drm_of.c                    |  6 ++++--
>   drivers/gpu/drm/etnaviv/etnaviv_drv.c       | 11 ++---------
>   drivers/gpu/drm/msm/msm_drv.c               |  8 ++------
>   drivers/gpu/drm/msm/msm_drv.h               |  1 +
>   drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 12 +++---------
>   drivers/gpu/drm/sti/sti_drv.c               |  9 ++-------
>   drivers/gpu/drm/tilcdc/tilcdc_external.c    |  8 ++------
>   9 files changed, 21 insertions(+), 51 deletions(-)
>
>
> diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c
> index 872495e72294..aeb2787c9bd4 100644
> --- a/drivers/gpu/drm/sti/sti_drv.c
> +++ b/drivers/gpu/drm/sti/sti_drv.c
> @@ -10,6 +10,7 @@
>   #include <linux/debugfs.h>
>   #include <linux/kernel.h>
>   #include <linux/module.h>
> +#include <linux/of_component.h>
>   #include <linux/of_platform.h>
>
>   #include <drm/drm_atomic.h>
> @@ -341,11 +342,6 @@ static struct drm_driver sti_driver = {
>   	.minor = DRIVER_MINOR,
>   };
>
> -static int compare_of(struct device *dev, void *data)
> -{
> -	return dev->of_node == data;
> -}
> -
>   static int sti_bind(struct device *dev)
>   {
>   	return drm_platform_init(&sti_driver, to_platform_device(dev));
> @@ -375,8 +371,7 @@ static int sti_platform_probe(struct platform_device *pdev)
>   	child_np = of_get_next_available_child(node, NULL);
>
>   	while (child_np) {
> -		component_match_add(dev, &match, compare_of, child_np);
> -		of_node_put(child_np);
> +		component_match_add_of(dev, &match, child_np);
>   		child_np = of_get_next_available_child(node, child_np);
>   	}
>

Hi Russel,

Ok for the sti driver.

Acked-by: Vincent Abriou <vincent.abriou at st.com>

From daniel at ffwll.ch  Tue Jun  7 07:39:34 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 7 Jun 2016 16:39:34 +0200
Subject: [PATCH v2 01/20] drm/atomic: Fix remaining places where
 !funcs->best_encoder is valid
In-Reply-To: <1465300095-16971-2-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-2-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160607143934.GA3363@phenom.ffwll.local>

On Tue, Jun 07, 2016 at 01:47:56PM +0200, Boris Brezillon wrote:
> Adapt drm_pick_crtcs() and update_connector_routing() to fallback to
> drm_atomic_helper_best_encoder() if funcs->best_encoder() is NULL so
> that DRM drivers can leave this hook unassigned if they know they want
> to use drm_atomic_helper_best_encoder().
> 
> Update the vtables documentation accordingly.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>

Applied to drm-misc, thanks. I think I'll wait with the driver patches
until next week or so.
-Daniel

> ---
>  drivers/gpu/drm/drm_atomic_helper.c      |  4 +++-
>  drivers/gpu/drm/drm_fb_helper.c          | 13 ++++++++++++-
>  include/drm/drm_modeset_helper_vtables.h | 10 ++++++++--
>  3 files changed, 23 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
> index f6a3350..849d029 100644
> --- a/drivers/gpu/drm/drm_atomic_helper.c
> +++ b/drivers/gpu/drm/drm_atomic_helper.c
> @@ -300,8 +300,10 @@ update_connector_routing(struct drm_atomic_state *state,
>  	if (funcs->atomic_best_encoder)
>  		new_encoder = funcs->atomic_best_encoder(connector,
>  							 connector_state);
> -	else
> +	else if (funcs->best_encoder)
>  		new_encoder = funcs->best_encoder(connector);
> +	else
> +		new_encoder = drm_atomic_helper_best_encoder(connector);
>  
>  	if (!new_encoder) {
>  		DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 7c2eb75..d44389a 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -2000,7 +2000,18 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
>  		my_score++;
>  
>  	connector_funcs = connector->helper_private;
> -	encoder = connector_funcs->best_encoder(connector);
> +
> +	/*
> +	 * If the DRM device implements atomic hooks and ->best_encoder() is
> +	 * NULL we fallback to the default drm_atomic_helper_best_encoder()
> +	 * helper.
> +	 */
> +	if (fb_helper->dev->mode_config.funcs->atomic_commit &&
> +	    !connector_funcs->best_encoder)
> +		encoder = drm_atomic_helper_best_encoder(connector);
> +	else
> +		encoder = connector_funcs->best_encoder(connector);
> +
>  	if (!encoder)
>  		goto out;
>  
> diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
> index d4619dc..4e7a53b 100644
> --- a/include/drm/drm_modeset_helper_vtables.h
> +++ b/include/drm/drm_modeset_helper_vtables.h
> @@ -736,6 +736,11 @@ struct drm_connector_helper_funcs {
>  	 * inspect dynamic configuration state should instead use
>  	 * @atomic_best_encoder.
>  	 *
> +	 * You can leave this function to NULL if the connector is only
> +	 * attached to a single encoder and you are using the atomic helpers.
> +	 * In this case, the core will call drm_atomic_helper_best_encoder()
> +	 * for you.
> +	 *
>  	 * RETURNS:
>  	 *
>  	 * Encoder that should be used for the given connector and connector
> @@ -752,8 +757,9 @@ struct drm_connector_helper_funcs {
>  	 * need to select the best encoder depending upon the desired
>  	 * configuration and can't select it statically.
>  	 *
> -	 * This function is used by drm_atomic_helper_check_modeset() and either
> -	 * this or @best_encoder is required.
> +	 * This function is used by drm_atomic_helper_check_modeset().
> +	 * If it is not implemented, the core will fallback to @best_encoder
> +	 * (or drm_atomic_helper_best_encoder() if @best_encoder is NULL).
>  	 *
>  	 * NOTE:
>  	 *
> -- 
> 2.7.4
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From broonie at kernel.org  Tue Jun  7 08:54:27 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 7 Jun 2016 16:54:27 +0100
Subject: [alsa-devel] [PATCH v2 2/9] ASoC: mediatek: implement mediatek
 common structure
In-Reply-To: <1464929784-5489-3-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-3-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <20160607155427.GB7510@sirena.org.uk>

On Fri, Jun 03, 2016 at 12:56:17PM +0800, Garlic Tseng wrote:

> +	/*enable agent*/

Lots of comments with missing spaces in them, there's quite a few
examples of that in here.

> +EXPORT_SYMBOL(mtk_afe_fe_startup);

All the ASoC and regmap APIs are _GPL(), you really shouldn't export new
interfaces based on them without it - the point with the _GPL() is that
non-GPL code shouldn't be able to use the APIs.

> +EXPORT_SYMBOL(mtk_afe_fe_hw_params);

Do you need to export the individual ops rather than just the ops
structure?

> +int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
> +		       struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> +	struct snd_pcm_runtime * const runtime = substream->runtime;
> +	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
> +	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
> +	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
> +	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
> +	unsigned int counter = runtime->period_size;
> +	int fs;
> +
> +	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);

That's way too noisy, dev_dbg() at most.

> +static DEFINE_MUTEX(irqs_lock);
> +int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
> +{
> +	int i;
> +
> +	mutex_lock(&irqs_lock);
> +	for (i = 0; i < afe->irqs_size; ++i) {

Why is the lock global and not part of the AFE struct?

> +void mtk_simple_isr(struct mtk_base_afe *afe, struct mtk_base_afe_memif *memif)
> +{
> +	snd_pcm_period_elapsed(memif->substream);
> +}
> +EXPORT_SYMBOL(mtk_simple_isr);

Is this really worth it over just calling _period_elapsed() directly?
-------------- 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-mediatek/attachments/20160607/82eeab5e/attachment.sig>

From broonie at kernel.org  Tue Jun  7 09:15:08 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 7 Jun 2016 17:15:08 +0100
Subject: [alsa-devel] [PATCH v2 5/9] ASoC: mediatek: add structure define
 and clock control for 2701
In-Reply-To: <1464929784-5489-6-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-6-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <20160607161508.GC7510@sirena.org.uk>

On Fri, Jun 03, 2016 at 12:56:20PM +0800, Garlic Tseng wrote:

> +void mt2701_init_clock(struct mtk_base_afe *afe)
> +{
> +	struct mt2701_afe_private *afe_priv = afe->platform_priv;
> +	int i = 0;
> +
> +	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
> +		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
> +		if (IS_ERR(aud_clks[i]))
> +			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
> +				 __func__, aud_clks[i]);
> +	}
> +}

This looks like we just ignore the error but I'm not seeing anything
that stops us trying to use the clocks or handle deferred probe?
-------------- 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-mediatek/attachments/20160607/e066cb30/attachment.sig>

From broonie at kernel.org  Tue Jun  7 09:31:12 2016
From: broonie at kernel.org (Mark Brown)
Date: Tue, 7 Jun 2016 17:31:12 +0100
Subject: [alsa-devel] [PATCH v2 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1464929784-5489-7-git-send-email-garlic.tseng@mediatek.com>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-7-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <20160607163112.GD7510@sirena.org.uk>

On Fri, Jun 03, 2016 at 12:56:21PM +0800, Garlic Tseng wrote:

> +	if (val < 0 || val > MT2701_I2S_NUM) {
> +		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
> +			__func__, num, val);
> +		return -1;

Real error codes please.

> +static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
> +	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1,
> +				    1),
> +};

On/off controls should end in Switch.
-------------- 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-mediatek/attachments/20160607/23b0b2bb/attachment-0001.sig>

From matthias.bgg at gmail.com  Tue Jun  7 09:59:31 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Tue, 7 Jun 2016 18:59:31 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <575181E5.6090603@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com>
Message-ID: <5756FD73.3050607@gmail.com>



On 03/06/16 15:11, Matthias Brugger wrote:
>
>
[...]

>>>>>>>>>> +
>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>> +        }
>>>>>>>>>> +
>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>> task->command_size,
>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>> +    }
>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>> +    bool err;
>>>>>>>>>> +
>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>> +        err = true;
>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>> +        err = false;
>>>>>>>>>> +    else
>>>>>>>>>> +        return;
>>>>>>>>>> +
>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>> +
>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>> +                 list_entry) {
>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>
>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>> commands in the
>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>> guarantees
>>>>>>>>> about dma_handle?
>>>>>>>>
>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>> 2. Yes.
>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>
>>>>>>>
>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>> calling
>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>> use this
>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>> currently
>>>>>>> executing. So I think this mecanism is not working.
>>>>>>
>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>    From CPU's point of view, tasks are linked by the busy list.
>>>>>>    From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>
>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>
>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>> when it is running ISR.
>>>>>>
>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>
>>>>>
>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>> we need to do it in a thread context.
>>>>
>>>> Actually, our initial implementation is similar to your suggestion,
>>>> but display needs CMDQ to return callback function very precisely,
>>>> else display will drop frame.
>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>> and CMDQ needs to call callback function in ISR.
>>>> If we defer callback to workqueue, the time interval may be larger than
>>>> 32 ms.sometimes.
>>>>
>>>
>>> I think the problem is, that you implemented the workqueue as a ordered
>>> workqueue, so there is no parallel processing. I'm still not sure why
>>> you need the workqueue to be ordered. Can you please explain.
>>
>> The order should be kept.
>> Let me use mouse cursor as an example.
>> If task 1 means move mouse cursor to point A, task 2 means point B,
>> and task 3 means point C, our expected result is A -> B -> C.
>> If the order is not kept, the result could become A -> C -> B.
>>
>
> Got it, thanks for the clarification.
>

I think a way to get rid of the workqueue is to use a timer, which gets 
programmed to the time a timeout in the first task in the busy list 
would happen. Everytime we update the busy list (e.g. because of task 
got finished by the thread), we update the timer. When the timer 
triggers, which hopefully won't happen too often, we return timeout on 
the busy list elements, until the time is lower then the actual time.

At least with this we can reduce the data structures in this driver and 
make it more lightweight.

Best regards,
Matthias

>>>>> I keep thinking about how to get rid of the two data structures,
>>>>> task_busy_list and the task_release_wq. We need the latter for the
>>>>> only
>>>>> sake of getting a timeout.
>>>>>
>>>>> Did you have a look on how the mailbox framework handles this?
>>>>> By the way, what is the reason to not implement the whole driver as a
>>>>> mailbox controller? For me, this driver looks like a good fit.
>>>>
>>>> CMDQ needs to encode commands for GCE hardware. We think this behavior
>>>> should be put in CMDQ driver, and client just call CMDQ functions.
>>>> Therefore, if we want to use mailbox framework, cmdq_rec must be
>>>> mailbox client, and the others must be mailbox controller.
>>>>
>>>
>>> You mean the functions to fill the cmdq_rec and execute it?
>>> I think this should be part of the driver.
>>
>> Yes.
>>
>>> Jassi, can you have a look on the interface this driver exports [0].
>>> They are needed to actually create the message which will be send.
>>> Could something like this be part of a mailbox driver?
>>>
>>> [0] https://patchwork.kernel.org/patch/9140221/
>>>
>>>> However, if we use mailbox controller, CMDQ driver still needs to
>>>> control busy list for each GCE thread, and use workqueue to handle
>>>> timeout tasks.
>>>>
>>>
>>> Let me summarize my ideas around this driver:
>>> When we enter the ISR, we know that all task in task_busy_list before
>>> the entry which represents curr_task can be set to TASK_STATE_DONE.
>>> The curr_task could be TASK_STATE_ERROR if the corresponding bit in the
>>> irq status registers is set.
>>> Do we need to call the callback in the same order as the tasks got
>>> dispatched to the HW thread? If not, we could try to call all this
>>> callbacks in a multithreaded workqueue.
>>
>> Yes, we should keep order.
>>
>>> Regards,
>>> Matthias
>>
>> Thanks,
>> HS
>>
>>>> The only thing that we can borrow from mailbox framework is the send
>>>> (CMDQ flush) and receive (CMDQ callback) interface, However, we don't
>>>> think we can gain many benefits from it, and we have some overheads to
>>>> conform to mailbox interface.
>>>>
>>>>
>>>>>
>>>>>>>>>> +            curr_task = task;
>>>>>>>>>> +        if (task->cb.cb) {
>>>>>>>>>> +            cmdq_cb_data.err = curr_task ? err : false;
>>>>>>>>>> +            cmdq_cb_data.data = task->cb.data;
>>>>>>>>>> +            task->cb.cb(cmdq_cb_data);
>>>>>>>>>> +        }
>>>>>>>>>> +        task->task_state = (curr_task && err) ?
>>>>>>>>>> TASK_STATE_ERROR :
>>>>>>>>>> +                TASK_STATE_DONE;
>>>>>>>>>> +        list_del(&task->list_entry);
>>>>>>>>>> +        if (curr_task)
>>>>>>>>>> +            break;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    wake_up(&thread->wait_task_done);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_thread *thread = &cmdq->thread[tid];
>>>>>>>>>> +    unsigned long flags = 0L;
>>>>>>>>>> +    u32 irq_flag;
>>>>>>>>>> +
>>>>>>>>>> +    spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>>>>> +
>>>>>>>>>> +    irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>>>>> +    cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>>>>> +
>>>>>>>>>> +    /*
>>>>>>>>>> +     * Another CPU core could run "release task" right before
>>>>>>>>>> we acquire
>>>>>>>>>> +     * the spin lock, and thus reset / disable this GCE
>>>>>>>>>> thread, so we
>>>>>>>>>> +     * need to check the enable bit of this GCE thread.
>>>>>>>>>> +     */
>>>>>>>>>> +    if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>>>> +          CMDQ_THR_ENABLED))
>>>>>>>>>> +        irq_flag = 0;
>>>>>>>>>
>>>>>>>>> cmdq_handle_error_done just retuns in this case. Programming
>>>>>>>>> this way
>>>>>>>>> just makes things confusing. What about:
>>>>>>>>>
>>>>>>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>>>           CMDQ_THR_ENABLED)
>>>>>>>>>     cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>>> else
>>>>>>>>>         irq_flag = 0;
>>>>>>>>>
>>>>>>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>
>>>>>>>> We still need to clear irq_flag if GCE thread is disabled.
>>>>>>>> So, I think we can just return here.
>>>>>>>>
>>>>>>>>     if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
>>>>>>>>           CMDQ_THR_ENABLED))
>>>>>>>>         return;
>>>>>>>>
>>>>>>>> What do you think?
>>>>>>>>
>>>>>>>
>>>>>>> No, you can't just return, you need to unlock the spinlock.
>>>>>>> Anyway I would prefer it the other way round, as I put it in my last
>>>>>>> mail. Just delete the else branch, we don't need to set irq_flag
>>>>>>> to zero.
>>>>>>
>>>>>> Sorry for my previous wrong reply since I merge your comment
>>>>>> and CK's comment.
>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
>>>>>>
>>>>>> So, I will put this if condition into cmdq_task_handle_error_result()
>>>>>> and then just return it if GCE thread is disabled.
>>>>>>
>>>>>
>>>>> You mean in cmdq_task_handle_done
>>>>> We should rename this functions to not create confusion.
>>>>
>>>> Sorry again. I mean in cmdq_handle_error_done().
>>>> This function handles both done and error.
>>>>
>>>> I agree the function name looks confusion.
>>>> I think it can be renamed to cmdq_thread_irq_handler()
>>>> since it is used to handle irq for GCE thread.
>>>>
>>>>> Regards,
>>>>> Matthias
>>>>
>>>> Thanks,
>>>> HS
>>>>
>>>>>>>>>> +
>>>>>>>>>> +    cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = dev;
>>>>>>>>>> +    u32 irq_status;
>>>>>>>>>> +    int i;
>>>>>>>>>> +
>>>>>>>>>> +    irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
>>>>>>>>>> +    irq_status &= CMDQ_IRQ_MASK;
>>>>>>>>>> +    irq_status ^= CMDQ_IRQ_MASK;
>>>>>>>>>
>>>>>>>>> irq_status can be much bigger then 3, which is the number of
>>>>>>>>> threads in
>>>>>>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here
>>>>>>>>> isn't
>>>>>>>>> clear to me.
>>>>>>>>
>>>>>>>> Our GCE hardware has 16 threads, but we only use 3 threads
>>>>>>>> currently.
>>>>>>>>
>>>>>>>
>>>>>>> Ok, but please use bitops here.
>>>>>>
>>>>>> Will use bitops.
>>>>>>
>>>>>>>>>> +
>>>>>>>>>> +    if (!irq_status)
>>>>>>>>>> +        return IRQ_NONE;
>>>>>>>>>> +
>>>>>>>>>> +    while (irq_status) {
>>>>>>>>>> +        i = ffs(irq_status) - 1;
>>>>>>>>>> +        irq_status &= ~BIT(i);
>>>>>>>>>> +        cmdq_thread_irq_handler(cmdq, i);
>>>>>>>>>> +    }
>>>>>>>>>
>>>>>>>>> Can you explain how the irq status register looks like, that
>>>>>>>>> would it
>>>>>>>>> make much easier to understand what happens here.
>>>>>>>>
>>>>>>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
>>>>>>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
>>>>>>>>
>>>>>>>
>>>>>>> Thanks, that helped. :)
>>>>>>>
>>>>>>>>>> +
>>>>>>>>>> +    return IRQ_HANDLED;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
>>>>>>>>>
>>>>>>>>> We never check the return values, why do we have them?
>>>>>>>>
>>>>>>>> Will drop return value.
>>>>>>>>
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
>>>>>>>>>> +    struct device *dev = cmdq->dev;
>>>>>>>>>> +    struct cmdq_thread *thread = task->thread;
>>>>>>>>>> +    struct cmdq_task *next_task, *prev_task;
>>>>>>>>>> +    u32 irq_flag;
>>>>>>>>>> +
>>>>>>>>>> +    /* suspend GCE thread to ensure consistency */
>>>>>>>>>> +    WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
>>>>>>>>>> +
>>>>>>>>>> +    /* ISR has handled this error task */
>>>>>>>>>> +    if (task->task_state == TASK_STATE_ERROR) {
>>>>>>>>>> +        next_task =
>>>>>>>>>> list_first_entry_or_null(&thread->task_busy_list,
>>>>>>>>>> +                struct cmdq_task, list_entry);
>>>>>>>>>> +        if (next_task) /* move to next task */
>>>>>>>>>> +            cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>>> +                       CMDQ_THR_CURR_ADDR);
>>>>>>>>>
>>>>>>>>> We have to do this, as we suppose that the thread did not reach
>>>>>>>>> the jump
>>>>>>>>> instruction we put into it's command queue, right?
>>>>>>>>
>>>>>>>> Yes.
>>>>>>>>
>>>>>>>
>>>>>>> So this should then go into it's own function. In wait_release_work,
>>>>>>> something like this:
>>>>>>>
>>>>>>> if(task->task_state == TASK_STATE_ERROR)
>>>>>>>          cmdq_task_handle_error(task)
>>>>>>
>>>>>> OK.
>>>>>> I will write new function cmdq_task_handle_error() to handle error
>>>>>> case.
>>>>>>
>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>> +        return -ECANCELED;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> if task_state != ERROR and != DONE. This means that the timeout of
>>>>>>>>> task_release_wq has timed out, right?
>>>>>>>>
>>>>>>>> Yes.
>>>>>>>>
>>>>>>>>>> +    /*
>>>>>>>>>> +     * Save next_task and prev_task in advance
>>>>>>>>>> +     * since cmdq_handle_error_done will remove list_entry.
>>>>>>>>>> +     */
>>>>>>>>>> +    next_task = prev_task = NULL;
>>>>>>>>>> +    if (task->list_entry.next != &thread->task_busy_list)
>>>>>>>>>> +        next_task = list_next_entry(task, list_entry);
>>>>>>>>>> +    if (task->list_entry.prev != &thread->task_busy_list)
>>>>>>>>>> +        prev_task = list_prev_entry(task, list_entry);
>>>>>>>>>> +
>>>>>>>>>> +    /*
>>>>>>>>>> +     * Although IRQ is disabled, GCE continues to execute.
>>>>>>>>>> +     * It may have pending IRQ before GCE thread is suspended,
>>>>>>>>>> +     * so check this condition again.
>>>>>>>>>> +     */
>>>>>>>>>
>>>>>>>>> The first thing we did in this function was suspending the
>>>>>>>>> thread. Why
>>>>>>>>> do we need this then?
>>>>>>>>
>>>>>>>> Because timeout is CPU timeout not GCE timeout, GCE could just
>>>>>>>> finish
>>>>>>>> this task before the GCE thread is suspended.
>>>>>>>>
>>>>>>>
>>>>>>> What are the reasons for a timeout? An error has happend, or the
>>>>>>> task is
>>>>>>> still executing.
>>>>>>
>>>>>>    From GCE's point of view, this task is still executing.
>>>>>> But, it could be an error of client.
>>>>>> For example, task may never get event if display turn off hardware
>>>>>> before waiting for task to finish its work.
>>>>>>
>>>>>>>>> To be honest this whole functions looks really like a design
>>>>>>>>> error. We
>>>>>>>>> have to sperate the states much clearer so that it is possible to
>>>>>>>>> understand what is happening in the GCE. Isn't it for example
>>>>>>>>> posible to
>>>>>>>>> have worker queues for timed out tasks and tasks with an error?
>>>>>>>>> I'm not
>>>>>>>>> sure how to do this, actually I'm not sure if I really
>>>>>>>>> understood how
>>>>>>>>> this is supposed to work.
>>>>>>>>
>>>>>>>> GCE doesn't have timeout. The timeout is decided and controlled
>>>>>>>> by CPU,
>>>>>>>> so we check timeout in release work.
>>>>>>>> For error and done, they are easy to check by register, and we have
>>>>>>>> already created release work for timeout. So, I don't think we
>>>>>>>> need to
>>>>>>>> create work queue for each case.
>>>>>>>>
>>>>>>>> What do you think?
>>>>>>>>
>>>>>>>
>>>>>>> I think, if we find in here, that the irq_flag is set, then the the
>>>>>>> interrupt handler was triggered and is spinning the spinlock. If
>>>>>>> this is
>>>>>>> not the case, we have a timeout and we handle this.
>>>>>>
>>>>>> I will write another function to handle error, and handle timeout
>>>>>> here.
>>>>>>
>>>>>>>>> How much do we win, when we patch the thread command queue for
>>>>>>>>> every
>>>>>>>>> task we add, instead of just taking one task after another from
>>>>>>>>> the
>>>>>>>>> task_busy_list?
>>>>>>>>
>>>>>>>> GCE is used to help read/write registers with critical time
>>>>>>>> limitation.
>>>>>>>> Sometimes, client may ask to process multiple tasks in a short
>>>>>>>> period
>>>>>>>> of time, e.g. display flush multiple tasks for next vblank. So,
>>>>>>>> CMDQ
>>>>>>>> shouldn't limit to process one task after another from the
>>>>>>>> task_busy_list. Currently, when interrupt or timeout, we will check
>>>>>>>> how many tasks are done, and which one is error or timeout.
>>>>>>>>
>>>>>>>
>>>>>>> So I suppose the device driver who use this are interested in
>>>>>>> throughput
>>>>>>> and not in latency. The callback of every
>>>>>>>
>>>>>>>>>> +    irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
>>>>>>>>>> +    cmdq_handle_error_done(cmdq, thread, irq_flag);
>>>>>>>>>> +    cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
>>>>>>>>>> +
>>>>>>>>>> +    if (task->task_state == TASK_STATE_DONE) {
>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>> +        return 0;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    if (task->task_state == TASK_STATE_ERROR) {
>>>>>>>>>> +        dev_err(dev, "task 0x%p error\n", task);
>>>>>>>>>> +        if (next_task) /* move to next task */
>>>>>>>>>> +            cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>>> +                       CMDQ_THR_CURR_ADDR);
>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>> +        return -ECANCELED;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    /* Task is running, so we force to remove it. */
>>>>>>>>>> +    dev_err(dev, "task 0x%p timeout or killed\n", task);
>>>>>>>>>> +    task->task_state = TASK_STATE_ERROR;
>>>>>>>>>> +
>>>>>>>>>> +    if (prev_task) {
>>>>>>>>>> +        u64 *prev_va = prev_task->va_base;
>>>>>>>>>> +        u64 *curr_va = task->va_base;
>>>>>>>>>> +
>>>>>>>>>> +        /* copy JUMP instruction */
>>>>>>>>>> +        prev_va[prev_task->num_cmd - 1] =
>>>>>>>>>> curr_va[task->num_cmd - 1];
>>>>>>>>>> +
>>>>>>>>>> +        cmdq_thread_invalidate_fetched_data(thread);
>>>>>>>>>> +    } else if (next_task) { /* move to next task */
>>>>>>>>>> +        cmdq_thread_writel(thread, next_task->pa_base,
>>>>>>>>>> +                   CMDQ_THR_CURR_ADDR);
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    list_del(&task->list_entry);
>>>>>>>>>> +    cmdq_thread_resume(thread);
>>>>>>>>>> +
>>>>>>>>>> +    /* call cb here to prevent lock */
>>>>>>>>>> +    if (task->cb.cb) {
>>>>>>>>>> +        struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>> +
>>>>>>>>>> +        cmdq_cb_data.err = true;
>>>>>>>>>> +        cmdq_cb_data.data = task->cb.data;
>>>>>>>>>> +        task->cb.cb(cmdq_cb_data);
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    return -ECANCELED;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void cmdq_task_wait_release_work(struct work_struct
>>>>>>>>>> *work_item)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_task *task = container_of(work_item, struct
>>>>>>>>>> cmdq_task,
>>>>>>>>>> +                          release_work);
>>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
>>>>>>>>>> +    struct cmdq_thread *thread = task->thread;
>>>>>>>>>> +    unsigned long flags;
>>>>>>>>>> +
>>>>>>>>>> +    wait_event_timeout(thread->wait_task_done,
>>>>>>>>>> +               task->task_state != TASK_STATE_BUSY,
>>>>>>>>>> +               msecs_to_jiffies(CMDQ_TIMEOUT_MS));
>>>>>>>>>> +
>>>>>>>>>> +    spin_lock_irqsave(&cmdq->exec_lock, flags);
>>>>>>>>>> +    if (task->task_state != TASK_STATE_DONE)
>>>>>>>>>> +        cmdq_task_handle_error_result(task);
>>>>>>>>>> +    if (list_empty(&thread->task_busy_list))
>>>>>>>>>> +        cmdq_thread_disable(cmdq, thread);
>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>> +
>>>>>>>>>> +    /* release regardless of success or not */
>>>>>>>>>> +    clk_disable_unprepare(cmdq->clock);
>>>>>>>>>> +    cmdq_task_release(task);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task
>>>>>>>>>> *task)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
>>>>>>>>>> +
>>>>>>>>>> +    INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
>>>>>>>>>> +    queue_work(cmdq->task_release_wq, &task->release_work);
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec,
>>>>>>>>>> size_t size)
>>>>>>>>>> +{
>>>>>>>>>> +    void *new_buf;
>>>>>>>>>> +
>>>>>>>>>> +    new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
>>>>>>>>>> +    if (!new_buf)
>>>>>>>>>> +        return -ENOMEM;
>>>>>>>>>> +    rec->buf = new_buf;
>>>>>>>>>> +    rec->buf_size = size;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_base *cmdq_base;
>>>>>>>>>> +    struct resource res;
>>>>>>>>>> +    int subsys;
>>>>>>>>>> +    u32 base;
>>>>>>>>>> +
>>>>>>>>>> +    if (of_address_to_resource(dev->of_node, 0, &res))
>>>>>>>>>> +        return NULL;
>>>>>>>>>> +    base = (u32)res.start;
>>>>>>>>>> +
>>>>>>>>>> +    subsys = cmdq_subsys_base_to_id(base >> 16);
>>>>>>>>>> +    if (subsys < 0)
>>>>>>>>>> +        return NULL;
>>>>>>>>>> +
>>>>>>>>>> +    cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base),
>>>>>>>>>> GFP_KERNEL);
>>>>>>>>>> +    if (!cmdq_base)
>>>>>>>>>> +        return NULL;
>>>>>>>>>> +    cmdq_base->subsys = subsys;
>>>>>>>>>> +    cmdq_base->base = base;
>>>>>>>>>> +
>>>>>>>>>> +    return cmdq_base;
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_register_device);
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>>>>> +            struct cmdq_rec **rec_ptr)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_rec *rec;
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    rec = kzalloc(sizeof(*rec), GFP_KERNEL);
>>>>>>>>>> +    if (!rec)
>>>>>>>>>> +        return -ENOMEM;
>>>>>>>>>> +    rec->cmdq = dev_get_drvdata(dev);
>>>>>>>>>> +    rec->engine_flag = engine_flag;
>>>>>>>>>> +    err = cmdq_rec_realloc_cmd_buffer(rec,
>>>>>>>>>> CMDQ_INITIAL_CMD_BLOCK_SIZE);
>>>>>>>>>
>>>>>>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
>>>>>>>>
>>>>>>>> Will do.
>>>>>>>>
>>>>>>>>>> +    if (err < 0) {
>>>>>>>>>> +        kfree(rec);
>>>>>>>>>> +        return err;
>>>>>>>>>> +    }
>>>>>>>>>> +    *rec_ptr = rec;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum
>>>>>>>>>> cmdq_code code,
>>>>>>>>>> +                   u32 arg_a, u32 arg_b)
>>>>>>>>>> +{
>>>>>>>>>> +    u64 *cmd_ptr;
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    if (WARN_ON(rec->finalized))
>>>>>>>>>> +        return -EBUSY;
>>>>>>>>>> +    if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
>>>>>>>>>> +        return -EINVAL;
>>>>>>>>>
>>>>>>>>> cmdq_rec_append_command is just called from inside this driver
>>>>>>>>> and code
>>>>>>>>> is a enum. We can expect it to be correct, no need for this check.
>>>>>>>>
>>>>>>>> Will drop this check.
>>>>>>>>
>>>>>>>>>> +    if (unlikely(rec->command_size + CMDQ_INST_SIZE >
>>>>>>>>>> rec->buf_size)) {
>>>>>>>>>
>>>>>>>>> command_size is the offset into the buffer to which a new
>>>>>>>>> command is
>>>>>>>>> written, so this name is highly confusing. I wonder if this
>>>>>>>>> would be
>>>>>>>>> easier to understand if we redefine command_size to something
>>>>>>>>> like the
>>>>>>>>> number of commands and divide/multiply CMDQ_INST_SIZE where
>>>>>>>>> this is needed.
>>>>>>>>
>>>>>>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
>>>>>>>> dividing CMDQ_INST_SIZE.
>>>>>>>> What do you think?
>>>>>>>>
>>>>>>>>>> +        err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size
>>>>>>>>>> * 2);
>>>>>>>>>> +        if (err < 0)
>>>>>>>>>> +            return err;
>>>>>>>>>> +    }
>>>>>>>>>> +    cmd_ptr = rec->buf + rec->command_size;
>>>>>>>>>> +    (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a)
>>>>>>>>>> << 32 | arg_b;
>>>>>>>>>> +    rec->command_size += CMDQ_INST_SIZE;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct
>>>>>>>>>> cmdq_base *base,
>>>>>>>>>> +           u32 offset)
>>>>>>>>>> +{
>>>>>>>>>> +    u32 arg_a = ((base->base + offset) &
>>>>>>>>>> CMDQ_ARG_A_WRITE_MASK) |
>>>>>>>>>> +            ((base->subsys & CMDQ_SUBSYS_MASK) <<
>>>>>>>>>> CMDQ_SUBSYS_SHIFT);
>>>>>>>>>
>>>>>>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
>>>>>>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
>>>>>>>>
>>>>>>>> Will drop it.
>>>>>>>>
>>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE,
>>>>>>>>>> arg_a, value);
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>>>>> +            struct cmdq_base *base, u32 offset, u32 mask)
>>>>>>>>>> +{
>>>>>>>>>> +    u32 offset_mask = offset;
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    if (mask != 0xffffffff) {
>>>>>>>>>> +        err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0,
>>>>>>>>>> ~mask);
>>>>>>>>>> +        if (err < 0)
>>>>>>>>>> +            return err;
>>>>>>>>>> +        offset_mask |= CMDQ_WRITE_ENABLE_MASK;
>>>>>>>>>> +    }
>>>>>>>>>> +    return cmdq_rec_write(rec, value, base, offset_mask);
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
>>>>>>>>>> +{
>>>>>>>>>> +    u32 arg_b;
>>>>>>>>>> +
>>>>>>>>>> +    if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>>>>> +        return -EINVAL;
>>>>>>>>>> +
>>>>>>>>>> +    /*
>>>>>>>>>> +     * bit 0-11: wait value
>>>>>>>>>> +     * bit 15: 1 - wait, 0 - no wait
>>>>>>>>>> +     * bit 16-27: update value
>>>>>>>>>> +     * bit 31: 1 - update, 0 - no update
>>>>>>>>>> +     */
>>>>>>>>>
>>>>>>>>> I don't understand this comments. What are they for?
>>>>>>>>
>>>>>>>> This is for WFE command. I will comment it.
>>>>>>>>
>>>>>>>>>> +    arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT |
>>>>>>>>>> CMDQ_WFE_WAIT_VALUE;
>>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
>>>>>>>>>> arg_b);
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum
>>>>>>>>>> cmdq_event event)
>>>>>>>>>> +{
>>>>>>>>>> +    if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
>>>>>>>>>> +        return -EINVAL;
>>>>>>>>>> +
>>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
>>>>>>>>>> +                       CMDQ_WFE_UPDATE);
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
>>>>>>>>>> +{
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    if (rec->finalized)
>>>>>>>>>> +        return 0;
>>>>>>>>>> +
>>>>>>>>>> +    /* insert EOC and generate IRQ for each command iteration */
>>>>>>>>>> +    err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0,
>>>>>>>>>> CMDQ_EOC_IRQ_EN);
>>>>>>>>>> +    if (err < 0)
>>>>>>>>>> +        return err;
>>>>>>>>>> +
>>>>>>>>>> +    /* JUMP to end */
>>>>>>>>>> +    err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0,
>>>>>>>>>> CMDQ_JUMP_PASS);
>>>>>>>>>> +    if (err < 0)
>>>>>>>>>> +        return err;
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> Does this need to be atomic?
>>>>>>>>> What happens if after CODE_EOC and before CODE_JUMP some
>>>>>>>>> write/read/event gets added?
>>>>>>>>> What happens if more commands get added to the queue after
>>>>>>>>> CODE_JUMP,
>>>>>>>>> but before finalized is set to true. Why don't you use atomic
>>>>>>>>> functions
>>>>>>>>> to access finalized?
>>>>>>>>
>>>>>>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
>>>>>>>> client uses cmdq_rec.
>>>>>>>>
>>>>>>>
>>>>>>> Well I think that rec->finalized tries to implement this, but might
>>>>>>> fail, if two kernel threads work on the same cmdq_rec.
>>>>>>>
>>>>>>>>>> +    rec->finalized = true;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec,
>>>>>>>>>> cmdq_async_flush_cb cb,
>>>>>>>>>> +             void *data)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = rec->cmdq;
>>>>>>>>>> +    struct cmdq_task *task;
>>>>>>>>>> +    struct cmdq_task_cb task_cb;
>>>>>>>>>> +    struct cmdq_thread *thread;
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    mutex_lock(&cmdq->task_mutex);
>>>>>>>>>> +    if (cmdq->suspended) {
>>>>>>>>>> +        dev_err(cmdq->dev, "%s is called after suspended\n",
>>>>>>>>>> __func__);
>>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
>>>>>>>>>> +        return -EPERM;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    err = cmdq_rec_finalize(rec);
>>>>>>>>>> +    if (err < 0) {
>>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
>>>>>>>>>> +        return err;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    task_cb.cb = cb;
>>>>>>>>>> +    task_cb.data = data;
>>>>>>>>>> +    task = cmdq_task_acquire(rec, task_cb);
>>>>>>>>>> +    if (!task) {
>>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
>>>>>>>>>> +        return -EFAULT;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    thread =
>>>>>>>>>> &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
>>>>>>>>>> +    cmdq_task_exec(task, thread);
>>>>>>>>>> +    cmdq_task_wait_release_schedule(task);
>>>>>>>>>> +    mutex_unlock(&cmdq->task_mutex);
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_flush_completion {
>>>>>>>>>> +    struct completion cmplt;
>>>>>>>>>> +    bool err;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_flush_completion *cmplt = data.data;
>>>>>>>>>> +
>>>>>>>>>> +    cmplt->err = data.err;
>>>>>>>>>> +    complete(&cmplt->cmplt);
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_flush_completion cmplt;
>>>>>>>>>> +    int err;
>>>>>>>>>> +
>>>>>>>>>> +    init_completion(&cmplt.cmplt);
>>>>>>>>>> +    err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
>>>>>>>>>> +    if (err < 0)
>>>>>>>>>> +        return err;
>>>>>>>>>> +    wait_for_completion(&cmplt.cmplt);
>>>>>>>>>> +    return cmplt.err ? -EFAULT : 0;
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
>>>>>>>>>> +
>>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
>>>>>>>>>> +{
>>>>>>>>>> +    kfree(rec->buf);
>>>>>>>>>> +    kfree(rec);
>>>>>>>>>> +}
>>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
>>>>>>>>>> +
>>>>>>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq_thread *thread;
>>>>>>>>>> +    int i;
>>>>>>>>>> +
>>>>>>>>>> +    for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>>>>> +        thread = &cmdq->thread[i];
>>>>>>>>>> +        if (!list_empty(&thread->task_busy_list))
>>>>>>>>>> +            return false;
>>>>>>>>>> +    }
>>>>>>>>>> +    return true;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_suspend(struct device *dev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>>>>> +    u32 exec_threads;
>>>>>>>>>> +
>>>>>>>>>> +    mutex_lock(&cmdq->task_mutex);
>>>>>>>>>> +    cmdq->suspended = true;
>>>>>>>>>> +    mutex_unlock(&cmdq->task_mutex);
>>>>>>>>>> +
>>>>>>>>>> +    exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
>>>>>>>>>> +    if ((exec_threads & CMDQ_THR_EXECUTING) &&
>>>>>>>>>> !cmdq_task_is_empty(cmdq)) {
>>>>>>>>>> +        dev_err(dev, "wait active tasks timeout.\n");
>>>>>>>>>> +        flush_workqueue(cmdq->task_release_wq);
>>>>>>>>>> +    }
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_resume(struct device *dev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = dev_get_drvdata(dev);
>>>>>>>>>> +
>>>>>>>>>> +    cmdq->suspended = false;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_remove(struct platform_device *pdev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct cmdq *cmdq = platform_get_drvdata(pdev);
>>>>>>>>>> +
>>>>>>>>>> +    destroy_workqueue(cmdq->task_release_wq);
>>>>>>>>>> +    cmdq->task_release_wq = NULL;
>>>>>>>>>> +    return 0;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static int cmdq_probe(struct platform_device *pdev)
>>>>>>>>>> +{
>>>>>>>>>> +    struct device *dev = &pdev->dev;
>>>>>>>>>> +    struct device_node *node = dev->of_node;
>>>>>>>>>> +    struct resource *res;
>>>>>>>>>> +    struct cmdq *cmdq;
>>>>>>>>>> +    int err, i;
>>>>>>>>>> +
>>>>>>>>>> +    cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
>>>>>>>>>> +    if (!cmdq)
>>>>>>>>>> +        return -ENOMEM;
>>>>>>>>>> +    cmdq->dev = dev;
>>>>>>>>>> +
>>>>>>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>>>>>>>>> +    cmdq->base = devm_ioremap_resource(dev, res);
>>>>>>>>>> +    if (IS_ERR(cmdq->base)) {
>>>>>>>>>> +        dev_err(dev, "failed to ioremap gce\n");
>>>>>>>>>> +        return PTR_ERR(cmdq->base);
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    cmdq->irq = irq_of_parse_and_map(node, 0);
>>>>>>>>>> +    if (!cmdq->irq) {
>>>>>>>>>> +        dev_err(dev, "failed to get irq\n");
>>>>>>>>>> +        return -EINVAL;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
>>>>>>>>>> +        dev, cmdq->base, cmdq->irq);
>>>>>>>>>> +
>>>>>>>>>> +    mutex_init(&cmdq->task_mutex);
>>>>>>>>>> +    spin_lock_init(&cmdq->exec_lock);
>>>>>>>>>> +    cmdq->task_release_wq = alloc_ordered_workqueue(
>>>>>>>>>> +            "%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
>>>>>>>>>> +            "cmdq_task_wait_release");
>>>>>>>>>> +
>>>>>>>>>> +    for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
>>>>>>>>>> +        cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
>>>>>>>>>> +                CMDQ_THR_SHIFT * i;
>>>>>>>>>> +        init_waitqueue_head(&cmdq->thread[i].wait_task_done);
>>>>>>>>>> +        INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    platform_set_drvdata(pdev, cmdq);
>>>>>>>>>> +
>>>>>>>>>> +    err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler,
>>>>>>>>>> IRQF_SHARED,
>>>>>>>>>> +                   CMDQ_DRIVER_DEVICE_NAME, cmdq);
>>>>>>>>>> +    if (err < 0) {
>>>>>>>>>> +        dev_err(dev, "failed to register ISR (%d)\n", err);
>>>>>>>>>> +        goto fail;
>>>>>>>>>> +    }
>>>>>>>>>> +
>>>>>>>>>> +    cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
>>>>>>>>>> +    if (IS_ERR(cmdq->clock)) {
>>>>>>>>>> +        dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
>>>>>>>>>> +        err = PTR_ERR(cmdq->clock);
>>>>>>>>>> +        goto fail;
>>>>>>>>>> +    }
>>>>>>>>>> +    return 0;
>>>>>>>>>> +
>>>>>>>>>> +fail:
>>>>>>>>>> +    cmdq_remove(pdev);
>>>>>>>>>> +    return err;
>>>>>>>>>> +}
>>>>>>>>>> +
>>>>>>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
>>>>>>>>>> +    .suspend = cmdq_suspend,
>>>>>>>>>> +    .resume = cmdq_resume,
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static const struct of_device_id cmdq_of_ids[] = {
>>>>>>>>>> +    {.compatible = "mediatek,mt8173-gce",},
>>>>>>>>>> +    {}
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +static struct platform_driver cmdq_drv = {
>>>>>>>>>> +    .probe = cmdq_probe,
>>>>>>>>>> +    .remove = cmdq_remove,
>>>>>>>>>> +    .driver = {
>>>>>>>>>> +        .name = CMDQ_DRIVER_DEVICE_NAME,
>>>>>>>>>> +        .owner = THIS_MODULE,
>>>>>>>>>> +        .pm = &cmdq_pm_ops,
>>>>>>>>>> +        .of_match_table = cmdq_of_ids,
>>>>>>>>>> +    }
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +builtin_platform_driver(cmdq_drv);
>>>>>>>>>> diff --git a/include/soc/mediatek/cmdq.h
>>>>>>>>>> b/include/soc/mediatek/cmdq.h
>>>>>>>>>> new file mode 100644
>>>>>>>>>> index 0000000..60eef3d
>>>>>>>>>> --- /dev/null
>>>>>>>>>> +++ b/include/soc/mediatek/cmdq.h
>>>>>>>>>> @@ -0,0 +1,197 @@
>>>>>>>>>> +/*
>>>>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
>>>>>>>>>> + *
>>>>>>>>>> + * This program is free software; you can redistribute it
>>>>>>>>>> and/or modify
>>>>>>>>>> + * it under the terms of the GNU General Public License
>>>>>>>>>> version 2 as
>>>>>>>>>> + * published by the Free Software Foundation.
>>>>>>>>>> + *
>>>>>>>>>> + * This program is distributed in the hope that it will be
>>>>>>>>>> useful,
>>>>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied
>>>>>>>>>> warranty of
>>>>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>>>>>>>>> + * GNU General Public License for more details.
>>>>>>>>>> + */
>>>>>>>>>> +
>>>>>>>>>> +#ifndef __MTK_CMDQ_H__
>>>>>>>>>> +#define __MTK_CMDQ_H__
>>>>>>>>>> +
>>>>>>>>>> +#include <linux/platform_device.h>
>>>>>>>>>> +#include <linux/types.h>
>>>>>>>>>> +
>>>>>>>>>> +enum cmdq_eng {
>>>>>>>>>> +    CMDQ_ENG_DISP_AAL,
>>>>>>>>>> +    CMDQ_ENG_DISP_COLOR0,
>>>>>>>>>> +    CMDQ_ENG_DISP_COLOR1,
>>>>>>>>>> +    CMDQ_ENG_DISP_DPI0,
>>>>>>>>>> +    CMDQ_ENG_DISP_DSI0,
>>>>>>>>>> +    CMDQ_ENG_DISP_DSI1,
>>>>>>>>>> +    CMDQ_ENG_DISP_GAMMA,
>>>>>>>>>> +    CMDQ_ENG_DISP_OD,
>>>>>>>>>> +    CMDQ_ENG_DISP_OVL0,
>>>>>>>>>> +    CMDQ_ENG_DISP_OVL1,
>>>>>>>>>> +    CMDQ_ENG_DISP_PWM0,
>>>>>>>>>> +    CMDQ_ENG_DISP_PWM1,
>>>>>>>>>> +    CMDQ_ENG_DISP_RDMA0,
>>>>>>>>>> +    CMDQ_ENG_DISP_RDMA1,
>>>>>>>>>> +    CMDQ_ENG_DISP_RDMA2,
>>>>>>>>>> +    CMDQ_ENG_DISP_UFOE,
>>>>>>>>>> +    CMDQ_ENG_DISP_WDMA0,
>>>>>>>>>> +    CMDQ_ENG_DISP_WDMA1,
>>>>>>>>>> +    CMDQ_ENG_MAX,
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +/* events for CMDQ and display */
>>>>>>>>>> +enum cmdq_event {
>>>>>>>>>> +    /* Display start of frame(SOF) events */
>>>>>>>>>> +    CMDQ_EVENT_DISP_OVL0_SOF = 11,
>>>>>>>>>> +    CMDQ_EVENT_DISP_OVL1_SOF = 12,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_SOF = 13,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_SOF = 14,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_SOF = 15,
>>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA0_SOF = 16,
>>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA1_SOF = 17,
>>>>>>>>>> +    /* Display end of frame(EOF) events */
>>>>>>>>>> +    CMDQ_EVENT_DISP_OVL0_EOF = 39,
>>>>>>>>>> +    CMDQ_EVENT_DISP_OVL1_EOF = 40,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_EOF = 41,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_EOF = 42,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_EOF = 43,
>>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA0_EOF = 44,
>>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA1_EOF = 45,
>>>>>>>>>> +    /* Mutex end of frame(EOF) events */
>>>>>>>>>> +    CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
>>>>>>>>>> +    CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
>>>>>>>>>> +    CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
>>>>>>>>>> +    CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
>>>>>>>>>> +    CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
>>>>>>>>>> +    /* Display underrun events */
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
>>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
>>>>>>>>>> +    /* Keep this at the end of HW events */
>>>>>>>>>> +    CMDQ_MAX_HW_EVENT_COUNT = 260,
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_cb_data {
>>>>>>>>>> +    bool    err;
>>>>>>>>>> +    void    *data;
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_task;
>>>>>>>>>> +struct cmdq;
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_rec {
>>>>>>>>>> +    struct cmdq        *cmdq;
>>>>>>>>>> +    u64            engine_flag;
>>>>>>>>>> +    size_t            command_size;
>>>>>>>>>> +    void            *buf;
>>>>>>>>>> +    size_t            buf_size;
>>>>>>>>>> +    bool            finalized;
>>>>>>>>>> +};
>>>>>>>
>>>>>>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task
>>>>>>> for that
>>>>>>> and this way make the driver less complex?
>>>>>>
>>>>>> There are two main reasons for cmdq_rec.
>>>>>> 1. It is slow to access dma too frequently.
>>>>>>       So, we append commands to cacheable memory, and then flush
>>>>>> to dma.
>>>>>> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
>>>>>>       If we merge them, we need to take care of some synchronization
>>>>>>       issues.
>>>>>>
>>>>>>>>>> +
>>>>>>>>>> +struct cmdq_base {
>>>>>>>>>> +    int    subsys;
>>>>>>>>>> +    u32    base;
>>>>>>>>>
>>>>>>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base
>>>>>>>>> >> 16)
>>>>>>>>> so we can get rid of the struct, right?
>>>>>>>>
>>>>>>>> Current subsys method is based on previous comment from Daniel
>>>>>>>> Kurtz.
>>>>>>>> Please take a look of our previous discussion.
>>>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>>>>>>
>>>>>>>> Thanks.
>>>>>>>>
>>>>>>>
>>>>>>> I have to look deeper into this, but from what I read, the
>>>>>>> proposal from
>>>>>>> Daniel [1] seems good to me.
>>>>>>>
>>>>>>> [1] https://patchwork.kernel.org/patch/8068311/
>>>>>>
>>>>>> The initial proposal has some problem, so please see the follow-up
>>>>>> discussions. Thanks.
>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
>>>>>>
>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
>>>>>>
>>>>>>
>>>>>>
>>>>>>>>>> +};
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_register_device() - register device which needs CMDQ
>>>>>>>>>> + * @dev:        device
>>>>>>>>>> + *
>>>>>>>>>> + * Return: cmdq_base pointer or NULL for failed
>>>>>>>>>> + */
>>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_create() - create command queue record
>>>>>>>>>> + * @dev:        device
>>>>>>>>>> + * @engine_flag:    command queue engine flag
>>>>>>>>>> + * @rec_ptr:        command queue record pointer to retrieve
>>>>>>>>>> cmdq_rec
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
>>>>>>>>>> +            struct cmdq_rec **rec_ptr);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_write() - append write command to the command
>>>>>>>>>> queue record
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + * @value:    the specified target register value
>>>>>>>>>> + * @base:    the command queue base
>>>>>>>>>> + * @offset:    register offset from module base
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
>>>>>>>>>> +           struct cmdq_base *base, u32 offset);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_write_mask() - append write command with mask to
>>>>>>>>>> the command
>>>>>>>>>> + *               queue record
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + * @value:    the specified target register value
>>>>>>>>>> + * @base:    the command queue base
>>>>>>>>>> + * @offset:    register offset from module base
>>>>>>>>>> + * @mask:    the specified target register mask
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
>>>>>>>>>> +            struct cmdq_base *base, u32 offset, u32 mask);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_wfe() - append wait for event command to the
>>>>>>>>>> command queue reco    rd
>>>>>>>>>
>>>>>>>>> reco rd -> record
>>>>>>>>
>>>>>>>> Will fix it.
>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Matthias
>>>>>>>>>
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + * @event:    the desired event type to "wait and CLEAR"
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_clear_event() - append clear event command to the
>>>>>>>>>> command queue
>>>>>>>>>> + *                record
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + * @event:    the desired event to be cleared
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum
>>>>>>>>>> cmdq_event event);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded
>>>>>>>>>> commands
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + *
>>>>>>>>>> + * Trigger CMDQ to execute the recorded commands. Note that
>>>>>>>>>> this is a
>>>>>>>>>> + * synchronous flush function. When the function returned,
>>>>>>>>>> the recorded
>>>>>>>>>> + * commands have been done.
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously
>>>>>>>>>> execute the recorded
>>>>>>>>>> + *                commands and call back after ISR is finished
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + * @cb:        called in the end of CMDQ ISR
>>>>>>>>>> + * @data:    this data will pass back to cb
>>>>>>>>>> + *
>>>>>>>>>> + * Return: 0 for success; else the error code is returned
>>>>>>>>>> + *
>>>>>>>>>> + * Trigger CMDQ to asynchronously execute the recorded
>>>>>>>>>> commands and call back
>>>>>>>>>> + * after ISR is finished. Note that this is an ASYNC
>>>>>>>>>> function. When the function
>>>>>>>>>> + * returned, it may or may not be finished. The ISR callback
>>>>>>>>>> function is called
>>>>>>>>>> + * in the end of ISR.
>>>>>>>
>>>>>>> "The callback is called from the ISR."
>>>>>>>
>>>>>>> Regards,
>>>>>>> Matthias
>>>>>>>
>>>>>>>>>> + */
>>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec,
>>>>>>>>>> cmdq_async_flush_cb cb,
>>>>>>>>>> +             void *data);
>>>>>>>>>> +
>>>>>>>>>> +/**
>>>>>>>>>> + * cmdq_rec_destroy() - destroy command queue record
>>>>>>>>>> + * @rec:    the command queue record
>>>>>>>>>> + */
>>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
>>>>>>>>>> +
>>>>>>>>>> +#endif    /* __MTK_CMDQ_H__ */
>>>>>>>>>>
>>>>>>>>
>>>>>>>> Thanks,
>>>>>>>> HS
>>>>>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> HS
>>>>>>
>>>>
>>>>
>>
>>


From matthias.bgg at gmail.com  Tue Jun  7 10:04:27 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Tue, 7 Jun 2016 19:04:27 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
Message-ID: <5756FE9B.6020107@gmail.com>



On 30/05/16 05:19, HS Liao wrote:
> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> CMDQ is used to help read/write registers with critical time limitation,
> such as updating display configuration during the vblank. It controls
> Global Command Engine (GCE) hardware to achieve this requirement.
> Currently, CMDQ only supports display related hardwares, but we expect
> it can be extended to other hardwares for future requirements.
>
> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> ---

[...]

> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> +				   struct cmdq_thread *thread, u32 irq_flag)
> +{
> +	struct cmdq_task *task, *tmp, *curr_task = NULL;
> +	u32 curr_pa;
> +	struct cmdq_cb_data cmdq_cb_data;
> +	bool err;
> +
> +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
> +		err = true;
> +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
> +		err = false;
> +	else
> +		return;
> +
> +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> +
> +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> +				 list_entry) {
> +		if (curr_pa >= task->pa_base &&
> +		    curr_pa < (task->pa_base + task->command_size))
> +			curr_task = task;
> +		if (task->cb.cb) {
> +			cmdq_cb_data.err = curr_task ? err : false;
> +			cmdq_cb_data.data = task->cb.data;
> +			task->cb.cb(cmdq_cb_data);
> +		}

I think this is not right. If we got an IRQ_DONE, then the current task 
is in execution, we should not call the callback until it has finished.

Regards,
Matthias


From hansverk at cisco.com  Tue Jun  7 15:13:02 2016
From: hansverk at cisco.com (Hans Verkuil)
Date: Wed, 8 Jun 2016 07:13:02 +0900
Subject: [PATCH v3 0/9] Add MT8173 Video Decoder Driver
In-Reply-To: <20160607112235.475c2e4c@recife.lan>
References: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
 <20160607112235.475c2e4c@recife.lan>
Message-ID: <575746EE.3030706@cisco.com>



On 06/07/2016 11:22 PM, Mauro Carvalho Chehab wrote:
> Em Mon, 30 May 2016 20:29:14 +0800
> Tiffany Lin <tiffany.lin at mediatek.com> escreveu:
>
>> ==============
>>   Introduction
>> ==============
>>
>> The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
>> Mediatek Video Codec is able to handle video decoding of in a range of formats.
>>
>> This patch series add Mediatek block format V4L2_PIX_FMT_MT21, the decoder driver will decoded bitstream to
>> V4L2_PIX_FMT_MT21 format.
>>
>> This patch series rely on MTK VPU driver in patch series "Add MT8173 Video Encoder Driver and VPU Driver"[1]
>> and patch "CHROMIUM: v4l: Add V4L2_PIX_FMT_VP9 definition"[2] for VP9 support.
>> Mediatek Video Decoder driver rely on VPU driver to load, communicate with VPU.
>>
>> Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI both have been merged in v4.6-rc1.
>>
>> [1]https://patchwork.linuxtv.org/patch/33734/
>> [2]https://chromium-review.googlesource.com/#/c/245241/
>
> Hmm... I'm not seeing the firmware for this driver at the
> linux-firmware tree:
> 	https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/log/
>
> Nor I'm seeing any pull request for them. Did you send it?
> I'll only merge the driver upstream after seeing such pull request.

Mauro, are you confusing the decoder and encoder driver? I haven't thoroughly reviewed the decoder driver
yet, so there is no pull request for the decoder driver.

The only pull request I made was for the encoder driver.

Regards,

	Hans


From davem at davemloft.net  Tue Jun  7 16:01:16 2016
From: davem at davemloft.net (David Miller)
Date: Tue, 07 Jun 2016 16:01:16 -0700 (PDT)
Subject: [PATCH 01/12] net: mediatek: fix DQL support
In-Reply-To: <e1b52f75-edb7-8504-3e6d-40fcb03e39df@phrozen.org>
References: <1465108385-38286-2-git-send-email-john@phrozen.org>
 <20160605.003242.796780762112643713.davem@davemloft.net>
 <e1b52f75-edb7-8504-3e6d-40fcb03e39df@phrozen.org>
Message-ID: <20160607.160116.2076757909377302163.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Mon, 6 Jun 2016 08:43:13 +0200

> i think one solution would be to add some code to have 2 devices share
> the same dql instance. would that be an acceptable solution ?

You still need to address the issue of synchronization.

dql purposefully doesn't use locking, always because a higher level
object (in this case the netdev TX queue) it is contained within
provides the synchronization.

That breaks apart once you share the dql between two netdevs, as you
are proposing here.  You'll have to add locking, which is expensive.

That's why I'm trying to encourage you to think out of the box and
find some way to solve the issue without having to access shared
state shared between multiple devices.

Thanks.



From tom at herbertland.com  Tue Jun  7 16:20:33 2016
From: tom at herbertland.com (Tom Herbert)
Date: Tue, 7 Jun 2016 16:20:33 -0700
Subject: [PATCH 01/12] net: mediatek: fix DQL support
In-Reply-To: <20160607.160116.2076757909377302163.davem@davemloft.net>
References: <1465108385-38286-2-git-send-email-john@phrozen.org>
 <20160605.003242.796780762112643713.davem@davemloft.net>
 <e1b52f75-edb7-8504-3e6d-40fcb03e39df@phrozen.org>
 <20160607.160116.2076757909377302163.davem@davemloft.net>
Message-ID: <CALx6S357KoKbS6tMKUgvHHLowXauZqAXib=bGuJBprwMDFkQ2g@mail.gmail.com>

On Tue, Jun 7, 2016 at 4:01 PM, David Miller <davem at davemloft.net> wrote:
> From: John Crispin <john at phrozen.org>
> Date: Mon, 6 Jun 2016 08:43:13 +0200
>
>> i think one solution would be to add some code to have 2 devices share
>> the same dql instance. would that be an acceptable solution ?
>
> You still need to address the issue of synchronization.
>
> dql purposefully doesn't use locking, always because a higher level
> object (in this case the netdev TX queue) it is contained within
> provides the synchronization.
>
> That breaks apart once you share the dql between two netdevs, as you
> are proposing here.  You'll have to add locking, which is expensive.
>
> That's why I'm trying to encourage you to think out of the box and
> find some way to solve the issue without having to access shared
> state shared between multiple devices.
>

I think you guys mean mean BQL not DQL :-)

If two netdevs share the same DMA ring then is using two netdevs the
right approach. Seems like this would have other consequences beyond
BQL...

Tom

> Thanks.
>


From garlic.tseng at mediatek.com  Tue Jun  7 18:44:37 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Wed, 8 Jun 2016 09:44:37 +0800
Subject: [alsa-devel] [PATCH v2 5/9] ASoC: mediatek: add structure
 define and clock control for 2701
In-Reply-To: <20160607161508.GC7510@sirena.org.uk>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-6-git-send-email-garlic.tseng@mediatek.com>
 <20160607161508.GC7510@sirena.org.uk>
Message-ID: <1465350277.4197.2.camel@mtksdaap41>

On Tue, 2016-06-07 at 17:15 +0100, Mark Brown wrote:
> On Fri, Jun 03, 2016 at 12:56:20PM +0800, Garlic Tseng wrote:
> 
> > +void mt2701_init_clock(struct mtk_base_afe *afe)
> > +{
> > +	struct mt2701_afe_private *afe_priv = afe->platform_priv;
> > +	int i = 0;
> > +
> > +	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
> > +		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
> > +		if (IS_ERR(aud_clks[i]))
> > +			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
> > +				 __func__, aud_clks[i]);
> > +	}
> > +}
> 
> This looks like we just ignore the error but I'm not seeing anything
> that stops us trying to use the clocks or handle deferred probe?

The probe for platform_driver should fail if the clock error occur.
I'll fix it. Thanks.



From garlic.tseng at mediatek.com  Tue Jun  7 19:06:12 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Wed, 8 Jun 2016 10:06:12 +0800
Subject: [alsa-devel] [PATCH v2 2/9] ASoC: mediatek: implement mediatek
 common structure
In-Reply-To: <20160607155427.GB7510@sirena.org.uk>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-3-git-send-email-garlic.tseng@mediatek.com>
 <20160607155427.GB7510@sirena.org.uk>
Message-ID: <1465351572.4197.17.camel@mtksdaap41>

Hi Mark, thank for comment.

On Tue, 2016-06-07 at 16:54 +0100, Mark Brown wrote:
> On Fri, Jun 03, 2016 at 12:56:17PM +0800, Garlic Tseng wrote:
> 
> > +	/*enable agent*/
> 
> Lots of comments with missing spaces in them, there's quite a few
> examples of that in here.

I'll check all the code and fix them. 

> 
> > +EXPORT_SYMBOL(mtk_afe_fe_startup);
> 
> All the ASoC and regmap APIs are _GPL(), you really shouldn't export new
> interfaces based on them without it - the point with the _GPL() is that
> non-GPL code shouldn't be able to use the APIs.

Thanks for comment, I'll fix them.


> 
> > +EXPORT_SYMBOL(mtk_afe_fe_hw_params);
> 
> Do you need to export the individual ops rather than just the ops
> structure?
> 

Yes, in 2701 driver we modify some ops.


+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+	.startup	= mt2701_simple_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mt2701_simple_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+
+};

And here is one of them. Here MT2701 need one more reg control.

+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream
*substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe =
snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+
+	/*single DL use PAIR_INTERLEAVE*/
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_update_bits(afe->regmap,
+				   AFE_MEMIF_PBUF_SIZE,
+				   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+				   AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+	}
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}

MT2701 need some specific reg controls. If the control is
platform-specific I tend not to put them in the common ops structure.


> > +int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
> > +		       struct snd_soc_dai *dai)
> > +{
> > +	struct snd_soc_pcm_runtime *rtd = substream->private_data;
> > +	struct snd_pcm_runtime * const runtime = substream->runtime;
> > +	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
> > +	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
> > +	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
> > +	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
> > +	unsigned int counter = runtime->period_size;
> > +	int fs;
> > +
> > +	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
> 
> That's way too noisy, dev_dbg() at most.

OK. I'll fix it.

> > +static DEFINE_MUTEX(irqs_lock);
> > +int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
> > +{
> > +	int i;
> > +
> > +	mutex_lock(&irqs_lock);
> > +	for (i = 0; i < afe->irqs_size; ++i) {
> 
> Why is the lock global and not part of the AFE struct?

I'll put it in AFE struct. Thanks.

> 
> > +void mtk_simple_isr(struct mtk_base_afe *afe, struct mtk_base_afe_memif *memif)
> > +{
> > +	snd_pcm_period_elapsed(memif->substream);
> > +}
> > +EXPORT_SYMBOL(mtk_simple_isr);
> 
> Is this really worth it over just calling _period_elapsed() directly?

I'll remove the function and just calling period_elapsed() directly.





From garlic.tseng at mediatek.com  Tue Jun  7 20:32:22 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Wed, 8 Jun 2016 11:32:22 +0800
Subject: [alsa-devel] [PATCH v2 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <20160607163112.GD7510@sirena.org.uk>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-7-git-send-email-garlic.tseng@mediatek.com>
 <20160607163112.GD7510@sirena.org.uk>
Message-ID: <1465356742.18601.7.camel@mtksdaap41>

On Tue, 2016-06-07 at 17:31 +0100, Mark Brown wrote:
> On Fri, Jun 03, 2016 at 12:56:21PM +0800, Garlic Tseng wrote:
> 
> > +	if (val < 0 || val > MT2701_I2S_NUM) {
> > +		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
> > +			__func__, num, val);
> > +		return -1;
> 
> Real error codes please.

OK I'll fix it.

> 
> > +static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
> > +	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1,
> > +				    1),
> > +};
> 
> On/off controls should end in Switch.

Do you means that the name should end in Switch? Something like "Multi
ch asrc out3 Switch" (or maybe a shorter one)

I'll fix it (if I don't misunderstand the comment)

Thanks!

> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel




From hs.liao at mediatek.com  Tue Jun  7 22:09:39 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Wed, 8 Jun 2016 13:09:39 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <5756FE9B.6020107@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <5756FE9B.6020107@gmail.com>
Message-ID: <1465362579.9963.2.camel@mtksdaap41>

Hi Matthias,

On Tue, 2016-06-07 at 19:04 +0200, Matthias Brugger wrote:
> 
> On 30/05/16 05:19, HS Liao wrote:
> > This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> > CMDQ is used to help read/write registers with critical time limitation,
> > such as updating display configuration during the vblank. It controls
> > Global Command Engine (GCE) hardware to achieve this requirement.
> > Currently, CMDQ only supports display related hardwares, but we expect
> > it can be extended to other hardwares for future requirements.
> >
> > Signed-off-by: HS Liao <hs.liao at mediatek.com>
> > Signed-off-by: CK Hu <ck.hu at mediatek.com>
> > ---
> 
> [...]
> 
> > +static void cmdq_handle_error_done(struct cmdq *cmdq,
> > +				   struct cmdq_thread *thread, u32 irq_flag)
> > +{
> > +	struct cmdq_task *task, *tmp, *curr_task = NULL;
> > +	u32 curr_pa;
> > +	struct cmdq_cb_data cmdq_cb_data;
> > +	bool err;
> > +
> > +	if (irq_flag & CMDQ_THR_IRQ_ERROR)
> > +		err = true;
> > +	else if (irq_flag & CMDQ_THR_IRQ_DONE)
> > +		err = false;
> > +	else
> > +		return;
> > +
> > +	curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> > +
> > +	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> > +				 list_entry) {
> > +		if (curr_pa >= task->pa_base &&
> > +		    curr_pa < (task->pa_base + task->command_size))
> > +			curr_task = task;
> > +		if (task->cb.cb) {
> > +			cmdq_cb_data.err = curr_task ? err : false;
> > +			cmdq_cb_data.data = task->cb.data;
> > +			task->cb.cb(cmdq_cb_data);
> > +		}
> 
> I think this is not right. If we got an IRQ_DONE, then the current task 
> is in execution, we should not call the callback until it has finished.

Thanks for your finding. This is a bug from CMDQ v6.
I will fix it in next version (CMDQ v9).

> 
> Regards,
> Matthias

Thanks,
HS



From hs.liao at mediatek.com  Tue Jun  7 22:40:27 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Wed, 8 Jun 2016 13:40:27 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <5756FD73.3050607@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
Message-ID: <1465364427.9963.13.camel@mtksdaap41>

Hi Matthias,

On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> 
> On 03/06/16 15:11, Matthias Brugger wrote:
> >
> >
> [...]
> 
> >>>>>>>>>> +
> >>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>> +        }
> >>>>>>>>>> +
> >>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>> task->command_size,
> >>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>> +    }
> >>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>> +    bool err;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>> +        err = true;
> >>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>> +        err = false;
> >>>>>>>>>> +    else
> >>>>>>>>>> +        return;
> >>>>>>>>>> +
> >>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>> +
> >>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>> +                 list_entry) {
> >>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>
> >>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>> commands in the
> >>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>> guarantees
> >>>>>>>>> about dma_handle?
> >>>>>>>>
> >>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>> 2. Yes.
> >>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>> calling
> >>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>> use this
> >>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>> currently
> >>>>>>> executing. So I think this mecanism is not working.
> >>>>>>
> >>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>    From CPU's point of view, tasks are linked by the busy list.
> >>>>>>    From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>
> >>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>
> >>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>> when it is running ISR.
> >>>>>>
> >>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>
> >>>>>
> >>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>> we need to do it in a thread context.
> >>>>
> >>>> Actually, our initial implementation is similar to your suggestion,
> >>>> but display needs CMDQ to return callback function very precisely,
> >>>> else display will drop frame.
> >>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>> and CMDQ needs to call callback function in ISR.
> >>>> If we defer callback to workqueue, the time interval may be larger than
> >>>> 32 ms.sometimes.
> >>>>
> >>>
> >>> I think the problem is, that you implemented the workqueue as a ordered
> >>> workqueue, so there is no parallel processing. I'm still not sure why
> >>> you need the workqueue to be ordered. Can you please explain.
> >>
> >> The order should be kept.
> >> Let me use mouse cursor as an example.
> >> If task 1 means move mouse cursor to point A, task 2 means point B,
> >> and task 3 means point C, our expected result is A -> B -> C.
> >> If the order is not kept, the result could become A -> C -> B.
> >>
> >
> > Got it, thanks for the clarification.
> >
> 
> I think a way to get rid of the workqueue is to use a timer, which gets 
> programmed to the time a timeout in the first task in the busy list 
> would happen. Everytime we update the busy list (e.g. because of task 
> got finished by the thread), we update the timer. When the timer 
> triggers, which hopefully won't happen too often, we return timeout on 
> the busy list elements, until the time is lower then the actual time.
> 
> At least with this we can reduce the data structures in this driver and 
> make it more lightweight.

>From my understanding, your proposed method can handle timeout case.

However, the workqueue is also in charge of releasing tasks.
Do you take releasing tasks into consideration by using the proposed
timer method?
Furthermore, I think the code will become more complex if we also use
timer to implement releasing tasks.

> Best regards,
> Matthias

Thanks,
HS

> >>>>> I keep thinking about how to get rid of the two data structures,
> >>>>> task_busy_list and the task_release_wq. We need the latter for the
> >>>>> only
> >>>>> sake of getting a timeout.
> >>>>>
> >>>>> Did you have a look on how the mailbox framework handles this?
> >>>>> By the way, what is the reason to not implement the whole driver as a
> >>>>> mailbox controller? For me, this driver looks like a good fit.
> >>>>
> >>>> CMDQ needs to encode commands for GCE hardware. We think this behavior
> >>>> should be put in CMDQ driver, and client just call CMDQ functions.
> >>>> Therefore, if we want to use mailbox framework, cmdq_rec must be
> >>>> mailbox client, and the others must be mailbox controller.
> >>>>
> >>>
> >>> You mean the functions to fill the cmdq_rec and execute it?
> >>> I think this should be part of the driver.
> >>
> >> Yes.
> >>
> >>> Jassi, can you have a look on the interface this driver exports [0].
> >>> They are needed to actually create the message which will be send.
> >>> Could something like this be part of a mailbox driver?
> >>>
> >>> [0] https://patchwork.kernel.org/patch/9140221/
> >>>
> >>>> However, if we use mailbox controller, CMDQ driver still needs to
> >>>> control busy list for each GCE thread, and use workqueue to handle
> >>>> timeout tasks.
> >>>>
> >>>
> >>> Let me summarize my ideas around this driver:
> >>> When we enter the ISR, we know that all task in task_busy_list before
> >>> the entry which represents curr_task can be set to TASK_STATE_DONE.
> >>> The curr_task could be TASK_STATE_ERROR if the corresponding bit in the
> >>> irq status registers is set.
> >>> Do we need to call the callback in the same order as the tasks got
> >>> dispatched to the HW thread? If not, we could try to call all this
> >>> callbacks in a multithreaded workqueue.
> >>
> >> Yes, we should keep order.
> >>
> >>> Regards,
> >>> Matthias
> >>
> >> Thanks,
> >> HS
> >>
> >>>> The only thing that we can borrow from mailbox framework is the send
> >>>> (CMDQ flush) and receive (CMDQ callback) interface, However, we don't
> >>>> think we can gain many benefits from it, and we have some overheads to
> >>>> conform to mailbox interface.
> >>>>
> >>>>
> >>>>>
> >>>>>>>>>> +            curr_task = task;
> >>>>>>>>>> +        if (task->cb.cb) {
> >>>>>>>>>> +            cmdq_cb_data.err = curr_task ? err : false;
> >>>>>>>>>> +            cmdq_cb_data.data = task->cb.data;
> >>>>>>>>>> +            task->cb.cb(cmdq_cb_data);
> >>>>>>>>>> +        }
> >>>>>>>>>> +        task->task_state = (curr_task && err) ?
> >>>>>>>>>> TASK_STATE_ERROR :
> >>>>>>>>>> +                TASK_STATE_DONE;
> >>>>>>>>>> +        list_del(&task->list_entry);
> >>>>>>>>>> +        if (curr_task)
> >>>>>>>>>> +            break;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    wake_up(&thread->wait_task_done);
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static void cmdq_thread_irq_handler(struct cmdq *cmdq, int tid)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_thread *thread = &cmdq->thread[tid];
> >>>>>>>>>> +    unsigned long flags = 0L;
> >>>>>>>>>> +    u32 irq_flag;
> >>>>>>>>>> +
> >>>>>>>>>> +    spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>>>>>>> +
> >>>>>>>>>> +    irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>>>>>>> +    cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>>>>>>> +
> >>>>>>>>>> +    /*
> >>>>>>>>>> +     * Another CPU core could run "release task" right before
> >>>>>>>>>> we acquire
> >>>>>>>>>> +     * the spin lock, and thus reset / disable this GCE
> >>>>>>>>>> thread, so we
> >>>>>>>>>> +     * need to check the enable bit of this GCE thread.
> >>>>>>>>>> +     */
> >>>>>>>>>> +    if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>>>>>> +          CMDQ_THR_ENABLED))
> >>>>>>>>>> +        irq_flag = 0;
> >>>>>>>>>
> >>>>>>>>> cmdq_handle_error_done just retuns in this case. Programming
> >>>>>>>>> this way
> >>>>>>>>> just makes things confusing. What about:
> >>>>>>>>>
> >>>>>>>>> if (cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>>>>>           CMDQ_THR_ENABLED)
> >>>>>>>>>     cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>>>>> else
> >>>>>>>>>         irq_flag = 0;
> >>>>>>>>>
> >>>>>>>>> spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>
> >>>>>>>> We still need to clear irq_flag if GCE thread is disabled.
> >>>>>>>> So, I think we can just return here.
> >>>>>>>>
> >>>>>>>>     if (!(cmdq_thread_readl(thread, CMDQ_THR_ENABLE_TASK) &
> >>>>>>>>           CMDQ_THR_ENABLED))
> >>>>>>>>         return;
> >>>>>>>>
> >>>>>>>> What do you think?
> >>>>>>>>
> >>>>>>>
> >>>>>>> No, you can't just return, you need to unlock the spinlock.
> >>>>>>> Anyway I would prefer it the other way round, as I put it in my last
> >>>>>>> mail. Just delete the else branch, we don't need to set irq_flag
> >>>>>>> to zero.
> >>>>>>
> >>>>>> Sorry for my previous wrong reply since I merge your comment
> >>>>>> and CK's comment.
> >>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005547.html
> >>>>>>
> >>>>>> So, I will put this if condition into cmdq_task_handle_error_result()
> >>>>>> and then just return it if GCE thread is disabled.
> >>>>>>
> >>>>>
> >>>>> You mean in cmdq_task_handle_done
> >>>>> We should rename this functions to not create confusion.
> >>>>
> >>>> Sorry again. I mean in cmdq_handle_error_done().
> >>>> This function handles both done and error.
> >>>>
> >>>> I agree the function name looks confusion.
> >>>> I think it can be renamed to cmdq_thread_irq_handler()
> >>>> since it is used to handle irq for GCE thread.
> >>>>
> >>>>> Regards,
> >>>>> Matthias
> >>>>
> >>>> Thanks,
> >>>> HS
> >>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static irqreturn_t cmdq_irq_handler(int irq, void *dev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = dev;
> >>>>>>>>>> +    u32 irq_status;
> >>>>>>>>>> +    int i;
> >>>>>>>>>> +
> >>>>>>>>>> +    irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS);
> >>>>>>>>>> +    irq_status &= CMDQ_IRQ_MASK;
> >>>>>>>>>> +    irq_status ^= CMDQ_IRQ_MASK;
> >>>>>>>>>
> >>>>>>>>> irq_status can be much bigger then 3, which is the number of
> >>>>>>>>> threads in
> >>>>>>>>> the system (CMDQ_THR_MAX_COUNT). So why we use this mask here
> >>>>>>>>> isn't
> >>>>>>>>> clear to me.
> >>>>>>>>
> >>>>>>>> Our GCE hardware has 16 threads, but we only use 3 threads
> >>>>>>>> currently.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Ok, but please use bitops here.
> >>>>>>
> >>>>>> Will use bitops.
> >>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    if (!irq_status)
> >>>>>>>>>> +        return IRQ_NONE;
> >>>>>>>>>> +
> >>>>>>>>>> +    while (irq_status) {
> >>>>>>>>>> +        i = ffs(irq_status) - 1;
> >>>>>>>>>> +        irq_status &= ~BIT(i);
> >>>>>>>>>> +        cmdq_thread_irq_handler(cmdq, i);
> >>>>>>>>>> +    }
> >>>>>>>>>
> >>>>>>>>> Can you explain how the irq status register looks like, that
> >>>>>>>>> would it
> >>>>>>>>> make much easier to understand what happens here.
> >>>>>>>>
> >>>>>>>> Bit 0 ~ 15 of irq status register represents GCE thread 0 ~ 15
> >>>>>>>> interrupt. 0 means asserting interrupt; 1 means no interrupt.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Thanks, that helped. :)
> >>>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +    return IRQ_HANDLED;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_task_handle_error_result(struct cmdq_task *task)
> >>>>>>>>>
> >>>>>>>>> We never check the return values, why do we have them?
> >>>>>>>>
> >>>>>>>> Will drop return value.
> >>>>>>>>
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
> >>>>>>>>>> +    struct device *dev = cmdq->dev;
> >>>>>>>>>> +    struct cmdq_thread *thread = task->thread;
> >>>>>>>>>> +    struct cmdq_task *next_task, *prev_task;
> >>>>>>>>>> +    u32 irq_flag;
> >>>>>>>>>> +
> >>>>>>>>>> +    /* suspend GCE thread to ensure consistency */
> >>>>>>>>>> +    WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
> >>>>>>>>>> +
> >>>>>>>>>> +    /* ISR has handled this error task */
> >>>>>>>>>> +    if (task->task_state == TASK_STATE_ERROR) {
> >>>>>>>>>> +        next_task =
> >>>>>>>>>> list_first_entry_or_null(&thread->task_busy_list,
> >>>>>>>>>> +                struct cmdq_task, list_entry);
> >>>>>>>>>> +        if (next_task) /* move to next task */
> >>>>>>>>>> +            cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>>>>> +                       CMDQ_THR_CURR_ADDR);
> >>>>>>>>>
> >>>>>>>>> We have to do this, as we suppose that the thread did not reach
> >>>>>>>>> the jump
> >>>>>>>>> instruction we put into it's command queue, right?
> >>>>>>>>
> >>>>>>>> Yes.
> >>>>>>>>
> >>>>>>>
> >>>>>>> So this should then go into it's own function. In wait_release_work,
> >>>>>>> something like this:
> >>>>>>>
> >>>>>>> if(task->task_state == TASK_STATE_ERROR)
> >>>>>>>          cmdq_task_handle_error(task)
> >>>>>>
> >>>>>> OK.
> >>>>>> I will write new function cmdq_task_handle_error() to handle error
> >>>>>> case.
> >>>>>>
> >>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>> +        return -ECANCELED;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>
> >>>>>>>>> if task_state != ERROR and != DONE. This means that the timeout of
> >>>>>>>>> task_release_wq has timed out, right?
> >>>>>>>>
> >>>>>>>> Yes.
> >>>>>>>>
> >>>>>>>>>> +    /*
> >>>>>>>>>> +     * Save next_task and prev_task in advance
> >>>>>>>>>> +     * since cmdq_handle_error_done will remove list_entry.
> >>>>>>>>>> +     */
> >>>>>>>>>> +    next_task = prev_task = NULL;
> >>>>>>>>>> +    if (task->list_entry.next != &thread->task_busy_list)
> >>>>>>>>>> +        next_task = list_next_entry(task, list_entry);
> >>>>>>>>>> +    if (task->list_entry.prev != &thread->task_busy_list)
> >>>>>>>>>> +        prev_task = list_prev_entry(task, list_entry);
> >>>>>>>>>> +
> >>>>>>>>>> +    /*
> >>>>>>>>>> +     * Although IRQ is disabled, GCE continues to execute.
> >>>>>>>>>> +     * It may have pending IRQ before GCE thread is suspended,
> >>>>>>>>>> +     * so check this condition again.
> >>>>>>>>>> +     */
> >>>>>>>>>
> >>>>>>>>> The first thing we did in this function was suspending the
> >>>>>>>>> thread. Why
> >>>>>>>>> do we need this then?
> >>>>>>>>
> >>>>>>>> Because timeout is CPU timeout not GCE timeout, GCE could just
> >>>>>>>> finish
> >>>>>>>> this task before the GCE thread is suspended.
> >>>>>>>>
> >>>>>>>
> >>>>>>> What are the reasons for a timeout? An error has happend, or the
> >>>>>>> task is
> >>>>>>> still executing.
> >>>>>>
> >>>>>>    From GCE's point of view, this task is still executing.
> >>>>>> But, it could be an error of client.
> >>>>>> For example, task may never get event if display turn off hardware
> >>>>>> before waiting for task to finish its work.
> >>>>>>
> >>>>>>>>> To be honest this whole functions looks really like a design
> >>>>>>>>> error. We
> >>>>>>>>> have to sperate the states much clearer so that it is possible to
> >>>>>>>>> understand what is happening in the GCE. Isn't it for example
> >>>>>>>>> posible to
> >>>>>>>>> have worker queues for timed out tasks and tasks with an error?
> >>>>>>>>> I'm not
> >>>>>>>>> sure how to do this, actually I'm not sure if I really
> >>>>>>>>> understood how
> >>>>>>>>> this is supposed to work.
> >>>>>>>>
> >>>>>>>> GCE doesn't have timeout. The timeout is decided and controlled
> >>>>>>>> by CPU,
> >>>>>>>> so we check timeout in release work.
> >>>>>>>> For error and done, they are easy to check by register, and we have
> >>>>>>>> already created release work for timeout. So, I don't think we
> >>>>>>>> need to
> >>>>>>>> create work queue for each case.
> >>>>>>>>
> >>>>>>>> What do you think?
> >>>>>>>>
> >>>>>>>
> >>>>>>> I think, if we find in here, that the irq_flag is set, then the the
> >>>>>>> interrupt handler was triggered and is spinning the spinlock. If
> >>>>>>> this is
> >>>>>>> not the case, we have a timeout and we handle this.
> >>>>>>
> >>>>>> I will write another function to handle error, and handle timeout
> >>>>>> here.
> >>>>>>
> >>>>>>>>> How much do we win, when we patch the thread command queue for
> >>>>>>>>> every
> >>>>>>>>> task we add, instead of just taking one task after another from
> >>>>>>>>> the
> >>>>>>>>> task_busy_list?
> >>>>>>>>
> >>>>>>>> GCE is used to help read/write registers with critical time
> >>>>>>>> limitation.
> >>>>>>>> Sometimes, client may ask to process multiple tasks in a short
> >>>>>>>> period
> >>>>>>>> of time, e.g. display flush multiple tasks for next vblank. So,
> >>>>>>>> CMDQ
> >>>>>>>> shouldn't limit to process one task after another from the
> >>>>>>>> task_busy_list. Currently, when interrupt or timeout, we will check
> >>>>>>>> how many tasks are done, and which one is error or timeout.
> >>>>>>>>
> >>>>>>>
> >>>>>>> So I suppose the device driver who use this are interested in
> >>>>>>> throughput
> >>>>>>> and not in latency. The callback of every
> >>>>>>>
> >>>>>>>>>> +    irq_flag = cmdq_thread_readl(thread, CMDQ_THR_IRQ_STATUS);
> >>>>>>>>>> +    cmdq_handle_error_done(cmdq, thread, irq_flag);
> >>>>>>>>>> +    cmdq_thread_writel(thread, ~irq_flag, CMDQ_THR_IRQ_STATUS);
> >>>>>>>>>> +
> >>>>>>>>>> +    if (task->task_state == TASK_STATE_DONE) {
> >>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>> +        return 0;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    if (task->task_state == TASK_STATE_ERROR) {
> >>>>>>>>>> +        dev_err(dev, "task 0x%p error\n", task);
> >>>>>>>>>> +        if (next_task) /* move to next task */
> >>>>>>>>>> +            cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>>>>> +                       CMDQ_THR_CURR_ADDR);
> >>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>> +        return -ECANCELED;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    /* Task is running, so we force to remove it. */
> >>>>>>>>>> +    dev_err(dev, "task 0x%p timeout or killed\n", task);
> >>>>>>>>>> +    task->task_state = TASK_STATE_ERROR;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (prev_task) {
> >>>>>>>>>> +        u64 *prev_va = prev_task->va_base;
> >>>>>>>>>> +        u64 *curr_va = task->va_base;
> >>>>>>>>>> +
> >>>>>>>>>> +        /* copy JUMP instruction */
> >>>>>>>>>> +        prev_va[prev_task->num_cmd - 1] =
> >>>>>>>>>> curr_va[task->num_cmd - 1];
> >>>>>>>>>> +
> >>>>>>>>>> +        cmdq_thread_invalidate_fetched_data(thread);
> >>>>>>>>>> +    } else if (next_task) { /* move to next task */
> >>>>>>>>>> +        cmdq_thread_writel(thread, next_task->pa_base,
> >>>>>>>>>> +                   CMDQ_THR_CURR_ADDR);
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    list_del(&task->list_entry);
> >>>>>>>>>> +    cmdq_thread_resume(thread);
> >>>>>>>>>> +
> >>>>>>>>>> +    /* call cb here to prevent lock */
> >>>>>>>>>> +    if (task->cb.cb) {
> >>>>>>>>>> +        struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>> +
> >>>>>>>>>> +        cmdq_cb_data.err = true;
> >>>>>>>>>> +        cmdq_cb_data.data = task->cb.data;
> >>>>>>>>>> +        task->cb.cb(cmdq_cb_data);
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    return -ECANCELED;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static void cmdq_task_wait_release_work(struct work_struct
> >>>>>>>>>> *work_item)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_task *task = container_of(work_item, struct
> >>>>>>>>>> cmdq_task,
> >>>>>>>>>> +                          release_work);
> >>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
> >>>>>>>>>> +    struct cmdq_thread *thread = task->thread;
> >>>>>>>>>> +    unsigned long flags;
> >>>>>>>>>> +
> >>>>>>>>>> +    wait_event_timeout(thread->wait_task_done,
> >>>>>>>>>> +               task->task_state != TASK_STATE_BUSY,
> >>>>>>>>>> +               msecs_to_jiffies(CMDQ_TIMEOUT_MS));
> >>>>>>>>>> +
> >>>>>>>>>> +    spin_lock_irqsave(&cmdq->exec_lock, flags);
> >>>>>>>>>> +    if (task->task_state != TASK_STATE_DONE)
> >>>>>>>>>> +        cmdq_task_handle_error_result(task);
> >>>>>>>>>> +    if (list_empty(&thread->task_busy_list))
> >>>>>>>>>> +        cmdq_thread_disable(cmdq, thread);
> >>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>> +
> >>>>>>>>>> +    /* release regardless of success or not */
> >>>>>>>>>> +    clk_disable_unprepare(cmdq->clock);
> >>>>>>>>>> +    cmdq_task_release(task);
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static void cmdq_task_wait_release_schedule(struct cmdq_task
> >>>>>>>>>> *task)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = task->cmdq;
> >>>>>>>>>> +
> >>>>>>>>>> +    INIT_WORK(&task->release_work, cmdq_task_wait_release_work);
> >>>>>>>>>> +    queue_work(cmdq->task_release_wq, &task->release_work);
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec,
> >>>>>>>>>> size_t size)
> >>>>>>>>>> +{
> >>>>>>>>>> +    void *new_buf;
> >>>>>>>>>> +
> >>>>>>>>>> +    new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
> >>>>>>>>>> +    if (!new_buf)
> >>>>>>>>>> +        return -ENOMEM;
> >>>>>>>>>> +    rec->buf = new_buf;
> >>>>>>>>>> +    rec->buf_size = size;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_base *cmdq_base;
> >>>>>>>>>> +    struct resource res;
> >>>>>>>>>> +    int subsys;
> >>>>>>>>>> +    u32 base;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (of_address_to_resource(dev->of_node, 0, &res))
> >>>>>>>>>> +        return NULL;
> >>>>>>>>>> +    base = (u32)res.start;
> >>>>>>>>>> +
> >>>>>>>>>> +    subsys = cmdq_subsys_base_to_id(base >> 16);
> >>>>>>>>>> +    if (subsys < 0)
> >>>>>>>>>> +        return NULL;
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base),
> >>>>>>>>>> GFP_KERNEL);
> >>>>>>>>>> +    if (!cmdq_base)
> >>>>>>>>>> +        return NULL;
> >>>>>>>>>> +    cmdq_base->subsys = subsys;
> >>>>>>>>>> +    cmdq_base->base = base;
> >>>>>>>>>> +
> >>>>>>>>>> +    return cmdq_base;
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_register_device);
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>>>>>>> +            struct cmdq_rec **rec_ptr)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_rec *rec;
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    rec = kzalloc(sizeof(*rec), GFP_KERNEL);
> >>>>>>>>>> +    if (!rec)
> >>>>>>>>>> +        return -ENOMEM;
> >>>>>>>>>> +    rec->cmdq = dev_get_drvdata(dev);
> >>>>>>>>>> +    rec->engine_flag = engine_flag;
> >>>>>>>>>> +    err = cmdq_rec_realloc_cmd_buffer(rec,
> >>>>>>>>>> CMDQ_INITIAL_CMD_BLOCK_SIZE);
> >>>>>>>>>
> >>>>>>>>> Just pass PAGE_SIZE here, no need for CMDQ_INITIAL_CMD_BLOCK_SIZE.
> >>>>>>>>
> >>>>>>>> Will do.
> >>>>>>>>
> >>>>>>>>>> +    if (err < 0) {
> >>>>>>>>>> +        kfree(rec);
> >>>>>>>>>> +        return err;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    *rec_ptr = rec;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_create);
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_rec_append_command(struct cmdq_rec *rec, enum
> >>>>>>>>>> cmdq_code code,
> >>>>>>>>>> +                   u32 arg_a, u32 arg_b)
> >>>>>>>>>> +{
> >>>>>>>>>> +    u64 *cmd_ptr;
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (WARN_ON(rec->finalized))
> >>>>>>>>>> +        return -EBUSY;
> >>>>>>>>>> +    if (code < CMDQ_CODE_MOVE || code > CMDQ_CODE_EOC)
> >>>>>>>>>> +        return -EINVAL;
> >>>>>>>>>
> >>>>>>>>> cmdq_rec_append_command is just called from inside this driver
> >>>>>>>>> and code
> >>>>>>>>> is a enum. We can expect it to be correct, no need for this check.
> >>>>>>>>
> >>>>>>>> Will drop this check.
> >>>>>>>>
> >>>>>>>>>> +    if (unlikely(rec->command_size + CMDQ_INST_SIZE >
> >>>>>>>>>> rec->buf_size)) {
> >>>>>>>>>
> >>>>>>>>> command_size is the offset into the buffer to which a new
> >>>>>>>>> command is
> >>>>>>>>> written, so this name is highly confusing. I wonder if this
> >>>>>>>>> would be
> >>>>>>>>> easier to understand if we redefine command_size to something
> >>>>>>>>> like the
> >>>>>>>>> number of commands and divide/multiply CMDQ_INST_SIZE where
> >>>>>>>>> this is needed.
> >>>>>>>>
> >>>>>>>> I can rename command_size to cmd_buf_size and calculate num_cmd by
> >>>>>>>> dividing CMDQ_INST_SIZE.
> >>>>>>>> What do you think?
> >>>>>>>>
> >>>>>>>>>> +        err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size
> >>>>>>>>>> * 2);
> >>>>>>>>>> +        if (err < 0)
> >>>>>>>>>> +            return err;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    cmd_ptr = rec->buf + rec->command_size;
> >>>>>>>>>> +    (*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a)
> >>>>>>>>>> << 32 | arg_b;
> >>>>>>>>>> +    rec->command_size += CMDQ_INST_SIZE;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct
> >>>>>>>>>> cmdq_base *base,
> >>>>>>>>>> +           u32 offset)
> >>>>>>>>>> +{
> >>>>>>>>>> +    u32 arg_a = ((base->base + offset) &
> >>>>>>>>>> CMDQ_ARG_A_WRITE_MASK) |
> >>>>>>>>>> +            ((base->subsys & CMDQ_SUBSYS_MASK) <<
> >>>>>>>>>> CMDQ_SUBSYS_SHIFT);
> >>>>>>>>>
> >>>>>>>>> base->subsys is the id from gce_sybsys, so we can expect it to be
> >>>>>>>>> correct, no need to mask with CMDQ_SUBSYS_MASK.
> >>>>>>>>
> >>>>>>>> Will drop it.
> >>>>>>>>
> >>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE,
> >>>>>>>>>> arg_a, value);
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write);
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>>>>>>> +            struct cmdq_base *base, u32 offset, u32 mask)
> >>>>>>>>>> +{
> >>>>>>>>>> +    u32 offset_mask = offset;
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (mask != 0xffffffff) {
> >>>>>>>>>> +        err = cmdq_rec_append_command(rec, CMDQ_CODE_MOVE, 0,
> >>>>>>>>>> ~mask);
> >>>>>>>>>> +        if (err < 0)
> >>>>>>>>>> +            return err;
> >>>>>>>>>> +        offset_mask |= CMDQ_WRITE_ENABLE_MASK;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    return cmdq_rec_write(rec, value, base, offset_mask);
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_write_mask);
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> >>>>>>>>>> +{
> >>>>>>>>>> +    u32 arg_b;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>>>>>>> +        return -EINVAL;
> >>>>>>>>>> +
> >>>>>>>>>> +    /*
> >>>>>>>>>> +     * bit 0-11: wait value
> >>>>>>>>>> +     * bit 15: 1 - wait, 0 - no wait
> >>>>>>>>>> +     * bit 16-27: update value
> >>>>>>>>>> +     * bit 31: 1 - update, 0 - no update
> >>>>>>>>>> +     */
> >>>>>>>>>
> >>>>>>>>> I don't understand this comments. What are they for?
> >>>>>>>>
> >>>>>>>> This is for WFE command. I will comment it.
> >>>>>>>>
> >>>>>>>>>> +    arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT |
> >>>>>>>>>> CMDQ_WFE_WAIT_VALUE;
> >>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
> >>>>>>>>>> arg_b);
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_wfe);
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum
> >>>>>>>>>> cmdq_event event)
> >>>>>>>>>> +{
> >>>>>>>>>> +    if (event >= CMDQ_MAX_HW_EVENT_COUNT || event < 0)
> >>>>>>>>>> +        return -EINVAL;
> >>>>>>>>>> +
> >>>>>>>>>> +    return cmdq_rec_append_command(rec, CMDQ_CODE_WFE, event,
> >>>>>>>>>> +                       CMDQ_WFE_UPDATE);
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_clear_event);
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_rec_finalize(struct cmdq_rec *rec)
> >>>>>>>>>> +{
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    if (rec->finalized)
> >>>>>>>>>> +        return 0;
> >>>>>>>>>> +
> >>>>>>>>>> +    /* insert EOC and generate IRQ for each command iteration */
> >>>>>>>>>> +    err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0,
> >>>>>>>>>> CMDQ_EOC_IRQ_EN);
> >>>>>>>>>> +    if (err < 0)
> >>>>>>>>>> +        return err;
> >>>>>>>>>> +
> >>>>>>>>>> +    /* JUMP to end */
> >>>>>>>>>> +    err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0,
> >>>>>>>>>> CMDQ_JUMP_PASS);
> >>>>>>>>>> +    if (err < 0)
> >>>>>>>>>> +        return err;
> >>>>>>>>>> +
> >>>>>>>>>
> >>>>>>>>> Does this need to be atomic?
> >>>>>>>>> What happens if after CODE_EOC and before CODE_JUMP some
> >>>>>>>>> write/read/event gets added?
> >>>>>>>>> What happens if more commands get added to the queue after
> >>>>>>>>> CODE_JUMP,
> >>>>>>>>> but before finalized is set to true. Why don't you use atomic
> >>>>>>>>> functions
> >>>>>>>>> to access finalized?
> >>>>>>>>
> >>>>>>>> Since cmdq_rec doesn't guarantee thread safe, mutex is needed when
> >>>>>>>> client uses cmdq_rec.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Well I think that rec->finalized tries to implement this, but might
> >>>>>>> fail, if two kernel threads work on the same cmdq_rec.
> >>>>>>>
> >>>>>>>>>> +    rec->finalized = true;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec,
> >>>>>>>>>> cmdq_async_flush_cb cb,
> >>>>>>>>>> +             void *data)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = rec->cmdq;
> >>>>>>>>>> +    struct cmdq_task *task;
> >>>>>>>>>> +    struct cmdq_task_cb task_cb;
> >>>>>>>>>> +    struct cmdq_thread *thread;
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    mutex_lock(&cmdq->task_mutex);
> >>>>>>>>>> +    if (cmdq->suspended) {
> >>>>>>>>>> +        dev_err(cmdq->dev, "%s is called after suspended\n",
> >>>>>>>>>> __func__);
> >>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
> >>>>>>>>>> +        return -EPERM;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    err = cmdq_rec_finalize(rec);
> >>>>>>>>>> +    if (err < 0) {
> >>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
> >>>>>>>>>> +        return err;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    task_cb.cb = cb;
> >>>>>>>>>> +    task_cb.data = data;
> >>>>>>>>>> +    task = cmdq_task_acquire(rec, task_cb);
> >>>>>>>>>> +    if (!task) {
> >>>>>>>>>> +        mutex_unlock(&cmdq->task_mutex);
> >>>>>>>>>> +        return -EFAULT;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    thread =
> >>>>>>>>>> &cmdq->thread[cmdq_eng_get_thread(task->engine_flag)];
> >>>>>>>>>> +    cmdq_task_exec(task, thread);
> >>>>>>>>>> +    cmdq_task_wait_release_schedule(task);
> >>>>>>>>>> +    mutex_unlock(&cmdq->task_mutex);
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush_async);
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_flush_completion {
> >>>>>>>>>> +    struct completion cmplt;
> >>>>>>>>>> +    bool err;
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_rec_flush_cb(struct cmdq_cb_data data)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_flush_completion *cmplt = data.data;
> >>>>>>>>>> +
> >>>>>>>>>> +    cmplt->err = data.err;
> >>>>>>>>>> +    complete(&cmplt->cmplt);
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_flush_completion cmplt;
> >>>>>>>>>> +    int err;
> >>>>>>>>>> +
> >>>>>>>>>> +    init_completion(&cmplt.cmplt);
> >>>>>>>>>> +    err = cmdq_rec_flush_async(rec, cmdq_rec_flush_cb, &cmplt);
> >>>>>>>>>> +    if (err < 0)
> >>>>>>>>>> +        return err;
> >>>>>>>>>> +    wait_for_completion(&cmplt.cmplt);
> >>>>>>>>>> +    return cmplt.err ? -EFAULT : 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_flush);
> >>>>>>>>>> +
> >>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec)
> >>>>>>>>>> +{
> >>>>>>>>>> +    kfree(rec->buf);
> >>>>>>>>>> +    kfree(rec);
> >>>>>>>>>> +}
> >>>>>>>>>> +EXPORT_SYMBOL(cmdq_rec_destroy);
> >>>>>>>>>> +
> >>>>>>>>>> +static bool cmdq_task_is_empty(struct cmdq *cmdq)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq_thread *thread;
> >>>>>>>>>> +    int i;
> >>>>>>>>>> +
> >>>>>>>>>> +    for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>>>>>>> +        thread = &cmdq->thread[i];
> >>>>>>>>>> +        if (!list_empty(&thread->task_busy_list))
> >>>>>>>>>> +            return false;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    return true;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_suspend(struct device *dev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>>>>>>> +    u32 exec_threads;
> >>>>>>>>>> +
> >>>>>>>>>> +    mutex_lock(&cmdq->task_mutex);
> >>>>>>>>>> +    cmdq->suspended = true;
> >>>>>>>>>> +    mutex_unlock(&cmdq->task_mutex);
> >>>>>>>>>> +
> >>>>>>>>>> +    exec_threads = readl(cmdq->base + CMDQ_CURR_LOADED_THR);
> >>>>>>>>>> +    if ((exec_threads & CMDQ_THR_EXECUTING) &&
> >>>>>>>>>> !cmdq_task_is_empty(cmdq)) {
> >>>>>>>>>> +        dev_err(dev, "wait active tasks timeout.\n");
> >>>>>>>>>> +        flush_workqueue(cmdq->task_release_wq);
> >>>>>>>>>> +    }
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_resume(struct device *dev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = dev_get_drvdata(dev);
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq->suspended = false;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_remove(struct platform_device *pdev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct cmdq *cmdq = platform_get_drvdata(pdev);
> >>>>>>>>>> +
> >>>>>>>>>> +    destroy_workqueue(cmdq->task_release_wq);
> >>>>>>>>>> +    cmdq->task_release_wq = NULL;
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static int cmdq_probe(struct platform_device *pdev)
> >>>>>>>>>> +{
> >>>>>>>>>> +    struct device *dev = &pdev->dev;
> >>>>>>>>>> +    struct device_node *node = dev->of_node;
> >>>>>>>>>> +    struct resource *res;
> >>>>>>>>>> +    struct cmdq *cmdq;
> >>>>>>>>>> +    int err, i;
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
> >>>>>>>>>> +    if (!cmdq)
> >>>>>>>>>> +        return -ENOMEM;
> >>>>>>>>>> +    cmdq->dev = dev;
> >>>>>>>>>> +
> >>>>>>>>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> >>>>>>>>>> +    cmdq->base = devm_ioremap_resource(dev, res);
> >>>>>>>>>> +    if (IS_ERR(cmdq->base)) {
> >>>>>>>>>> +        dev_err(dev, "failed to ioremap gce\n");
> >>>>>>>>>> +        return PTR_ERR(cmdq->base);
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq->irq = irq_of_parse_and_map(node, 0);
> >>>>>>>>>> +    if (!cmdq->irq) {
> >>>>>>>>>> +        dev_err(dev, "failed to get irq\n");
> >>>>>>>>>> +        return -EINVAL;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
> >>>>>>>>>> +        dev, cmdq->base, cmdq->irq);
> >>>>>>>>>> +
> >>>>>>>>>> +    mutex_init(&cmdq->task_mutex);
> >>>>>>>>>> +    spin_lock_init(&cmdq->exec_lock);
> >>>>>>>>>> +    cmdq->task_release_wq = alloc_ordered_workqueue(
> >>>>>>>>>> +            "%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
> >>>>>>>>>> +            "cmdq_task_wait_release");
> >>>>>>>>>> +
> >>>>>>>>>> +    for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
> >>>>>>>>>> +        cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
> >>>>>>>>>> +                CMDQ_THR_SHIFT * i;
> >>>>>>>>>> +        init_waitqueue_head(&cmdq->thread[i].wait_task_done);
> >>>>>>>>>> +        INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    platform_set_drvdata(pdev, cmdq);
> >>>>>>>>>> +
> >>>>>>>>>> +    err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler,
> >>>>>>>>>> IRQF_SHARED,
> >>>>>>>>>> +                   CMDQ_DRIVER_DEVICE_NAME, cmdq);
> >>>>>>>>>> +    if (err < 0) {
> >>>>>>>>>> +        dev_err(dev, "failed to register ISR (%d)\n", err);
> >>>>>>>>>> +        goto fail;
> >>>>>>>>>> +    }
> >>>>>>>>>> +
> >>>>>>>>>> +    cmdq->clock = devm_clk_get(dev, CMDQ_CLK_NAME);
> >>>>>>>>>> +    if (IS_ERR(cmdq->clock)) {
> >>>>>>>>>> +        dev_err(dev, "failed to get clk:%s\n", CMDQ_CLK_NAME);
> >>>>>>>>>> +        err = PTR_ERR(cmdq->clock);
> >>>>>>>>>> +        goto fail;
> >>>>>>>>>> +    }
> >>>>>>>>>> +    return 0;
> >>>>>>>>>> +
> >>>>>>>>>> +fail:
> >>>>>>>>>> +    cmdq_remove(pdev);
> >>>>>>>>>> +    return err;
> >>>>>>>>>> +}
> >>>>>>>>>> +
> >>>>>>>>>> +static const struct dev_pm_ops cmdq_pm_ops = {
> >>>>>>>>>> +    .suspend = cmdq_suspend,
> >>>>>>>>>> +    .resume = cmdq_resume,
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +static const struct of_device_id cmdq_of_ids[] = {
> >>>>>>>>>> +    {.compatible = "mediatek,mt8173-gce",},
> >>>>>>>>>> +    {}
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +static struct platform_driver cmdq_drv = {
> >>>>>>>>>> +    .probe = cmdq_probe,
> >>>>>>>>>> +    .remove = cmdq_remove,
> >>>>>>>>>> +    .driver = {
> >>>>>>>>>> +        .name = CMDQ_DRIVER_DEVICE_NAME,
> >>>>>>>>>> +        .owner = THIS_MODULE,
> >>>>>>>>>> +        .pm = &cmdq_pm_ops,
> >>>>>>>>>> +        .of_match_table = cmdq_of_ids,
> >>>>>>>>>> +    }
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +builtin_platform_driver(cmdq_drv);
> >>>>>>>>>> diff --git a/include/soc/mediatek/cmdq.h
> >>>>>>>>>> b/include/soc/mediatek/cmdq.h
> >>>>>>>>>> new file mode 100644
> >>>>>>>>>> index 0000000..60eef3d
> >>>>>>>>>> --- /dev/null
> >>>>>>>>>> +++ b/include/soc/mediatek/cmdq.h
> >>>>>>>>>> @@ -0,0 +1,197 @@
> >>>>>>>>>> +/*
> >>>>>>>>>> + * Copyright (c) 2015 MediaTek Inc.
> >>>>>>>>>> + *
> >>>>>>>>>> + * This program is free software; you can redistribute it
> >>>>>>>>>> and/or modify
> >>>>>>>>>> + * it under the terms of the GNU General Public License
> >>>>>>>>>> version 2 as
> >>>>>>>>>> + * published by the Free Software Foundation.
> >>>>>>>>>> + *
> >>>>>>>>>> + * This program is distributed in the hope that it will be
> >>>>>>>>>> useful,
> >>>>>>>>>> + * but WITHOUT ANY WARRANTY; without even the implied
> >>>>>>>>>> warranty of
> >>>>>>>>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >>>>>>>>>> + * GNU General Public License for more details.
> >>>>>>>>>> + */
> >>>>>>>>>> +
> >>>>>>>>>> +#ifndef __MTK_CMDQ_H__
> >>>>>>>>>> +#define __MTK_CMDQ_H__
> >>>>>>>>>> +
> >>>>>>>>>> +#include <linux/platform_device.h>
> >>>>>>>>>> +#include <linux/types.h>
> >>>>>>>>>> +
> >>>>>>>>>> +enum cmdq_eng {
> >>>>>>>>>> +    CMDQ_ENG_DISP_AAL,
> >>>>>>>>>> +    CMDQ_ENG_DISP_COLOR0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_COLOR1,
> >>>>>>>>>> +    CMDQ_ENG_DISP_DPI0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_DSI0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_DSI1,
> >>>>>>>>>> +    CMDQ_ENG_DISP_GAMMA,
> >>>>>>>>>> +    CMDQ_ENG_DISP_OD,
> >>>>>>>>>> +    CMDQ_ENG_DISP_OVL0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_OVL1,
> >>>>>>>>>> +    CMDQ_ENG_DISP_PWM0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_PWM1,
> >>>>>>>>>> +    CMDQ_ENG_DISP_RDMA0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_RDMA1,
> >>>>>>>>>> +    CMDQ_ENG_DISP_RDMA2,
> >>>>>>>>>> +    CMDQ_ENG_DISP_UFOE,
> >>>>>>>>>> +    CMDQ_ENG_DISP_WDMA0,
> >>>>>>>>>> +    CMDQ_ENG_DISP_WDMA1,
> >>>>>>>>>> +    CMDQ_ENG_MAX,
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +/* events for CMDQ and display */
> >>>>>>>>>> +enum cmdq_event {
> >>>>>>>>>> +    /* Display start of frame(SOF) events */
> >>>>>>>>>> +    CMDQ_EVENT_DISP_OVL0_SOF = 11,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_OVL1_SOF = 12,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> >>>>>>>>>> +    /* Display end of frame(EOF) events */
> >>>>>>>>>> +    CMDQ_EVENT_DISP_OVL0_EOF = 39,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_OVL1_EOF = 40,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> >>>>>>>>>> +    /* Mutex end of frame(EOF) events */
> >>>>>>>>>> +    CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> >>>>>>>>>> +    CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> >>>>>>>>>> +    CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> >>>>>>>>>> +    CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> >>>>>>>>>> +    CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> >>>>>>>>>> +    /* Display underrun events */
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> >>>>>>>>>> +    CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> >>>>>>>>>> +    /* Keep this at the end of HW events */
> >>>>>>>>>> +    CMDQ_MAX_HW_EVENT_COUNT = 260,
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_cb_data {
> >>>>>>>>>> +    bool    err;
> >>>>>>>>>> +    void    *data;
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +typedef int (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_task;
> >>>>>>>>>> +struct cmdq;
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_rec {
> >>>>>>>>>> +    struct cmdq        *cmdq;
> >>>>>>>>>> +    u64            engine_flag;
> >>>>>>>>>> +    size_t            command_size;
> >>>>>>>>>> +    void            *buf;
> >>>>>>>>>> +    size_t            buf_size;
> >>>>>>>>>> +    bool            finalized;
> >>>>>>>>>> +};
> >>>>>>>
> >>>>>>> Why do we need cmdq_rec at all? Can't we just use the cmdq_task
> >>>>>>> for that
> >>>>>>> and this way make the driver less complex?
> >>>>>>
> >>>>>> There are two main reasons for cmdq_rec.
> >>>>>> 1. It is slow to access dma too frequently.
> >>>>>>       So, we append commands to cacheable memory, and then flush
> >>>>>> to dma.
> >>>>>> 2. cmdq_rec is not thread safe, but cmdq_task needs thread safe.
> >>>>>>       If we merge them, we need to take care of some synchronization
> >>>>>>       issues.
> >>>>>>
> >>>>>>>>>> +
> >>>>>>>>>> +struct cmdq_base {
> >>>>>>>>>> +    int    subsys;
> >>>>>>>>>> +    u32    base;
> >>>>>>>>>
> >>>>>>>>> subsys can always be calculated via cmdq_subsys_base_to_id(base
> >>>>>>>>> >> 16)
> >>>>>>>>> so we can get rid of the struct, right?
> >>>>>>>>
> >>>>>>>> Current subsys method is based on previous comment from Daniel
> >>>>>>>> Kurtz.
> >>>>>>>> Please take a look of our previous discussion.
> >>>>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >>>>>>>>
> >>>>>>>> Thanks.
> >>>>>>>>
> >>>>>>>
> >>>>>>> I have to look deeper into this, but from what I read, the
> >>>>>>> proposal from
> >>>>>>> Daniel [1] seems good to me.
> >>>>>>>
> >>>>>>> [1] https://patchwork.kernel.org/patch/8068311/
> >>>>>>
> >>>>>> The initial proposal has some problem, so please see the follow-up
> >>>>>> discussions. Thanks.
> >>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/003972.html
> >>>>>>
> >>>>>> http://lists.infradead.org/pipermail/linux-mediatek/2016-February/004483.html
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>>>>> +};
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_register_device() - register device which needs CMDQ
> >>>>>>>>>> + * @dev:        device
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: cmdq_base pointer or NULL for failed
> >>>>>>>>>> + */
> >>>>>>>>>> +struct cmdq_base *cmdq_register_device(struct device *dev);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_create() - create command queue record
> >>>>>>>>>> + * @dev:        device
> >>>>>>>>>> + * @engine_flag:    command queue engine flag
> >>>>>>>>>> + * @rec_ptr:        command queue record pointer to retrieve
> >>>>>>>>>> cmdq_rec
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_create(struct device *dev, u64 engine_flag,
> >>>>>>>>>> +            struct cmdq_rec **rec_ptr);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_write() - append write command to the command
> >>>>>>>>>> queue record
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + * @value:    the specified target register value
> >>>>>>>>>> + * @base:    the command queue base
> >>>>>>>>>> + * @offset:    register offset from module base
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
> >>>>>>>>>> +           struct cmdq_base *base, u32 offset);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_write_mask() - append write command with mask to
> >>>>>>>>>> the command
> >>>>>>>>>> + *               queue record
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + * @value:    the specified target register value
> >>>>>>>>>> + * @base:    the command queue base
> >>>>>>>>>> + * @offset:    register offset from module base
> >>>>>>>>>> + * @mask:    the specified target register mask
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
> >>>>>>>>>> +            struct cmdq_base *base, u32 offset, u32 mask);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_wfe() - append wait for event command to the
> >>>>>>>>>> command queue reco    rd
> >>>>>>>>>
> >>>>>>>>> reco rd -> record
> >>>>>>>>
> >>>>>>>> Will fix it.
> >>>>>>>>
> >>>>>>>>> Regards,
> >>>>>>>>> Matthias
> >>>>>>>>>
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + * @event:    the desired event type to "wait and CLEAR"
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_clear_event() - append clear event command to the
> >>>>>>>>>> command queue
> >>>>>>>>>> + *                record
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + * @event:    the desired event to be cleared
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_clear_event(struct cmdq_rec *rec, enum
> >>>>>>>>>> cmdq_event event);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_flush() - trigger CMDQ to execute the recorded
> >>>>>>>>>> commands
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + *
> >>>>>>>>>> + * Trigger CMDQ to execute the recorded commands. Note that
> >>>>>>>>>> this is a
> >>>>>>>>>> + * synchronous flush function. When the function returned,
> >>>>>>>>>> the recorded
> >>>>>>>>>> + * commands have been done.
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_flush(struct cmdq_rec *rec);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_flush_async() - trigger CMDQ to asynchronously
> >>>>>>>>>> execute the recorded
> >>>>>>>>>> + *                commands and call back after ISR is finished
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + * @cb:        called in the end of CMDQ ISR
> >>>>>>>>>> + * @data:    this data will pass back to cb
> >>>>>>>>>> + *
> >>>>>>>>>> + * Return: 0 for success; else the error code is returned
> >>>>>>>>>> + *
> >>>>>>>>>> + * Trigger CMDQ to asynchronously execute the recorded
> >>>>>>>>>> commands and call back
> >>>>>>>>>> + * after ISR is finished. Note that this is an ASYNC
> >>>>>>>>>> function. When the function
> >>>>>>>>>> + * returned, it may or may not be finished. The ISR callback
> >>>>>>>>>> function is called
> >>>>>>>>>> + * in the end of ISR.
> >>>>>>>
> >>>>>>> "The callback is called from the ISR."
> >>>>>>>
> >>>>>>> Regards,
> >>>>>>> Matthias
> >>>>>>>
> >>>>>>>>>> + */
> >>>>>>>>>> +int cmdq_rec_flush_async(struct cmdq_rec *rec,
> >>>>>>>>>> cmdq_async_flush_cb cb,
> >>>>>>>>>> +             void *data);
> >>>>>>>>>> +
> >>>>>>>>>> +/**
> >>>>>>>>>> + * cmdq_rec_destroy() - destroy command queue record
> >>>>>>>>>> + * @rec:    the command queue record
> >>>>>>>>>> + */
> >>>>>>>>>> +void cmdq_rec_destroy(struct cmdq_rec *rec);
> >>>>>>>>>> +
> >>>>>>>>>> +#endif    /* __MTK_CMDQ_H__ */
> >>>>>>>>>>
> >>>>>>>>
> >>>>>>>> Thanks,
> >>>>>>>> HS
> >>>>>>>>
> >>>>>>
> >>>>>> Thanks,
> >>>>>> HS
> >>>>>>
> >>>>
> >>>>
> >>
> >>




From weiqing.kong at mediatek.com  Tue Jun  7 23:48:52 2016
From: weiqing.kong at mediatek.com (weiqing kong)
Date: Wed, 8 Jun 2016 14:48:52 +0800
Subject: [PATCH 1/3] dt-bindings: pwm: Add MediaTek display PWM bindings
In-Reply-To: <57519C59.5040107@gmail.com>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-2-git-send-email-weiqing.kong@mediatek.com>
 <20160602224550.GA19256@rob-hp-laptop> <1464936349.5209.9.camel@mhfsdcap03>
 <57519C59.5040107@gmail.com>
Message-ID: <1465368532.5209.15.camel@mhfsdcap03>

On Fri, 2016-06-03 at 17:03 +0200, Matthias Brugger wrote:
> 
> On 03/06/16 08:45, weiqing kong wrote:
> > On Thu, 2016-06-02 at 17:45 -0500, Rob Herring wrote:
> >> On Mon, May 30, 2016 at 04:41:50PM +0800, Weiqing Kong wrote:
> >>> Add MT2701 compatible string.
> >>>
> >>> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> >>> ---
> >>>   Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt | 5 +++--
> >>>   1 file changed, 3 insertions(+), 2 deletions(-)
> >>>
> >>> diff --git a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> >>> index f8f59ba..f2f2fa9 100644
> >>> --- a/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> >>> +++ b/Documentation/devicetree/bindings/pwm/pwm-mtk-disp.txt
> >>> @@ -1,9 +1,10 @@
> >>>   MediaTek display PWM controller
> >>>
> >>>   Required properties:
> >>> - - compatible: should be "mediatek,<name>-disp-pwm":
> >>> -   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
> >>> + - compatible: should be like these:
> >>> +   - "mediatek,mt2701-disp-bls": found on mt2701 SoC.
> >>
> >> Why bls instead of pwm?
> >
> > In mt6595/mt8173, we have a hardware module named pwm.
> > In mt2701, we have bls hardware module which includes disp pwm function,
> > so we named it mt2701-disp-bls rather than mt2701-disp-pwm in order to
> > match with hardware spec.
> > thanks.
> >
> 
>  From what I understand pwm module and disp pwm in mt6589/mt8173 is 
> independent from the disp pwm. Actually the disp pwm in mt6589 is part 
> of the bls module, so this should me mediatek,mt2701-disp-pwm as well
> 
> Thanks,
> Matthias
> 

ok, I will modify it into mediatek,mt2701-disp-pwm. 

> >>
> >>>      - "mediatek,mt6595-disp-pwm": found on mt6595 SoC.
> >>> +   - "mediatek,mt8173-disp-pwm": found on mt8173 SoC.
> >>>    - reg: physical base address and length of the controller's registers.
> >>>    - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
> >>>      the cell format.
> >>> --
> >>> 1.8.1.1.dirty
> >>>
> >
> >




From weiqing.kong at mediatek.com  Wed Jun  8 00:21:18 2016
From: weiqing.kong at mediatek.com (weiqing kong)
Date: Wed, 8 Jun 2016 15:21:18 +0800
Subject: [PATCH 2/3] pwm: Add MediaTek MT2701 display PWM driver support
In-Reply-To: <5751A3DD.7020402@gmail.com>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-3-git-send-email-weiqing.kong@mediatek.com>
 <5751A3DD.7020402@gmail.com>
Message-ID: <1465370478.5209.30.camel@mhfsdcap03>

On Fri, 2016-06-03 at 17:35 +0200, Matthias Brugger wrote:
> 
> On 30/05/16 10:41, Weiqing Kong wrote:
> > Use the mtk_pwm_data struction to define different registers
> > and add MT2701 specific register operations, such as MT2701
> > doesn't have commit register, needs to disable double buffer
> > before writing register, and needs to select manual mode
> > and use PWM_PERIOD/PWM_HIGH_WIDTH.
> >
> > Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> > ---
> >   drivers/pwm/pwm-mtk-disp.c | 89 +++++++++++++++++++++++++++++++++++++---------
> >   1 file changed, 72 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
> > index 0ad3385..03b9c9e 100644
> > --- a/drivers/pwm/pwm-mtk-disp.c
> > +++ b/drivers/pwm/pwm-mtk-disp.c
> > @@ -18,33 +18,44 @@
> >   #include <linux/io.h>
> >   #include <linux/module.h>
> >   #include <linux/of.h>
> > +#include <linux/of_device.h>
> >   #include <linux/platform_device.h>
> >   #include <linux/pwm.h>
> >   #include <linux/slab.h>
> >
> >   #define DISP_PWM_EN		0x00
> > -#define PWM_ENABLE_MASK		BIT(0)
> >
> >   #define DISP_PWM_COMMIT		0x08
> >   #define PWM_COMMIT_MASK		BIT(0)
> >
> > -#define DISP_PWM_CON_0		0x10
> >   #define PWM_CLKDIV_SHIFT	16
> >   #define PWM_CLKDIV_MAX		0x3ff
> >   #define PWM_CLKDIV_MASK		(PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
> >
> > -#define DISP_PWM_CON_1		0x14
> >   #define PWM_PERIOD_BIT_WIDTH	12
> >   #define PWM_PERIOD_MASK		((1 << PWM_PERIOD_BIT_WIDTH) - 1)
> >
> >   #define PWM_HIGH_WIDTH_SHIFT	16
> >   #define PWM_HIGH_WIDTH_MASK	(0x1fff << PWM_HIGH_WIDTH_SHIFT)
> >
> > +#define MT2701_PWM_MANUAL_SEL_MASK	BIT(1)
> > +#define MT2701_PWM_BLS_DEBUG		0xb0
> 
> Do we need to set magic 0x3 in the debug register? If so, this should be 
> part of mtk_pwm_data, just as con0_sel.

Do you mean that add bls_debug into struct mtk_pwm_data,
and set 0x3 in mt2701 data, 0x0 in mt8173 data?

> 
> > +#define MT2701_PWM_BLS_DEBUG_MASK	0x3
> > +
> > +struct mtk_pwm_data {
> > +	unsigned int enable_bit;
> > +	unsigned int con0;
> > +	unsigned int con0_sel;
> > +	unsigned int con1;
> > +	bool have_commit_reg;
> > +};
> > +
> >   struct mtk_disp_pwm {
> >   	struct pwm_chip chip;
> >   	struct clk *clk_main;
> >   	struct clk *clk_mm;
> >   	void __iomem *base;
> > +	const struct mtk_pwm_data *data;
> 
> Couldn't that just be the offset for the commit register and set it to 
> 0x0 if the commit register is not present. Right now, we suppose the 
> DISP_PWM_EN is at offset 0x0, so this should not be a problem.
> 

Do you mean that replace have_commit_reg with commit_reg_offset?
just like this?
bool have_commit_reg;
->
unsigned int commit_reg_offset;
and set commit_reg_offset 0x0 in mt2701, 0x1 in mt8173?

> >   };
> >
> >   static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
> > @@ -106,12 +117,18 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> >   		return err;
> >   	}
> >
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
> > +	mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> > +				 PWM_CLKDIV_MASK,
> >   				 clk_div << PWM_CLKDIV_SHIFT);
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
> > -				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
> > +	mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
> > +				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
> > +				 value);
> > +	if (mdp->data->have_commit_reg) {
> > +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> > +					 PWM_COMMIT_MASK, 0x1);
> > +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> > +					 PWM_COMMIT_MASK, 0x0);
> 
> should be renamed to MT8173_PWM_COMMIT_MASK, although I'm not really a 
> friend of mixing up SoC specific defines with a Soc specific 
> mtk_disp_pwm structure.
> 
> Regards,
> Matthias

ok, I will modify it as you said. thanks for you suggestion.

> 
> > +	}
> >
> >   	clk_disable(mdp->clk_mm);
> >   	clk_disable(mdp->clk_main);
> > @@ -134,7 +151,9 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> >   		return err;
> >   	}
> >
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
> > +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> > +				 mdp->data->enable_bit,
> > +				 mdp->data->enable_bit);
> >
> >   	return 0;
> >   }
> > @@ -143,7 +162,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> >   {
> >   	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
> >
> > -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
> > +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> > +				 mdp->data->enable_bit, 0x0);
> >
> >   	clk_disable(mdp->clk_mm);
> >   	clk_disable(mdp->clk_main);
> > @@ -156,12 +176,41 @@ static const struct pwm_ops mtk_disp_pwm_ops = {
> >   	.owner = THIS_MODULE,
> >   };
> >
> > +static const struct mtk_pwm_data mt8173_pwm_data = {
> > +	.enable_bit = BIT(0),
> > +	.con0 = 0x10,
> > +	.con0_sel = 0x0,
> > +	.con1 = 0x14,
> > +	.have_commit_reg = true,
> > +};
> > +
> > +static const struct mtk_pwm_data mt2701_pwm_data = {
> > +	.enable_bit = BIT(16),
> > +	.con0 = 0xa8,
> > +	.con0_sel = 0x2,
> > +	.con1 = 0xac,
> > +	.have_commit_reg = false,
> > +};
> > +
> > +static const struct of_device_id mtk_disp_pwm_of_match[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-bls", .data = &mt2701_pwm_data},
> > +	{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
> > +	{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> > +
> >   static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >   {
> > +	const struct of_device_id *id;
> >   	struct mtk_disp_pwm *mdp;
> >   	struct resource *r;
> >   	int ret;
> >
> > +	id = of_match_device(mtk_disp_pwm_of_match, &pdev->dev);
> > +	if (!id)
> > +		return -EINVAL;
> > +
> >   	mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL);
> >   	if (!mdp)
> >   		return -ENOMEM;
> > @@ -191,6 +240,7 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >   	mdp->chip.ops = &mtk_disp_pwm_ops;
> >   	mdp->chip.base = -1;
> >   	mdp->chip.npwm = 1;
> > +	mdp->data = id->data;
> >
> >   	ret = pwmchip_add(&mdp->chip);
> >   	if (ret < 0) {
> > @@ -200,6 +250,18 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >
> >   	platform_set_drvdata(pdev, mdp);
> >
> > +	/*
> > +	 * For MT2701, disable double buffer before writing register
> > +	 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
> > +	 */
> > +	if (!mdp->data->have_commit_reg) {
> > +		mtk_disp_pwm_update_bits(mdp, MT2701_PWM_BLS_DEBUG,
> > +					 MT2701_PWM_BLS_DEBUG_MASK, 0x3);
> > +		mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> > +					 MT2701_PWM_MANUAL_SEL_MASK,
> > +					 mdp->data->con0_sel);
> > +	}
> > +
> >   	return 0;
> >
> >   disable_clk_mm:
> > @@ -221,13 +283,6 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
> >   	return ret;
> >   }
> >
> > -static const struct of_device_id mtk_disp_pwm_of_match[] = {
> > -	{ .compatible = "mediatek,mt8173-disp-pwm" },
> > -	{ .compatible = "mediatek,mt6595-disp-pwm" },
> > -	{ }
> > -};
> > -MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> > -
> >   static struct platform_driver mtk_disp_pwm_driver = {
> >   	.driver = {
> >   		.name = "mediatek-disp-pwm",
> >




From linus.walleij at linaro.org  Wed Jun  8 01:10:19 2016
From: linus.walleij at linaro.org (Linus Walleij)
Date: Wed, 8 Jun 2016 10:10:19 +0200
Subject: [PATCH] pinctrl: mediatek: fix suspend/resume timing issue
In-Reply-To: <1464924495-20375-1-git-send-email-hongkun.cao@mediatek.com>
References: <1464924495-20375-1-git-send-email-hongkun.cao@mediatek.com>
Message-ID: <CACRpkdZ18pvNBLp1L4wBh2BnwODaju4mTwCjuK-Bnp-Rc5feqA@mail.gmail.com>

On Fri, Jun 3, 2016 at 5:28 AM,  <hongkun.cao at mediatek.com> wrote:

> From: "hongkun.cao" <hongkun.cao at mediatek.com>
>
> An irq which is a wake up source maybe masked unexpectedly if the wake
> up source irq was triggered after pinctrl irqchip suspend and before
> suspend_device_irqs finished.
> Use *_noirq callbacks to guarantee pinctrl irqchip suspend would be
> called after suspend_devices_irqs.
>
> Signed-off-by: hongkun.cao <hongkun.cao at mediatek.com>

No rreaction from the other maintainers but it looks correct to me
so patch applied.

Yours,
Linus Walleij


From vincent.abriou at st.com  Wed Jun  8 02:35:40 2016
From: vincent.abriou at st.com (Vincent ABRIOU)
Date: Wed, 8 Jun 2016 11:35:40 +0200
Subject: [PATCH v2 11/20] drm: sti: Rely on the default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-12-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-12-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <5757E6EC.1050802@st.com>

Hi Boris,

Thanks for the patch.

Acked-by: Vincent Abriou <vincent.abriou at st.com>

Vincent

On 06/07/2016 01:48 PM, Boris Brezillon wrote:
> All outputs have a 1:1 relationship between connectors and encoders
> and the driver is relying on the atomic helpers: we can drop the custom
> ->best_encoder() implementations and let the core call
> drm_atomic_helper_best_encoder() for us.
>
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>   drivers/gpu/drm/sti/sti_dvo.c  | 10 ----------
>   drivers/gpu/drm/sti/sti_hda.c  | 10 ----------
>   drivers/gpu/drm/sti/sti_hdmi.c | 10 ----------
>   3 files changed, 30 deletions(-)
>
> diff --git a/drivers/gpu/drm/sti/sti_dvo.c b/drivers/gpu/drm/sti/sti_dvo.c
> index 25f7663..d5627d1 100644
> --- a/drivers/gpu/drm/sti/sti_dvo.c
> +++ b/drivers/gpu/drm/sti/sti_dvo.c
> @@ -384,20 +384,10 @@ static int sti_dvo_connector_mode_valid(struct drm_connector *connector,
>   	return MODE_OK;
>   }
>
> -struct drm_encoder *sti_dvo_best_encoder(struct drm_connector *connector)
> -{
> -	struct sti_dvo_connector *dvo_connector
> -		= to_sti_dvo_connector(connector);
> -
> -	/* Best encoder is the one associated during connector creation */
> -	return dvo_connector->encoder;
> -}
> -
>   static const
>   struct drm_connector_helper_funcs sti_dvo_connector_helper_funcs = {
>   	.get_modes = sti_dvo_connector_get_modes,
>   	.mode_valid = sti_dvo_connector_mode_valid,
> -	.best_encoder = sti_dvo_best_encoder,
>   };
>
>   static enum drm_connector_status
> diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c
> index f7d3464..c4649f5 100644
> --- a/drivers/gpu/drm/sti/sti_hda.c
> +++ b/drivers/gpu/drm/sti/sti_hda.c
> @@ -676,20 +676,10 @@ static int sti_hda_connector_mode_valid(struct drm_connector *connector,
>   	return MODE_OK;
>   }
>
> -struct drm_encoder *sti_hda_best_encoder(struct drm_connector *connector)
> -{
> -	struct sti_hda_connector *hda_connector
> -		= to_sti_hda_connector(connector);
> -
> -	/* Best encoder is the one associated during connector creation */
> -	return hda_connector->encoder;
> -}
> -
>   static const
>   struct drm_connector_helper_funcs sti_hda_connector_helper_funcs = {
>   	.get_modes = sti_hda_connector_get_modes,
>   	.mode_valid = sti_hda_connector_mode_valid,
> -	.best_encoder = sti_hda_best_encoder,
>   };
>
>   static enum drm_connector_status
> diff --git a/drivers/gpu/drm/sti/sti_hdmi.c b/drivers/gpu/drm/sti/sti_hdmi.c
> index 6ef0715..dc9ab6e 100644
> --- a/drivers/gpu/drm/sti/sti_hdmi.c
> +++ b/drivers/gpu/drm/sti/sti_hdmi.c
> @@ -897,20 +897,10 @@ static int sti_hdmi_connector_mode_valid(struct drm_connector *connector,
>   	return MODE_OK;
>   }
>
> -struct drm_encoder *sti_hdmi_best_encoder(struct drm_connector *connector)
> -{
> -	struct sti_hdmi_connector *hdmi_connector
> -		= to_sti_hdmi_connector(connector);
> -
> -	/* Best encoder is the one associated during connector creation */
> -	return hdmi_connector->encoder;
> -}
> -
>   static const
>   struct drm_connector_helper_funcs sti_hdmi_connector_helper_funcs = {
>   	.get_modes = sti_hdmi_connector_get_modes,
>   	.mode_valid = sti_hdmi_connector_mode_valid,
> -	.best_encoder = sti_hdmi_best_encoder,
>   };
>
>   /* get detection status of display device */
>

From honghui.zhang at mediatek.com  Wed Jun  8 02:50:44 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:50:44 +0800
Subject: [PATCH] iommu/mediatek: Do not call of_node_put in mtk_iommu_of_xlate
Message-ID: <1465379444-14719-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

The device_node will be released in of_iommu_configure, it may be double
released if call of_node_put in mtk_iommu_of_xlate.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
---
 drivers/iommu/mtk_iommu.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index c3043d8..493bd3e 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -455,7 +455,6 @@ static int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
 	if (!dev->archdata.iommu) {
 		/* Get the m4u device */
 		m4updev = of_find_device_by_node(args->np);
-		of_node_put(args->np);
 		if (WARN_ON(!m4updev))
 			return -EINVAL;
 
-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:50:56 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:50:56 +0800
Subject: [PATCH v4 0/5] MT2701 iommu support
Message-ID: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

  Mediatek's m4u(Multimedia Memory Management Unit) and SMI(Smart
Multimedia Interface)have two generations HW. They basically sharing the
same hardware block diagram, but have some difference as below:

  Generation one m4u only supports one layer, flat pagetable addressing,
and only supports 4K size page mapping. While generation two m4u supports 2
levels of pagetable which uses the ARM short-descriptor translation table
format for address translation.
They have slight different register base and register offset.
They have very different HW ports defines.

  Generaion one SMI has additional "async" clock which transform the smi
clock into emi clock domain, this clock should be prepared and enabled for
SMI generation one HW.
The register which control the iommu need to translation the address or not
for a particular port is located at smi ao base(smi always on register
base) for generation one SMI HW, but located at each larb's register base
for generation two HW.

This patch set add mt2701 iommu support, it's based on 4.7-rc1 and James
Liao's "Add clock support for Mediatek MT2701 v8[1]" and "Mediatek MT2701
SCPSYS power domain support v7[2]" patch.

v4:
-Rebase on 4.7-rc1.
-Do not call of_node_put call in mtk_iommu_create_mapping.

v3: https://lists.linuxfoundation.org/pipermail/iommu/2016-May/017179.html
-Rebase on "of: Implement iterator for phandles[3]" and take use of
 of_for_each_phandle.
-Forward-declare mtk_iommu_domain and implement the struct separately.
-Free the pagetable memory in mtk_iommu_domain_free
-Roll back the mapping in error case.
-Minor cleanups.

v2: https://lists.linuxfoundation.org/pipermail/iommu/2016-May/017068.html
-Fix syntax errors in dt-bindings.
-Use dma_alloc/free_coherent to allocate pagetable memory and reduce the
 streaming DMA stuff.
-Make the mtk_iommu_ops.pgsize_bitmap as ~0UL << MT2701_IOMMU_PAGE_SHIFT.
-Use macro instead of variable to indicate the pagetable size.
-Change some macro name from MTK_XXX to MT2701_XXX.

v1: http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005301.html
-initial version

[1] http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005439.html
[2] http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005429.html
[3] https://lists.linuxfoundation.org/pipermail/iommu/2016-April/016300.html

Honghui Zhang (5):
  dt-bindings: mediatek: add descriptions for mediatek mt2701 iommu and
    smi
  iommu/mediatek: move the common struct into header file
  memory/mediatek: add support for mt2701
  iommu/mediatek: add support for mtk iommu generation one HW
  ARM: dts: mt2701: add iommu/smi dtsi node for mt2701

 .../devicetree/bindings/iommu/mediatek,iommu.txt   |  13 +-
 .../memory-controllers/mediatek,smi-common.txt     |  21 +-
 .../memory-controllers/mediatek,smi-larb.txt       |   4 +-
 arch/arm/boot/dts/mt2701.dtsi                      |  51 ++
 drivers/iommu/Kconfig                              |  18 +
 drivers/iommu/Makefile                             |   1 +
 drivers/iommu/mtk_iommu.c                          |  48 +-
 drivers/iommu/mtk_iommu.h                          |  77 +++
 drivers/iommu/mtk_iommu_v1.c                       | 727 +++++++++++++++++++++
 drivers/memory/mtk-smi.c                           | 167 ++++-
 include/dt-bindings/memory/mt2701-larb-port.h      |  85 +++
 11 files changed, 1139 insertions(+), 73 deletions(-)
 create mode 100644 drivers/iommu/mtk_iommu.h
 create mode 100644 drivers/iommu/mtk_iommu_v1.c
 create mode 100644 include/dt-bindings/memory/mt2701-larb-port.h

-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:51:01 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:51:01 +0800
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for mt2701
In-Reply-To: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

Add the dtsi node of iommu and smi for mt2701.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
---
 arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 42d5a37..363de0d 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -16,6 +16,7 @@
 #include <dt-bindings/power/mt2701-power.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/memory/mt2701-larb-port.h>
 #include "skeleton64.dtsi"
 #include "mt2701-pinfunc.h"
 
@@ -160,6 +161,16 @@
 		clock-names = "system-clk", "rtc-clk";
 	};
 
+	smi_common: smi at 1000c000 {
+		compatible = "mediatek,mt2701-smi-common";
+		reg = <0 0x1000c000 0 0x1000>;
+		clocks = <&infracfg CLK_INFRA_SMI>,
+			 <&mmsys CLK_MM_SMI_COMMON>,
+			 <&infracfg CLK_INFRA_SMI>;
+		clock-names = "apb", "smi", "async";
+		power-domains = <&scpsys MT2701_POWER_DOMAIN_DISP>;
+	};
+
 	sysirq: interrupt-controller at 10200100 {
 		compatible = "mediatek,mt2701-sysirq",
 			     "mediatek,mt6577-sysirq";
@@ -169,6 +180,16 @@
 		reg = <0 0x10200100 0 0x1c>;
 	};
 
+	iommu: mmsys_iommu at 10205000 {
+		compatible = "mediatek,mt2701-m4u";
+		reg = <0 0x10205000 0 0x1000>;
+		interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_M4U>;
+		clock-names = "bclk";
+		mediatek,larbs = <&larb0 &larb1 &larb2>;
+		#iommu-cells = <1>;
+	};
+
 	apmixedsys: syscon at 10209000 {
 		compatible = "mediatek,mt2701-apmixedsys", "syscon";
 		reg = <0 0x10209000 0 0x1000>;
@@ -234,6 +255,16 @@
 		status = "disabled";
 	};
 
+	larb0: larb at 14010000 {
+		compatible = "mediatek,mt2701-smi-larb";
+		reg = <0 0x14010000 0 0x1000>;
+		mediatek,smi = <&smi_common>;
+		clocks = <&mmsys CLK_MM_SMI_LARB0>,
+			 <&mmsys CLK_MM_SMI_LARB0>;
+		clock-names = "apb", "smi";
+		power-domains = <&scpsys MT2701_POWER_DOMAIN_DISP>;
+	};
+
 	imgsys: syscon at 15000000 {
 		compatible = "mediatek,mt2701-imgsys", "syscon";
 		reg = <0 0x15000000 0 0x1000>;
@@ -241,6 +272,16 @@
 		status = "disabled";
 	};
 
+	larb2: larb at 15001000 {
+		compatible = "mediatek,mt2701-smi-larb";
+		reg = <0 0x15001000 0 0x1000>;
+		mediatek,smi = <&smi_common>;
+		clocks = <&imgsys CLK_IMG_SMI_COMM>,
+			 <&imgsys CLK_IMG_SMI_COMM>;
+		clock-names = "apb", "smi";
+		power-domains = <&scpsys MT2701_POWER_DOMAIN_ISP>;
+	};
+
 	vdecsys: syscon at 16000000 {
 		compatible = "mediatek,mt2701-vdecsys", "syscon";
 		reg = <0 0x16000000 0 0x1000>;
@@ -248,6 +289,16 @@
 		status = "disabled";
 	};
 
+	larb1: larb at 16010000 {
+		compatible = "mediatek,mt2701-smi-larb";
+		reg = <0 0x16010000 0 0x1000>;
+		mediatek,smi = <&smi_common>;
+		clocks = <&vdecsys CLK_VDEC_CKGEN>,
+			 <&vdecsys CLK_VDEC_LARB>;
+		clock-names = "apb", "smi";
+		power-domains = <&scpsys MT2701_POWER_DOMAIN_VDEC>;
+	};
+
 	hifsys: syscon at 1a000000 {
 		compatible = "mediatek,mt2701-hifsys", "syscon";
 		reg = <0 0x1a000000 0 0x1000>;
-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:50:58 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:50:58 +0800
Subject: [PATCH v4 2/5] iommu/mediatek: move the common struct into header file
In-Reply-To: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465379461-14757-3-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

Move the struct defines of mtk iommu into a new header files for
common use.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
---
 drivers/iommu/mtk_iommu.c | 48 +----------------------------
 drivers/iommu/mtk_iommu.h | 77 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 47 deletions(-)
 create mode 100644 drivers/iommu/mtk_iommu.h

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index c3043d8..4f18ff5 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -34,7 +34,7 @@
 #include <dt-bindings/memory/mt8173-larb-port.h>
 #include <soc/mediatek/smi.h>
 
-#include "io-pgtable.h"
+#include "mtk_iommu.h"
 
 #define REG_MMU_PT_BASE_ADDR			0x000
 
@@ -93,20 +93,6 @@
 
 #define MTK_PROTECT_PA_ALIGN			128
 
-struct mtk_iommu_suspend_reg {
-	u32				standard_axi_mode;
-	u32				dcm_dis;
-	u32				ctrl_reg;
-	u32				int_control0;
-	u32				int_main_control;
-};
-
-struct mtk_iommu_client_priv {
-	struct list_head		client;
-	unsigned int			mtk_m4u_id;
-	struct device			*m4udev;
-};
-
 struct mtk_iommu_domain {
 	spinlock_t			pgtlock; /* lock for page table */
 
@@ -116,19 +102,6 @@ struct mtk_iommu_domain {
 	struct iommu_domain		domain;
 };
 
-struct mtk_iommu_data {
-	void __iomem			*base;
-	int				irq;
-	struct device			*dev;
-	struct clk			*bclk;
-	phys_addr_t			protect_base; /* protect memory base */
-	struct mtk_iommu_suspend_reg	reg;
-	struct mtk_iommu_domain		*m4u_dom;
-	struct iommu_group		*m4u_group;
-	struct mtk_smi_iommu		smi_imu;      /* SMI larb iommu info */
-	bool                            enable_4GB;
-};
-
 static struct iommu_ops mtk_iommu_ops;
 
 static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
@@ -552,25 +525,6 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
 	return 0;
 }
 
-static int compare_of(struct device *dev, void *data)
-{
-	return dev->of_node == data;
-}
-
-static int mtk_iommu_bind(struct device *dev)
-{
-	struct mtk_iommu_data *data = dev_get_drvdata(dev);
-
-	return component_bind_all(dev, &data->smi_imu);
-}
-
-static void mtk_iommu_unbind(struct device *dev)
-{
-	struct mtk_iommu_data *data = dev_get_drvdata(dev);
-
-	component_unbind_all(dev, &data->smi_imu);
-}
-
 static const struct component_master_ops mtk_iommu_com_ops = {
 	.bind		= mtk_iommu_bind,
 	.unbind		= mtk_iommu_unbind,
diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h
new file mode 100644
index 0000000..9ed0a84
--- /dev/null
+++ b/drivers/iommu/mtk_iommu.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015-2016 MediaTek Inc.
+ * Author: Honghui Zhang <honghui.zhang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_IOMMU_H_
+#define _MTK_IOMMU_H_
+
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <soc/mediatek/smi.h>
+
+#include "io-pgtable.h"
+
+struct mtk_iommu_suspend_reg {
+	u32				standard_axi_mode;
+	u32				dcm_dis;
+	u32				ctrl_reg;
+	u32				int_control0;
+	u32				int_main_control;
+};
+
+struct mtk_iommu_client_priv {
+	struct list_head		client;
+	unsigned int			mtk_m4u_id;
+	struct device			*m4udev;
+};
+
+struct mtk_iommu_domain;
+
+struct mtk_iommu_data {
+	void __iomem			*base;
+	int				irq;
+	struct device			*dev;
+	struct clk			*bclk;
+	phys_addr_t			protect_base; /* protect memory base */
+	struct mtk_iommu_suspend_reg	reg;
+	struct mtk_iommu_domain		*m4u_dom;
+	struct iommu_group		*m4u_group;
+	struct mtk_smi_iommu		smi_imu;      /* SMI larb iommu info */
+	bool                            enable_4GB;
+};
+
+static int compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static int mtk_iommu_bind(struct device *dev)
+{
+	struct mtk_iommu_data *data = dev_get_drvdata(dev);
+
+	return component_bind_all(dev, &data->smi_imu);
+}
+
+static void mtk_iommu_unbind(struct device *dev)
+{
+	struct mtk_iommu_data *data = dev_get_drvdata(dev);
+
+	component_unbind_all(dev, &data->smi_imu);
+}
+
+#endif
-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:50:57 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:50:57 +0800
Subject: [PATCH v4 1/5] dt-bindings: mediatek: add descriptions for mediatek
 mt2701 iommu and smi
In-Reply-To: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465379461-14757-2-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

This patch defines the local arbitor port IDs for mediatek SoC MT2701 and
add descriptions of binding for mediatek generation one iommu and smi.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
Acked-by: Rob Herring <robh at kernel.org>
---
 .../devicetree/bindings/iommu/mediatek,iommu.txt   | 13 +++-
 .../memory-controllers/mediatek,smi-common.txt     | 21 +++++-
 .../memory-controllers/mediatek,smi-larb.txt       |  4 +-
 include/dt-bindings/memory/mt2701-larb-port.h      | 85 ++++++++++++++++++++++
 4 files changed, 115 insertions(+), 8 deletions(-)
 create mode 100644 include/dt-bindings/memory/mt2701-larb-port.h

diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt b/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
index cd1b1cd..53c20ca 100644
--- a/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
@@ -1,7 +1,9 @@
 * Mediatek IOMMU Architecture Implementation
 
-  Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U) which
-uses the ARM Short-Descriptor translation table format for address translation.
+  Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U), and
+this M4U have two generations of HW architecture. Generation one uses flat
+pagetable, and only supports 4K size page mapping. Generation two uses the
+ARM Short-Descriptor translation table format for address translation.
 
   About the M4U Hardware Block Diagram, please check below:
 
@@ -36,7 +38,9 @@ in each larb. Take a example, There are many ports like MC, PP, VLD in the
 video decode local arbiter, all these ports are according to the video HW.
 
 Required properties:
-- compatible : must be "mediatek,mt8173-m4u".
+- compatible : must be one of the following string:
+	"mediatek,mt2701-m4u" for mt2701 which uses generation one m4u HW.
+	"mediatek,mt8173-m4u" for mt8173 which uses generation two m4u HW.
 - reg : m4u register base and size.
 - interrupts : the interrupt of m4u.
 - clocks : must contain one entry for each clock-names.
@@ -46,7 +50,8 @@ Required properties:
 	according to the local arbiter index, like larb0, larb1, larb2...
 - iommu-cells : must be 1. This is the mtk_m4u_id according to the HW.
 	Specifies the mtk_m4u_id as defined in
-	dt-binding/memory/mt8173-larb-port.h.
+	dt-binding/memory/mt2701-larb-port.h for mt2701 and
+	dt-binding/memory/mt8173-larb-port.h for mt8173
 
 Example:
 	iommu: iommu at 10205000 {
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
index 06a83ce..aa614b2 100644
--- a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
@@ -2,16 +2,31 @@ SMI (Smart Multimedia Interface) Common
 
 The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
 
+Mediatek SMI have two generations of HW architecture, mt8173 uses the second
+generation of SMI HW while mt2701 uses the first generation HW of SMI.
+
+There's slight differences between the two SMI, for generation 2, the
+register which control the iommu port is at each larb's register base. But
+for generation 1, the register is at smi ao base(smi always on register
+base). Besides that, the smi async clock should be prepared and enabled for
+SMI generation 1 to transform the smi clock into emi clock domain, but that is
+not needed for SMI generation 2.
+
 Required properties:
-- compatible : must be "mediatek,mt8173-smi-common"
+- compatible : must be one of :
+	"mediatek,mt2701-smi-common"
+	"mediatek,mt8173-smi-common"
 - reg : the register and size of the SMI block.
 - power-domains : a phandle to the power domain of this local arbiter.
 - clocks : Must contain an entry for each entry in clock-names.
-- clock-names : must contain 2 entries, as follows:
+- clock-names : must contain 3 entries for generation 1 smi HW and 2 entries
+  for generation 2 smi HW as follows:
   - "apb" : Advanced Peripheral Bus clock, It's the clock for setting
 	    the register.
   - "smi" : It's the clock for transfer data and command.
-  They may be the same if both source clocks are the same.
+	    They may be the same if both source clocks are the same.
+  - "async" : asynchronous clock, it help transform the smi clock into the emi
+	      clock domain, this clock is only needed by generation 1 smi HW.
 
 Example:
 	smi_common: smi at 14022000 {
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
index 55ff3b7..21277a5 100644
--- a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
@@ -3,7 +3,9 @@ SMI (Smart Multimedia Interface) Local Arbiter
 The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
 
 Required properties:
-- compatible : must be "mediatek,mt8173-smi-larb"
+- compatible : must be one of :
+		"mediatek,mt8173-smi-larb"
+		"mediatek,mt2701-smi-larb"
 - reg : the register and size of this local arbiter.
 - mediatek,smi : a phandle to the smi_common node.
 - power-domains : a phandle to the power domain of this local arbiter.
diff --git a/include/dt-bindings/memory/mt2701-larb-port.h b/include/dt-bindings/memory/mt2701-larb-port.h
new file mode 100644
index 0000000..78f6678
--- /dev/null
+++ b/include/dt-bindings/memory/mt2701-larb-port.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Honghui Zhang <honghui.zhang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_LARB_PORT_H_
+#define _MT2701_LARB_PORT_H_
+
+/*
+ * Mediatek m4u generation 1 such as mt2701 has flat m4u port numbers,
+ * the first port's id for larb[N] would be the last port's id of larb[N - 1]
+ * plus one while larb[0]'s first port number is 0. The definition of
+ * MT2701_M4U_ID_LARBx is following HW register spec.
+ * But m4u generation 2 like mt8173 have different port number, it use fixed
+ * offset for each larb, the first port's id for larb[N] would be (N * 32).
+ */
+#define LARB0_PORT_OFFSET		0
+#define LARB1_PORT_OFFSET		11
+#define LARB2_PORT_OFFSET		21
+#define LARB3_PORT_OFFSET		43
+
+#define MT2701_M4U_ID_LARB0(port)	((port) + LARB0_PORT_OFFSET)
+#define MT2701_M4U_ID_LARB1(port)	((port) + LARB1_PORT_OFFSET)
+#define MT2701_M4U_ID_LARB2(port)	((port) + LARB2_PORT_OFFSET)
+
+/* Port define for larb0 */
+#define MT2701_M4U_PORT_DISP_OVL_0		MT2701_M4U_ID_LARB0(0)
+#define MT2701_M4U_PORT_DISP_RDMA1		MT2701_M4U_ID_LARB0(1)
+#define MT2701_M4U_PORT_DISP_RDMA		MT2701_M4U_ID_LARB0(2)
+#define MT2701_M4U_PORT_DISP_WDMA		MT2701_M4U_ID_LARB0(3)
+#define MT2701_M4U_PORT_MM_CMDQ			MT2701_M4U_ID_LARB0(4)
+#define MT2701_M4U_PORT_MDP_RDMA		MT2701_M4U_ID_LARB0(5)
+#define MT2701_M4U_PORT_MDP_WDMA		MT2701_M4U_ID_LARB0(6)
+#define MT2701_M4U_PORT_MDP_ROTO		MT2701_M4U_ID_LARB0(7)
+#define MT2701_M4U_PORT_MDP_ROTCO		MT2701_M4U_ID_LARB0(8)
+#define MT2701_M4U_PORT_MDP_ROTVO		MT2701_M4U_ID_LARB0(9)
+#define MT2701_M4U_PORT_MDP_RDMA1		MT2701_M4U_ID_LARB0(10)
+
+/* Port define for larb1 */
+#define MT2701_M4U_PORT_VDEC_MC_EXT		MT2701_M4U_ID_LARB1(0)
+#define MT2701_M4U_PORT_VDEC_PP_EXT		MT2701_M4U_ID_LARB1(1)
+#define MT2701_M4U_PORT_VDEC_PPWRAP_EXT		MT2701_M4U_ID_LARB1(2)
+#define MT2701_M4U_PORT_VDEC_AVC_MV_EXT		MT2701_M4U_ID_LARB1(3)
+#define MT2701_M4U_PORT_VDEC_PRED_RD_EXT	MT2701_M4U_ID_LARB1(4)
+#define MT2701_M4U_PORT_VDEC_PRED_WR_EXT	MT2701_M4U_ID_LARB1(5)
+#define MT2701_M4U_PORT_VDEC_VLD_EXT		MT2701_M4U_ID_LARB1(6)
+#define MT2701_M4U_PORT_VDEC_VLD2_EXT		MT2701_M4U_ID_LARB1(7)
+#define MT2701_M4U_PORT_VDEC_TILE_EXT		MT2701_M4U_ID_LARB1(8)
+#define MT2701_M4U_PORT_VDEC_IMG_RESZ_EXT	MT2701_M4U_ID_LARB1(9)
+
+/* Port define for larb2 */
+#define MT2701_M4U_PORT_VENC_RCPU		MT2701_M4U_ID_LARB2(0)
+#define MT2701_M4U_PORT_VENC_REC_FRM		MT2701_M4U_ID_LARB2(1)
+#define MT2701_M4U_PORT_VENC_BSDMA		MT2701_M4U_ID_LARB2(2)
+#define MT2701_M4U_PORT_JPGENC_RDMA		MT2701_M4U_ID_LARB2(3)
+#define MT2701_M4U_PORT_VENC_LT_RCPU		MT2701_M4U_ID_LARB2(4)
+#define MT2701_M4U_PORT_VENC_LT_REC_FRM		MT2701_M4U_ID_LARB2(5)
+#define MT2701_M4U_PORT_VENC_LT_BSDMA		MT2701_M4U_ID_LARB2(6)
+#define MT2701_M4U_PORT_JPGDEC_BSDMA		MT2701_M4U_ID_LARB2(7)
+#define MT2701_M4U_PORT_VENC_SV_COMV		MT2701_M4U_ID_LARB2(8)
+#define MT2701_M4U_PORT_VENC_RD_COMV		MT2701_M4U_ID_LARB2(9)
+#define MT2701_M4U_PORT_JPGENC_BSDMA		MT2701_M4U_ID_LARB2(10)
+#define MT2701_M4U_PORT_VENC_CUR_LUMA		MT2701_M4U_ID_LARB2(11)
+#define MT2701_M4U_PORT_VENC_CUR_CHROMA		MT2701_M4U_ID_LARB2(12)
+#define MT2701_M4U_PORT_VENC_REF_LUMA		MT2701_M4U_ID_LARB2(13)
+#define MT2701_M4U_PORT_VENC_REF_CHROMA		MT2701_M4U_ID_LARB2(14)
+#define MT2701_M4U_PORT_IMG_RESZ		MT2701_M4U_ID_LARB2(15)
+#define MT2701_M4U_PORT_VENC_LT_SV_COMV		MT2701_M4U_ID_LARB2(16)
+#define MT2701_M4U_PORT_VENC_LT_RD_COMV		MT2701_M4U_ID_LARB2(17)
+#define MT2701_M4U_PORT_VENC_LT_CUR_LUMA	MT2701_M4U_ID_LARB2(18)
+#define MT2701_M4U_PORT_VENC_LT_CUR_CHROMA	MT2701_M4U_ID_LARB2(19)
+#define MT2701_M4U_PORT_VENC_LT_REF_LUMA	MT2701_M4U_ID_LARB2(20)
+#define MT2701_M4U_PORT_VENC_LT_REF_CHROMA	MT2701_M4U_ID_LARB2(21)
+#define MT2701_M4U_PORT_JPGDEC_WDMA		MT2701_M4U_ID_LARB2(22)
+
+#endif
-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:50:59 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:50:59 +0800
Subject: [PATCH v4 3/5] memory/mediatek: add support for mt2701
In-Reply-To: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465379461-14757-4-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

Mediatek SMI has two generations of HW architecture, mt8173 uses the
second generation of SMI HW while mt2701 uses the first generation
HW of SMI.

There's slight differences between the two generations, for generation 2,
the register which control the iommu port access PA or IOVA is at each
larb's register base. But for generation 1, the register is at smi ao
base(smi always on register base).
Besides that, the smi async clock should be prepared and enabled for SMI
generation 1 HW to transform the smi clock into emi clock domain, but is
not needed for SMI generation 2.

This patch add SMI driver for mt2701 which use generation 1 SMI HW.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
---
 drivers/memory/mtk-smi.c | 167 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 149 insertions(+), 18 deletions(-)

diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index f6b5757..4afbc41 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -21,19 +21,50 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <soc/mediatek/smi.h>
+#include <dt-bindings/memory/mt2701-larb-port.h>
 
 #define SMI_LARB_MMU_EN		0xf00
+#define REG_SMI_SECUR_CON_BASE		0x5c0
+
+/* every register control 8 port, register offset 0x4 */
+#define REG_SMI_SECUR_CON_OFFSET(id)	(((id) >> 3) << 2)
+#define REG_SMI_SECUR_CON_ADDR(id)	\
+	(REG_SMI_SECUR_CON_BASE + REG_SMI_SECUR_CON_OFFSET(id))
+
+/*
+ * every port have 4 bit to control, bit[port + 3] control virtual or physical,
+ * bit[port + 2 : port + 1] control the domain, bit[port] control the security
+ * or non-security.
+ */
+#define SMI_SECUR_CON_VAL_MSK(id)	(~(0xf << (((id) & 0x7) << 2)))
+#define SMI_SECUR_CON_VAL_VIRT(id)	BIT((((id) & 0x7) << 2) + 3)
+/* mt2701 domain should be set to 3 */
+#define SMI_SECUR_CON_VAL_DOMAIN(id)	(0x3 << ((((id) & 0x7) << 2) + 1))
+
+struct mtk_smi_larb_gen {
+	int port_in_larb[MTK_LARB_NR_MAX + 1];
+	void (*config_port)(struct device *);
+};
 
 struct mtk_smi {
-	struct device	*dev;
-	struct clk	*clk_apb, *clk_smi;
+	struct device			*dev;
+	struct clk			*clk_apb, *clk_smi;
+	struct clk			*clk_async; /*only needed by mt2701*/
+	void __iomem			*smi_ao_base;
 };
 
 struct mtk_smi_larb { /* larb: local arbiter */
-	struct mtk_smi  smi;
-	void __iomem	*base;
-	struct device	*smi_common_dev;
-	u32		*mmu;
+	struct mtk_smi			smi;
+	void __iomem			*base;
+	struct device			*smi_common_dev;
+	const struct mtk_smi_larb_gen	*larb_gen;
+	int				larbid;
+	u32				*mmu;
+};
+
+enum mtk_smi_gen {
+	MTK_SMI_GEN1,
+	MTK_SMI_GEN2
 };
 
 static int mtk_smi_enable(const struct mtk_smi *smi)
@@ -71,6 +102,7 @@ static void mtk_smi_disable(const struct mtk_smi *smi)
 int mtk_smi_larb_get(struct device *larbdev)
 {
 	struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
+	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
 	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
 	int ret;
 
@@ -87,7 +119,7 @@ int mtk_smi_larb_get(struct device *larbdev)
 	}
 
 	/* Configure the iommu info for this larb */
-	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
+	larb_gen->config_port(larbdev);
 
 	return 0;
 }
@@ -126,6 +158,45 @@ mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
 	return -ENODEV;
 }
 
+static void mtk_smi_larb_config_port(struct device *dev)
+{
+	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
+
+	writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
+}
+
+
+static void mtk_smi_larb_config_port_gen1(struct device *dev)
+{
+	struct mtk_smi_larb *larb = dev_get_drvdata(dev);
+	const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
+	struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
+	int i, m4u_port_id, larb_port_num;
+	u32 sec_con_val, reg_val;
+
+	m4u_port_id = larb_gen->port_in_larb[larb->larbid];
+	larb_port_num = larb_gen->port_in_larb[larb->larbid + 1]
+			- larb_gen->port_in_larb[larb->larbid];
+
+	for (i = 0; i < larb_port_num; i++, m4u_port_id++) {
+		if (*larb->mmu & BIT(i)) {
+			/* bit[port + 3] controls the virtual or physical */
+			sec_con_val = SMI_SECUR_CON_VAL_VIRT(m4u_port_id);
+		} else {
+			/* do not need to enable m4u for this port */
+			continue;
+		}
+		reg_val = readl(common->smi_ao_base
+			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
+		reg_val &= SMI_SECUR_CON_VAL_MSK(m4u_port_id);
+		reg_val |= sec_con_val;
+		reg_val |= SMI_SECUR_CON_VAL_DOMAIN(m4u_port_id);
+		writel(reg_val,
+			common->smi_ao_base
+			+ REG_SMI_SECUR_CON_ADDR(m4u_port_id));
+	}
+}
+
 static void
 mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
 {
@@ -137,6 +208,31 @@ static const struct component_ops mtk_smi_larb_component_ops = {
 	.unbind = mtk_smi_larb_unbind,
 };
 
+static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
+	/* mt8173 do not need the port in larb */
+	.config_port = mtk_smi_larb_config_port,
+};
+
+static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
+	.port_in_larb = {
+		LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
+		LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
+	},
+	.config_port = mtk_smi_larb_config_port_gen1,
+};
+
+static const struct of_device_id mtk_smi_larb_of_ids[] = {
+	{
+		.compatible = "mediatek,mt8173-smi-larb",
+		.data = &mtk_smi_larb_mt8173
+	},
+	{
+		.compatible = "mediatek,mt2701-smi-larb",
+		.data = &mtk_smi_larb_mt2701
+	},
+	{}
+};
+
 static int mtk_smi_larb_probe(struct platform_device *pdev)
 {
 	struct mtk_smi_larb *larb;
@@ -144,14 +240,20 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct device_node *smi_node;
 	struct platform_device *smi_pdev;
+	const struct of_device_id *of_id;
 
 	if (!dev->pm_domain)
 		return -EPROBE_DEFER;
 
+	of_id = of_match_node(mtk_smi_larb_of_ids, pdev->dev.of_node);
+	if (!of_id)
+		return -EINVAL;
+
 	larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
 	if (!larb)
 		return -ENOMEM;
 
+	larb->larb_gen = of_id->data;
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	larb->base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(larb->base))
@@ -191,24 +293,34 @@ static int mtk_smi_larb_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct of_device_id mtk_smi_larb_of_ids[] = {
-	{ .compatible = "mediatek,mt8173-smi-larb",},
-	{}
-};
-
 static struct platform_driver mtk_smi_larb_driver = {
 	.probe	= mtk_smi_larb_probe,
-	.remove = mtk_smi_larb_remove,
+	.remove	= mtk_smi_larb_remove,
 	.driver	= {
 		.name = "mtk-smi-larb",
 		.of_match_table = mtk_smi_larb_of_ids,
 	}
 };
 
+static const struct of_device_id mtk_smi_common_of_ids[] = {
+	{
+		.compatible = "mediatek,mt8173-smi-common",
+		.data = (void *)MTK_SMI_GEN2
+	},
+	{
+		.compatible = "mediatek,mt2701-smi-common",
+		.data = (void *)MTK_SMI_GEN1
+	},
+	{}
+};
+
 static int mtk_smi_common_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct mtk_smi *common;
+	struct resource *res;
+	const struct of_device_id *of_id;
+	enum mtk_smi_gen smi_gen;
 
 	if (!dev->pm_domain)
 		return -EPROBE_DEFER;
@@ -226,6 +338,29 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
 	if (IS_ERR(common->clk_smi))
 		return PTR_ERR(common->clk_smi);
 
+	of_id = of_match_node(mtk_smi_common_of_ids, pdev->dev.of_node);
+	if (!of_id)
+		return -EINVAL;
+
+	/*
+	 * for mtk smi gen 1, we need to get the ao(always on) base to config
+	 * m4u port, and we need to enable the aync clock for transform the smi
+	 * clock into emi clock domain, but for mtk smi gen2, there's no smi ao
+	 * base.
+	 */
+	smi_gen = (enum mtk_smi_gen)of_id->data;
+	if (smi_gen == MTK_SMI_GEN1) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		common->smi_ao_base = devm_ioremap_resource(dev, res);
+		if (IS_ERR(common->smi_ao_base))
+			return PTR_ERR(common->smi_ao_base);
+
+		common->clk_async = devm_clk_get(dev, "async");
+		if (IS_ERR(common->clk_async))
+			return PTR_ERR(common->clk_async);
+
+		clk_prepare_enable(common->clk_async);
+	}
 	pm_runtime_enable(dev);
 	platform_set_drvdata(pdev, common);
 	return 0;
@@ -237,11 +372,6 @@ static int mtk_smi_common_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static const struct of_device_id mtk_smi_common_of_ids[] = {
-	{ .compatible = "mediatek,mt8173-smi-common", },
-	{}
-};
-
 static struct platform_driver mtk_smi_common_driver = {
 	.probe	= mtk_smi_common_probe,
 	.remove = mtk_smi_common_remove,
@@ -272,4 +402,5 @@ err_unreg_smi:
 	platform_driver_unregister(&mtk_smi_common_driver);
 	return ret;
 }
+
 subsys_initcall(mtk_smi_init);
-- 
1.8.1.1.dirty



From honghui.zhang at mediatek.com  Wed Jun  8 02:51:00 2016
From: honghui.zhang at mediatek.com (honghui.zhang at mediatek.com)
Date: Wed, 8 Jun 2016 17:51:00 +0800
Subject: [PATCH v4 4/5] iommu/mediatek: add support for mtk iommu generation
 one HW
In-Reply-To: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <1465379461-14757-5-git-send-email-honghui.zhang@mediatek.com>

From: Honghui Zhang <honghui.zhang at mediatek.com>

Mediatek SoC's M4U has two generations of HW architcture. Generation one
uses flat, one layer pagetable, and was shipped with ARM architecture, it
only supports 4K size page mapping. MT2701 SoC uses this generation one
m4u HW. Generation two uses the ARM short-descriptor translation table
format for address translation, and was shipped with ARM64 architecture,
MT8173 uses this generation two m4u HW. All the two generation iommu HW
only have one iommu domain, and all its iommu clients share the same
iova address.

These two generation m4u HW have slit different register groups and
register offset, but most register names are the same. This patch add iommu
support for mediatek SoC mt2701.

Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
---
 drivers/iommu/Kconfig        |  18 ++
 drivers/iommu/Makefile       |   1 +
 drivers/iommu/mtk_iommu_v1.c | 727 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 746 insertions(+)
 create mode 100644 drivers/iommu/mtk_iommu_v1.c

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index ad08603..32bb1e5 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -343,4 +343,22 @@ config MTK_IOMMU
 
 	  If unsure, say N here.
 
+config MTK_IOMMU_V1
+	bool "MTK IOMMU Version 1 (M4U gen1) Support"
+	depends on ARM
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	select ARM_DMA_USE_IOMMU
+	select IOMMU_API
+	select MEMORY
+	select MTK_SMI
+	select COMMON_CLK_MT2701_MMSYS
+	select COMMON_CLK_MT2701_IMGSYS
+	select COMMON_CLK_MT2701_VDECSYS
+	help
+	  Support for the M4U on certain Mediatek SoCs. M4U generation 1 HW is
+	  Multimedia Memory Managememt Unit. This option enables remapping of
+	  DMA memory accesses for the multimedia subsystem.
+
+	  if unsure, say N here.
+
 endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c6edb31..778baf5 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
 obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
 obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
 obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o
+obj-$(CONFIG_MTK_IOMMU_V1) += mtk_iommu_v1.o
 obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
 obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
 obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
new file mode 100644
index 0000000..294485d
--- /dev/null
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -0,0 +1,727 @@
+/*
+ * Copyright (c) 2015-2016 MediaTek Inc.
+ * Author: Honghui Zhang <honghui.zhang at mediatek.com>
+ *
+ * Based on driver/iommu/mtk_iommu.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bootmem.h>
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/dma-iommu.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/kmemleak.h>
+#include <linux/list.h>
+#include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <asm/barrier.h>
+#include <asm/dma-iommu.h>
+#include <linux/module.h>
+#include <dt-bindings/memory/mt2701-larb-port.h>
+#include <soc/mediatek/smi.h>
+#include "mtk_iommu.h"
+
+#define REG_MMU_PT_BASE_ADDR			0x000
+
+#define F_ALL_INVLD				0x2
+#define F_MMU_INV_RANGE				0x1
+#define F_INVLD_EN0				BIT(0)
+#define F_INVLD_EN1				BIT(1)
+
+#define F_MMU_FAULT_VA_MSK			0xfffff000
+#define MTK_PROTECT_PA_ALIGN			128
+
+#define REG_MMU_CTRL_REG			0x210
+#define F_MMU_CTRL_COHERENT_EN			BIT(8)
+#define REG_MMU_IVRP_PADDR			0x214
+#define REG_MMU_INT_CONTROL			0x220
+#define F_INT_TRANSLATION_FAULT			BIT(0)
+#define F_INT_MAIN_MULTI_HIT_FAULT		BIT(1)
+#define F_INT_INVALID_PA_FAULT			BIT(2)
+#define F_INT_ENTRY_REPLACEMENT_FAULT		BIT(3)
+#define F_INT_TABLE_WALK_FAULT			BIT(4)
+#define F_INT_TLB_MISS_FAULT			BIT(5)
+#define F_INT_PFH_DMA_FIFO_OVERFLOW		BIT(6)
+#define F_INT_MISS_DMA_FIFO_OVERFLOW		BIT(7)
+
+#define F_MMU_TF_PROTECT_SEL(prot)		(((prot) & 0x3) << 5)
+#define F_INT_CLR_BIT				BIT(12)
+
+#define REG_MMU_FAULT_ST			0x224
+#define REG_MMU_FAULT_VA			0x228
+#define REG_MMU_INVLD_PA			0x22C
+#define REG_MMU_INT_ID				0x388
+#define REG_MMU_INVALIDATE			0x5c0
+#define REG_MMU_INVLD_START_A			0x5c4
+#define REG_MMU_INVLD_END_A			0x5c8
+
+#define REG_MMU_INV_SEL				0x5d8
+#define REG_MMU_STANDARD_AXI_MODE		0x5e8
+
+#define REG_MMU_DCM				0x5f0
+#define F_MMU_DCM_ON				BIT(1)
+#define REG_MMU_CPE_DONE			0x60c
+#define F_DESC_VALID				0x2
+#define F_DESC_NONSEC				BIT(3)
+#define MT2701_M4U_TF_LARB(TF)			(6 - (((TF) >> 13) & 0x7))
+#define MT2701_M4U_TF_PORT(TF)			(((TF) >> 8) & 0xF)
+/* MTK generation one iommu HW only support 4K size mapping */
+#define MT2701_IOMMU_PAGE_SHIFT			12
+#define MT2701_IOMMU_PAGE_SIZE			(1UL << MT2701_IOMMU_PAGE_SHIFT)
+
+/*
+ * MTK m4u support 4GB iova address space, and only support 4K page
+ * mapping. So the pagetable size should be exactly as 4M.
+ */
+#define M2701_IOMMU_PGT_SIZE			SZ_4M
+
+struct mtk_iommu_domain {
+	spinlock_t			pgtlock; /* lock for page table */
+	struct iommu_domain		domain;
+	u32				*pgt_va;
+	dma_addr_t			pgt_pa;
+	struct mtk_iommu_data		*data;
+};
+
+static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
+{
+	return container_of(dom, struct mtk_iommu_domain, domain);
+}
+
+static const int mt2701_m4u_in_larb[] = {
+	LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
+	LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
+};
+
+static inline int mt2701_m4u_to_larb(int id)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(mt2701_m4u_in_larb) - 1; i >= 0; i--)
+		if ((id) >= mt2701_m4u_in_larb[i])
+			return i;
+
+	return 0;
+}
+
+static inline int mt2701_m4u_to_port(int id)
+{
+	int larb = mt2701_m4u_to_larb(id);
+
+	return id - mt2701_m4u_in_larb[larb];
+}
+
+static void mtk_iommu_tlb_flush_all(struct mtk_iommu_data *data)
+{
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
+			data->base + REG_MMU_INV_SEL);
+	writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
+	wmb(); /* Make sure the tlb flush all done */
+}
+
+static void mtk_iommu_tlb_flush_range(struct mtk_iommu_data *data,
+				unsigned long iova, size_t size)
+{
+	int ret;
+	u32 tmp;
+
+	writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
+		data->base + REG_MMU_INV_SEL);
+	writel_relaxed(iova & F_MMU_FAULT_VA_MSK,
+		data->base + REG_MMU_INVLD_START_A);
+	writel_relaxed((iova + size - 1) & F_MMU_FAULT_VA_MSK,
+		data->base + REG_MMU_INVLD_END_A);
+	writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
+
+	ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE,
+				tmp, tmp != 0, 10, 100000);
+	if (ret) {
+		dev_warn(data->dev,
+			 "Partial TLB flush timed out, falling back to full flush\n");
+		mtk_iommu_tlb_flush_all(data);
+	}
+	/* Clear the CPE status */
+	writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+}
+
+static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
+{
+	struct mtk_iommu_data *data = dev_id;
+	struct mtk_iommu_domain *dom = data->m4u_dom;
+	u32 int_state, regval, fault_iova, fault_pa;
+	unsigned int fault_larb, fault_port;
+
+	/* Read error information from registers */
+	int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST);
+	fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+
+	fault_iova &= F_MMU_FAULT_VA_MSK;
+	fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
+	regval = readl_relaxed(data->base + REG_MMU_INT_ID);
+	fault_larb = MT2701_M4U_TF_LARB(regval);
+	fault_port = MT2701_M4U_TF_PORT(regval);
+
+	/*
+	 * MTK v1 iommu HW could not determine whether the fault is read or
+	 * write fault, report as read fault.
+	 */
+	if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
+			IOMMU_FAULT_READ))
+		dev_err_ratelimited(data->dev,
+			"fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d\n",
+			int_state, fault_iova, fault_pa,
+			fault_larb, fault_port);
+
+	/* Interrupt clear */
+	regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL);
+	regval |= F_INT_CLR_BIT;
+	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+
+	mtk_iommu_tlb_flush_all(data);
+
+	return IRQ_HANDLED;
+}
+
+static void mtk_iommu_config(struct mtk_iommu_data *data,
+			     struct device *dev, bool enable)
+{
+	struct mtk_iommu_client_priv *head, *cur, *next;
+	struct mtk_smi_larb_iommu    *larb_mmu;
+	unsigned int                 larbid, portid;
+
+	head = dev->archdata.iommu;
+	list_for_each_entry_safe(cur, next, &head->client, client) {
+		larbid = mt2701_m4u_to_larb(cur->mtk_m4u_id);
+		portid = mt2701_m4u_to_port(cur->mtk_m4u_id);
+		larb_mmu = &data->smi_imu.larb_imu[larbid];
+
+		dev_dbg(dev, "%s iommu port: %d\n",
+			enable ? "enable" : "disable", portid);
+
+		if (enable)
+			larb_mmu->mmu |= MTK_SMI_MMU_EN(portid);
+		else
+			larb_mmu->mmu &= ~MTK_SMI_MMU_EN(portid);
+	}
+}
+
+static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
+{
+	struct mtk_iommu_domain *dom = data->m4u_dom;
+
+	spin_lock_init(&dom->pgtlock);
+
+	dom->pgt_va = dma_zalloc_coherent(data->dev,
+				M2701_IOMMU_PGT_SIZE,
+				&dom->pgt_pa, GFP_KERNEL);
+	if (!dom->pgt_va)
+		return -ENOMEM;
+
+	writel(dom->pgt_pa, data->base + REG_MMU_PT_BASE_ADDR);
+
+	dom->data = data;
+
+	return 0;
+}
+
+static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type)
+{
+	struct mtk_iommu_domain *dom;
+
+	if (type != IOMMU_DOMAIN_UNMANAGED)
+		return NULL;
+
+	dom = kzalloc(sizeof(*dom), GFP_KERNEL);
+	if (!dom)
+		return NULL;
+
+	return &dom->domain;
+}
+
+static void mtk_iommu_domain_free(struct iommu_domain *domain)
+{
+	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	struct mtk_iommu_data *data = dom->data;
+
+	dma_free_coherent(data->dev, M2701_IOMMU_PGT_SIZE,
+			dom->pgt_va, dom->pgt_pa);
+	kfree(to_mtk_domain(domain));
+}
+
+static int mtk_iommu_attach_device(struct iommu_domain *domain,
+				   struct device *dev)
+{
+	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
+	struct mtk_iommu_data *data;
+	int ret;
+
+	if (!priv)
+		return -ENODEV;
+
+	data = dev_get_drvdata(priv->m4udev);
+	if (!data->m4u_dom) {
+		data->m4u_dom = dom;
+		ret = mtk_iommu_domain_finalise(data);
+		if (ret) {
+			data->m4u_dom = NULL;
+			return ret;
+		}
+	}
+
+	mtk_iommu_config(data, dev, true);
+	return 0;
+}
+
+static void mtk_iommu_detach_device(struct iommu_domain *domain,
+				    struct device *dev)
+{
+	struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
+	struct mtk_iommu_data *data;
+
+	if (!priv)
+		return;
+
+	data = dev_get_drvdata(priv->m4udev);
+	mtk_iommu_config(data, dev, false);
+}
+
+static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
+			 phys_addr_t paddr, size_t size, int prot)
+{
+	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT;
+	unsigned long flags;
+	unsigned int i;
+	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT);
+	u32 pabase = (u32)paddr;
+	int map_size = 0;
+
+	spin_lock_irqsave(&dom->pgtlock, flags);
+	for (i = 0; i < page_num; i++) {
+		if (pgt_base_iova[i]) {
+			memset(pgt_base_iova, 0, i * sizeof(u32));
+			break;
+		}
+		pgt_base_iova[i] = pabase | F_DESC_VALID | F_DESC_NONSEC;
+		pabase += MT2701_IOMMU_PAGE_SIZE;
+		map_size += MT2701_IOMMU_PAGE_SIZE;
+	}
+
+	spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+	mtk_iommu_tlb_flush_range(dom->data, iova, size);
+
+	return map_size == size ? 0 : -EEXIST;
+}
+
+static size_t mtk_iommu_unmap(struct iommu_domain *domain,
+			      unsigned long iova, size_t size)
+{
+	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	unsigned long flags;
+	u32 *pgt_base_iova = dom->pgt_va + (iova  >> MT2701_IOMMU_PAGE_SHIFT);
+	unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT;
+
+	spin_lock_irqsave(&dom->pgtlock, flags);
+	memset(pgt_base_iova, 0, page_num * sizeof(u32));
+	spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+	mtk_iommu_tlb_flush_range(dom->data, iova, size);
+
+	return size;
+}
+
+static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
+					  dma_addr_t iova)
+{
+	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	unsigned long flags;
+	phys_addr_t pa;
+
+	spin_lock_irqsave(&dom->pgtlock, flags);
+	pa = *(dom->pgt_va + (iova >> MT2701_IOMMU_PAGE_SHIFT));
+	pa = pa & (~(MT2701_IOMMU_PAGE_SIZE - 1));
+	spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+	return pa;
+}
+
+/*
+ * MTK generation one iommu HW only support one iommu domain, and all the client
+ * sharing the same iova address space.
+ */
+static int mtk_iommu_create_mapping(struct device *dev,
+				    struct of_phandle_args *args)
+{
+	struct mtk_iommu_client_priv *head, *priv, *next;
+	struct platform_device *m4updev;
+	struct dma_iommu_mapping *mtk_mapping;
+	struct device *m4udev;
+	int ret;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n",
+			args->args_count);
+		return -EINVAL;
+	}
+
+	if (!dev->archdata.iommu) {
+		/* Get the m4u device */
+		m4updev = of_find_device_by_node(args->np);
+		if (WARN_ON(!m4updev))
+			return -EINVAL;
+
+		head = kzalloc(sizeof(*head), GFP_KERNEL);
+		if (!head)
+			return -ENOMEM;
+
+		dev->archdata.iommu = head;
+		INIT_LIST_HEAD(&head->client);
+		head->m4udev = &m4updev->dev;
+	} else {
+		head = dev->archdata.iommu;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto err_free_mem;
+	}
+	priv->mtk_m4u_id = args->args[0];
+	list_add_tail(&priv->client, &head->client);
+
+	m4udev = head->m4udev;
+	mtk_mapping = m4udev->archdata.iommu;
+	if (!mtk_mapping) {
+		/* MTK iommu support 4GB iova address space. */
+		mtk_mapping = arm_iommu_create_mapping(&platform_bus_type,
+						0, 1ULL << 32);
+		if (IS_ERR(mtk_mapping)) {
+			ret = PTR_ERR(mtk_mapping);
+			goto err_free_mem;
+		}
+		m4udev->archdata.iommu = mtk_mapping;
+	}
+
+	ret = arm_iommu_attach_device(dev, mtk_mapping);
+	if (ret)
+		goto err_release_mapping;
+
+	return 0;
+
+err_release_mapping:
+	arm_iommu_release_mapping(mtk_mapping);
+	m4udev->archdata.iommu = NULL;
+err_free_mem:
+	list_for_each_entry_safe(priv, next, &head->client, client)
+		kfree(priv);
+	kfree(head);
+	dev->archdata.iommu = NULL;
+	return ret;
+}
+
+static int mtk_iommu_add_device(struct device *dev)
+{
+	struct iommu_group *group;
+	struct of_phandle_args iommu_spec;
+	struct of_phandle_iterator it;
+	int err;
+
+	of_for_each_phandle(&it, err, dev->of_node, "iommus",
+			"#iommu-cells", 0) {
+		int count = of_phandle_iterator_args(&it, iommu_spec.args,
+					MAX_PHANDLE_ARGS);
+		iommu_spec.np = of_node_get(it.node);
+		iommu_spec.args_count = count;
+
+		mtk_iommu_create_mapping(dev, &iommu_spec);
+		of_node_put(iommu_spec.np);
+	}
+
+	if (!dev->archdata.iommu) /* Not a iommu client device */
+		return -ENODEV;
+
+	group = iommu_group_get_for_dev(dev);
+	if (IS_ERR(group))
+		return PTR_ERR(group);
+
+	iommu_group_put(group);
+	return 0;
+}
+
+static void mtk_iommu_remove_device(struct device *dev)
+{
+	struct mtk_iommu_client_priv *head, *cur, *next;
+
+	head = dev->archdata.iommu;
+	if (!head)
+		return;
+
+	list_for_each_entry_safe(cur, next, &head->client, client) {
+		list_del(&cur->client);
+		kfree(cur);
+	}
+	kfree(head);
+	dev->archdata.iommu = NULL;
+
+	iommu_group_remove_device(dev);
+}
+
+static struct iommu_group *mtk_iommu_device_group(struct device *dev)
+{
+	struct mtk_iommu_data *data;
+	struct mtk_iommu_client_priv *priv;
+
+	priv = dev->archdata.iommu;
+	if (!priv)
+		return ERR_PTR(-ENODEV);
+
+	/* All the client devices are in the same m4u iommu-group */
+	data = dev_get_drvdata(priv->m4udev);
+	if (!data->m4u_group) {
+		data->m4u_group = iommu_group_alloc();
+		if (IS_ERR(data->m4u_group))
+			dev_err(dev, "Failed to allocate M4U IOMMU group\n");
+	}
+	return data->m4u_group;
+}
+
+static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
+{
+	u32 regval;
+	int ret;
+
+	ret = clk_prepare_enable(data->bclk);
+	if (ret) {
+		dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
+		return ret;
+	}
+
+	regval = F_MMU_CTRL_COHERENT_EN | F_MMU_TF_PROTECT_SEL(2);
+	writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+
+	regval = F_INT_TRANSLATION_FAULT |
+		F_INT_MAIN_MULTI_HIT_FAULT |
+		F_INT_INVALID_PA_FAULT |
+		F_INT_ENTRY_REPLACEMENT_FAULT |
+		F_INT_TABLE_WALK_FAULT |
+		F_INT_TLB_MISS_FAULT |
+		F_INT_PFH_DMA_FIFO_OVERFLOW |
+		F_INT_MISS_DMA_FIFO_OVERFLOW;
+	writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL);
+
+	/* protect memory,hw will write here while translation fault */
+	writel_relaxed(data->protect_base,
+			data->base + REG_MMU_IVRP_PADDR);
+
+	writel_relaxed(F_MMU_DCM_ON, data->base + REG_MMU_DCM);
+
+	if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
+			     dev_name(data->dev), (void *)data)) {
+		writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
+		clk_disable_unprepare(data->bclk);
+		dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+static struct iommu_ops mtk_iommu_ops = {
+	.domain_alloc	= mtk_iommu_domain_alloc,
+	.domain_free	= mtk_iommu_domain_free,
+	.attach_dev	= mtk_iommu_attach_device,
+	.detach_dev	= mtk_iommu_detach_device,
+	.map		= mtk_iommu_map,
+	.unmap		= mtk_iommu_unmap,
+	.map_sg		= default_iommu_map_sg,
+	.iova_to_phys	= mtk_iommu_iova_to_phys,
+	.add_device	= mtk_iommu_add_device,
+	.remove_device	= mtk_iommu_remove_device,
+	.device_group	= mtk_iommu_device_group,
+	.pgsize_bitmap	= ~0UL << MT2701_IOMMU_PAGE_SHIFT,
+};
+
+static const struct of_device_id mtk_iommu_of_ids[] = {
+	{ .compatible = "mediatek,mt2701-m4u", },
+	{}
+};
+
+static const struct component_master_ops mtk_iommu_com_ops = {
+	.bind		= mtk_iommu_bind,
+	.unbind		= mtk_iommu_unbind,
+};
+
+static int mtk_iommu_probe(struct platform_device *pdev)
+{
+	struct mtk_iommu_data		*data;
+	struct device			*dev = &pdev->dev;
+	struct resource			*res;
+	struct component_match		*match = NULL;
+	struct of_phandle_args		larb_spec;
+	struct of_phandle_iterator	it;
+	void				*protect;
+	int				larb_nr, ret, err;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->dev = dev;
+
+	/* Protect memory. HW will access here while translation fault.*/
+	protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2,
+			GFP_KERNEL | GFP_DMA);
+	if (!protect)
+		return -ENOMEM;
+	data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(data->base))
+		return PTR_ERR(data->base);
+
+	data->irq = platform_get_irq(pdev, 0);
+	if (data->irq < 0)
+		return data->irq;
+
+	data->bclk = devm_clk_get(dev, "bclk");
+	if (IS_ERR(data->bclk))
+		return PTR_ERR(data->bclk);
+
+	larb_nr = 0;
+	of_for_each_phandle(&it, err, dev->of_node,
+			"mediatek,larbs", NULL, 0) {
+		struct platform_device *plarbdev;
+		int count = of_phandle_iterator_args(&it, larb_spec.args,
+					MAX_PHANDLE_ARGS);
+
+		if (count)
+			continue;
+
+		larb_spec.np = of_node_get(it.node);
+		if (!of_device_is_available(larb_spec.np))
+			continue;
+
+		plarbdev = of_find_device_by_node(larb_spec.np);
+		of_node_put(larb_spec.np);
+		if (!plarbdev) {
+			plarbdev = of_platform_device_create(
+						larb_spec.np, NULL,
+						platform_bus_type.dev_root);
+			if (!plarbdev)
+				return -EPROBE_DEFER;
+		}
+
+		data->smi_imu.larb_imu[larb_nr].dev = &plarbdev->dev;
+		component_match_add(dev, &match, compare_of, larb_spec.np);
+		larb_nr++;
+	}
+
+	data->smi_imu.larb_nr = larb_nr;
+
+	platform_set_drvdata(pdev, data);
+
+	ret = mtk_iommu_hw_init(data);
+	if (ret)
+		return ret;
+
+	if (!iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type,  &mtk_iommu_ops);
+
+	return component_master_add_with_match(dev, &mtk_iommu_com_ops, match);
+}
+
+static int mtk_iommu_remove(struct platform_device *pdev)
+{
+	struct mtk_iommu_data *data = platform_get_drvdata(pdev);
+
+	if (iommu_present(&platform_bus_type))
+		bus_set_iommu(&platform_bus_type, NULL);
+
+	clk_disable_unprepare(data->bclk);
+	devm_free_irq(&pdev->dev, data->irq, data);
+	component_master_del(&pdev->dev, &mtk_iommu_com_ops);
+	return 0;
+}
+
+static int __maybe_unused mtk_iommu_suspend(struct device *dev)
+{
+	struct mtk_iommu_data *data = dev_get_drvdata(dev);
+	struct mtk_iommu_suspend_reg *reg = &data->reg;
+	void __iomem *base = data->base;
+
+	reg->standard_axi_mode = readl_relaxed(base +
+					       REG_MMU_STANDARD_AXI_MODE);
+	reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM);
+	reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
+	reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL);
+	return 0;
+}
+
+static int __maybe_unused mtk_iommu_resume(struct device *dev)
+{
+	struct mtk_iommu_data *data = dev_get_drvdata(dev);
+	struct mtk_iommu_suspend_reg *reg = &data->reg;
+	void __iomem *base = data->base;
+
+	writel_relaxed(data->m4u_dom->pgt_pa, base + REG_MMU_PT_BASE_ADDR);
+	writel_relaxed(reg->standard_axi_mode,
+		       base + REG_MMU_STANDARD_AXI_MODE);
+	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM);
+	writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+	writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL);
+	writel_relaxed(data->protect_base, base + REG_MMU_IVRP_PADDR);
+	return 0;
+}
+
+const struct dev_pm_ops mtk_iommu_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
+};
+
+static struct platform_driver mtk_iommu_driver = {
+	.probe	= mtk_iommu_probe,
+	.remove	= mtk_iommu_remove,
+	.driver	= {
+		.name = "mtk-iommu",
+		.of_match_table = mtk_iommu_of_ids,
+		.pm = &mtk_iommu_pm_ops,
+	}
+};
+
+static int __init m4u_init(void)
+{
+	return platform_driver_register(&mtk_iommu_driver);
+}
+
+static void __exit m4u_exit(void)
+{
+	return platform_driver_unregister(&mtk_iommu_driver);
+}
+
+subsys_initcall(m4u_init);
+module_exit(m4u_exit);
+
+MODULE_DESCRIPTION("IOMMU API for MTK architected m4u v1 implementations");
+MODULE_AUTHOR("Honghui Zhang <honghui.zhang at mediatek.com>");
+MODULE_LICENSE("GPL v2");
-- 
1.8.1.1.dirty



From architt at codeaurora.org  Wed Jun  8 03:09:03 2016
From: architt at codeaurora.org (Archit Taneja)
Date: Wed, 8 Jun 2016 15:39:03 +0530
Subject: [PATCH v2 08/20] drm: msm: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <1465300095-16971-9-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-9-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <5757EEBF.1010501@codeaurora.org>

Hi,

On 06/07/2016 05:18 PM, Boris Brezillon wrote:
> For all outputs except DSI we have a 1:1 relationship between connectors
> and encoders and the driver is relying on the atomic helpers: we can
> drop the custom ->best_encoder() and let the core call
> drm_atomic_helper_best_encoder() for us.

Works fine with msm.

Tested-by: Archit Taneja <architt at codeaurora.org>

Thanks,
Archit

>
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> ---
>   drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
>   drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
>   drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
>   3 files changed, 27 deletions(-)
>
> diff --git a/drivers/gpu/drm/msm/edp/edp_connector.c b/drivers/gpu/drm/msm/edp/edp_connector.c
> index 72360cd..5960628 100644
> --- a/drivers/gpu/drm/msm/edp/edp_connector.c
> +++ b/drivers/gpu/drm/msm/edp/edp_connector.c
> @@ -91,15 +91,6 @@ static int edp_connector_mode_valid(struct drm_connector *connector,
>   	return MODE_OK;
>   }
>
> -static struct drm_encoder *
> -edp_connector_best_encoder(struct drm_connector *connector)
> -{
> -	struct edp_connector *edp_connector = to_edp_connector(connector);
> -
> -	DBG("");
> -	return edp_connector->edp->encoder;
> -}
> -
>   static const struct drm_connector_funcs edp_connector_funcs = {
>   	.dpms = drm_atomic_helper_connector_dpms,
>   	.detect = edp_connector_detect,
> @@ -113,7 +104,6 @@ static const struct drm_connector_funcs edp_connector_funcs = {
>   static const struct drm_connector_helper_funcs edp_connector_helper_funcs = {
>   	.get_modes = edp_connector_get_modes,
>   	.mode_valid = edp_connector_mode_valid,
> -	.best_encoder = edp_connector_best_encoder,
>   };
>
>   /* initialize connector */
> diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
> index b15d726..a2515b4 100644
> --- a/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
> +++ b/drivers/gpu/drm/msm/hdmi/hdmi_connector.c
> @@ -406,13 +406,6 @@ static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
>   	return 0;
>   }
>
> -static struct drm_encoder *
> -msm_hdmi_connector_best_encoder(struct drm_connector *connector)
> -{
> -	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
> -	return hdmi_connector->hdmi->encoder;
> -}
> -
>   static const struct drm_connector_funcs hdmi_connector_funcs = {
>   	.dpms = drm_atomic_helper_connector_dpms,
>   	.detect = hdmi_connector_detect,
> @@ -426,7 +419,6 @@ static const struct drm_connector_funcs hdmi_connector_funcs = {
>   static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
>   	.get_modes = msm_hdmi_connector_get_modes,
>   	.mode_valid = msm_hdmi_connector_mode_valid,
> -	.best_encoder = msm_hdmi_connector_best_encoder,
>   };
>
>   /* initialize connector */
> diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
> index 2648cd7..353429b 100644
> --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
> +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c
> @@ -90,14 +90,6 @@ static int mdp4_lvds_connector_mode_valid(struct drm_connector *connector,
>   	return MODE_OK;
>   }
>
> -static struct drm_encoder *
> -mdp4_lvds_connector_best_encoder(struct drm_connector *connector)
> -{
> -	struct mdp4_lvds_connector *mdp4_lvds_connector =
> -			to_mdp4_lvds_connector(connector);
> -	return mdp4_lvds_connector->encoder;
> -}
> -
>   static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
>   	.dpms = drm_atomic_helper_connector_dpms,
>   	.detect = mdp4_lvds_connector_detect,
> @@ -111,7 +103,6 @@ static const struct drm_connector_funcs mdp4_lvds_connector_funcs = {
>   static const struct drm_connector_helper_funcs mdp4_lvds_connector_helper_funcs = {
>   	.get_modes = mdp4_lvds_connector_get_modes,
>   	.mode_valid = mdp4_lvds_connector_mode_valid,
> -	.best_encoder = mdp4_lvds_connector_best_encoder,
>   };
>
>   /* initialize connector */
>

-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora 
Forum, hosted by The Linux Foundation


From matthias.bgg at gmail.com  Wed Jun  8 03:45:54 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Wed, 8 Jun 2016 12:45:54 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1465364427.9963.13.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41>
Message-ID: <5757F762.4020908@gmail.com>



On 08/06/16 07:40, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>
>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>
>>>
>> [...]
>>
>>>>>>>>>>>> +
>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>> +        }
>>>>>>>>>>>> +
>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>> +    }
>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>> +}
>>>>>>>>>>>> +
>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>> +{
>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>> +    else
>>>>>>>>>>>> +        return;
>>>>>>>>>>>> +
>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>> +
>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>
>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>> commands in the
>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>> guarantees
>>>>>>>>>>> about dma_handle?
>>>>>>>>>>
>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>> 2. Yes.
>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>> calling
>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>> use this
>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>> currently
>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>
>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>     From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>     From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>
>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>
>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>> when it is running ISR.
>>>>>>>>
>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>
>>>>>>>
>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>> we need to do it in a thread context.
>>>>>>
>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>> else display will drop frame.
>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>> 32 ms.sometimes.
>>>>>>
>>>>>
>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>
>>>> The order should be kept.
>>>> Let me use mouse cursor as an example.
>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>> If the order is not kept, the result could become A -> C -> B.
>>>>
>>>
>>> Got it, thanks for the clarification.
>>>
>>
>> I think a way to get rid of the workqueue is to use a timer, which gets
>> programmed to the time a timeout in the first task in the busy list
>> would happen. Everytime we update the busy list (e.g. because of task
>> got finished by the thread), we update the timer. When the timer
>> triggers, which hopefully won't happen too often, we return timeout on
>> the busy list elements, until the time is lower then the actual time.
>>
>> At least with this we can reduce the data structures in this driver and
>> make it more lightweight.
>
>  From my understanding, your proposed method can handle timeout case.
>
> However, the workqueue is also in charge of releasing tasks.
> Do you take releasing tasks into consideration by using the proposed
> timer method?
> Furthermore, I think the code will become more complex if we also use
> timer to implement releasing tasks.
>

Can't we call
         clk_disable_unprepare(cmdq->clock);
         cmdq_task_release(task);
after invoking the callback?
Regrading the clock, wouldn't it be easier to handle the clock 
enable/disable depending on the state of task_busy_list? I suppose we 
can't as we would need to check the task_busy_list of all threads, right?

Regards,
Matthias


From matthias.bgg at gmail.com  Wed Jun  8 04:17:07 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Wed, 8 Jun 2016 13:17:07 +0200
Subject: [PATCH v2 07/20] drm: mediatek: Rely on the default
 ->best_encoder() behavior
In-Reply-To: <1465300095-16971-8-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-8-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <5757FEB3.8050001@gmail.com>



On 07/06/16 13:48, Boris Brezillon wrote:
> We have a 1:1 relationship between connectors and encoders and the
> driver is relying on the atomic helpers: we can drop the custom
> ->best_encoder() implementation and let the core call
> drm_atomic_helper_best_encoder() for us.
>
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>

Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

> ---
>   drivers/gpu/drm/mediatek/mtk_dsi.c | 9 ---------
>   1 file changed, 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c
> index 2d808e5..7343ffc 100644
> --- a/drivers/gpu/drm/mediatek/mtk_dsi.c
> +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
> @@ -575,14 +575,6 @@ static int mtk_dsi_connector_get_modes(struct drm_connector *connector)
>   	return drm_panel_get_modes(dsi->panel);
>   }
>
> -static struct drm_encoder *mtk_dsi_connector_best_encoder(
> -		struct drm_connector *connector)
> -{
> -	struct mtk_dsi *dsi = connector_to_dsi(connector);
> -
> -	return &dsi->encoder;
> -}
> -
>   static const struct drm_encoder_helper_funcs mtk_dsi_encoder_helper_funcs = {
>   	.mode_fixup = mtk_dsi_encoder_mode_fixup,
>   	.mode_set = mtk_dsi_encoder_mode_set,
> @@ -603,7 +595,6 @@ static const struct drm_connector_funcs mtk_dsi_connector_funcs = {
>   static const struct drm_connector_helper_funcs
>   	mtk_dsi_connector_helper_funcs = {
>   	.get_modes = mtk_dsi_connector_get_modes,
> -	.best_encoder = mtk_dsi_connector_best_encoder,
>   };
>
>   static int mtk_drm_attach_bridge(struct drm_bridge *bridge,
>


From matthias.bgg at gmail.com  Wed Jun  8 04:22:55 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Wed, 8 Jun 2016 13:22:55 +0200
Subject: [PATCH 2/3] pwm: Add MediaTek MT2701 display PWM driver support
In-Reply-To: <1465370478.5209.30.camel@mhfsdcap03>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-3-git-send-email-weiqing.kong@mediatek.com>
 <5751A3DD.7020402@gmail.com> <1465370478.5209.30.camel@mhfsdcap03>
Message-ID: <5758000F.7050503@gmail.com>



On 08/06/16 09:21, weiqing kong wrote:
> On Fri, 2016-06-03 at 17:35 +0200, Matthias Brugger wrote:
>>
>> On 30/05/16 10:41, Weiqing Kong wrote:
>>> Use the mtk_pwm_data struction to define different registers
>>> and add MT2701 specific register operations, such as MT2701
>>> doesn't have commit register, needs to disable double buffer
>>> before writing register, and needs to select manual mode
>>> and use PWM_PERIOD/PWM_HIGH_WIDTH.
>>>
>>> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
>>> ---
>>>    drivers/pwm/pwm-mtk-disp.c | 89 +++++++++++++++++++++++++++++++++++++---------
>>>    1 file changed, 72 insertions(+), 17 deletions(-)
>>>
>>> diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
>>> index 0ad3385..03b9c9e 100644
>>> --- a/drivers/pwm/pwm-mtk-disp.c
>>> +++ b/drivers/pwm/pwm-mtk-disp.c
>>> @@ -18,33 +18,44 @@
>>>    #include <linux/io.h>
>>>    #include <linux/module.h>
>>>    #include <linux/of.h>
>>> +#include <linux/of_device.h>
>>>    #include <linux/platform_device.h>
>>>    #include <linux/pwm.h>
>>>    #include <linux/slab.h>
>>>
>>>    #define DISP_PWM_EN		0x00
>>> -#define PWM_ENABLE_MASK		BIT(0)
>>>
>>>    #define DISP_PWM_COMMIT		0x08
>>>    #define PWM_COMMIT_MASK		BIT(0)
>>>
>>> -#define DISP_PWM_CON_0		0x10
>>>    #define PWM_CLKDIV_SHIFT	16
>>>    #define PWM_CLKDIV_MAX		0x3ff
>>>    #define PWM_CLKDIV_MASK		(PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
>>>
>>> -#define DISP_PWM_CON_1		0x14
>>>    #define PWM_PERIOD_BIT_WIDTH	12
>>>    #define PWM_PERIOD_MASK		((1 << PWM_PERIOD_BIT_WIDTH) - 1)
>>>
>>>    #define PWM_HIGH_WIDTH_SHIFT	16
>>>    #define PWM_HIGH_WIDTH_MASK	(0x1fff << PWM_HIGH_WIDTH_SHIFT)
>>>
>>> +#define MT2701_PWM_MANUAL_SEL_MASK	BIT(1)
>>> +#define MT2701_PWM_BLS_DEBUG		0xb0
>>
>> Do we need to set magic 0x3 in the debug register? If so, this should be
>> part of mtk_pwm_data, just as con0_sel.
>
> Do you mean that add bls_debug into struct mtk_pwm_data,
> and set 0x3 in mt2701 data, 0x0 in mt8173 data?
>

Yes.

>>
>>> +#define MT2701_PWM_BLS_DEBUG_MASK	0x3
>>> +
>>> +struct mtk_pwm_data {
>>> +	unsigned int enable_bit;
>>> +	unsigned int con0;
>>> +	unsigned int con0_sel;
>>> +	unsigned int con1;
>>> +	bool have_commit_reg;
>>> +};
>>> +
>>>    struct mtk_disp_pwm {
>>>    	struct pwm_chip chip;
>>>    	struct clk *clk_main;
>>>    	struct clk *clk_mm;
>>>    	void __iomem *base;
>>> +	const struct mtk_pwm_data *data;
>>
>> Couldn't that just be the offset for the commit register and set it to
>> 0x0 if the commit register is not present. Right now, we suppose the
>> DISP_PWM_EN is at offset 0x0, so this should not be a problem.
>>
>
> Do you mean that replace have_commit_reg with commit_reg_offset?
> just like this?
> bool have_commit_reg;
> ->
> unsigned int commit_reg_offset;
> and set commit_reg_offset 0x0 in mt2701, 0x1 in mt8173?
>

No, you can set comit_reg_offset to the value of DISP_PWM_COMMIT for 
mt8173 and 0x0 for mt2701. Then you can get rid of DISP_PWM_COMMIT.

Regards,
Matthias

>>>    };
>>>
>>>    static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
>>> @@ -106,12 +117,18 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
>>>    		return err;
>>>    	}
>>>
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
>>> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
>>> +				 PWM_CLKDIV_MASK,
>>>    				 clk_div << PWM_CLKDIV_SHIFT);
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
>>> -				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
>>> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
>>> +				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
>>> +				 value);
>>> +	if (mdp->data->have_commit_reg) {
>>> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
>>> +					 PWM_COMMIT_MASK, 0x1);
>>> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
>>> +					 PWM_COMMIT_MASK, 0x0);
>>
>> should be renamed to MT8173_PWM_COMMIT_MASK, although I'm not really a
>> friend of mixing up SoC specific defines with a Soc specific
>> mtk_disp_pwm structure.
>>
>> Regards,
>> Matthias
>
> ok, I will modify it as you said. thanks for you suggestion.
>
>>
>>> +	}
>>>
>>>    	clk_disable(mdp->clk_mm);
>>>    	clk_disable(mdp->clk_main);
>>> @@ -134,7 +151,9 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
>>>    		return err;
>>>    	}
>>>
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
>>> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
>>> +				 mdp->data->enable_bit,
>>> +				 mdp->data->enable_bit);
>>>
>>>    	return 0;
>>>    }
>>> @@ -143,7 +162,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
>>>    {
>>>    	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
>>>
>>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
>>> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
>>> +				 mdp->data->enable_bit, 0x0);
>>>
>>>    	clk_disable(mdp->clk_mm);
>>>    	clk_disable(mdp->clk_main);
>>> @@ -156,12 +176,41 @@ static const struct pwm_ops mtk_disp_pwm_ops = {
>>>    	.owner = THIS_MODULE,
>>>    };
>>>
>>> +static const struct mtk_pwm_data mt8173_pwm_data = {
>>> +	.enable_bit = BIT(0),
>>> +	.con0 = 0x10,
>>> +	.con0_sel = 0x0,
>>> +	.con1 = 0x14,
>>> +	.have_commit_reg = true,
>>> +};
>>> +
>>> +static const struct mtk_pwm_data mt2701_pwm_data = {
>>> +	.enable_bit = BIT(16),
>>> +	.con0 = 0xa8,
>>> +	.con0_sel = 0x2,
>>> +	.con1 = 0xac,
>>> +	.have_commit_reg = false,
>>> +};
>>> +
>>> +static const struct of_device_id mtk_disp_pwm_of_match[] = {
>>> +	{ .compatible = "mediatek,mt2701-disp-bls", .data = &mt2701_pwm_data},
>>> +	{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
>>> +	{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
>>> +	{ }
>>> +};
>>> +MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
>>> +
>>>    static int mtk_disp_pwm_probe(struct platform_device *pdev)
>>>    {
>>> +	const struct of_device_id *id;
>>>    	struct mtk_disp_pwm *mdp;
>>>    	struct resource *r;
>>>    	int ret;
>>>
>>> +	id = of_match_device(mtk_disp_pwm_of_match, &pdev->dev);
>>> +	if (!id)
>>> +		return -EINVAL;
>>> +
>>>    	mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL);
>>>    	if (!mdp)
>>>    		return -ENOMEM;
>>> @@ -191,6 +240,7 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
>>>    	mdp->chip.ops = &mtk_disp_pwm_ops;
>>>    	mdp->chip.base = -1;
>>>    	mdp->chip.npwm = 1;
>>> +	mdp->data = id->data;
>>>
>>>    	ret = pwmchip_add(&mdp->chip);
>>>    	if (ret < 0) {
>>> @@ -200,6 +250,18 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
>>>
>>>    	platform_set_drvdata(pdev, mdp);
>>>
>>> +	/*
>>> +	 * For MT2701, disable double buffer before writing register
>>> +	 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
>>> +	 */
>>> +	if (!mdp->data->have_commit_reg) {
>>> +		mtk_disp_pwm_update_bits(mdp, MT2701_PWM_BLS_DEBUG,
>>> +					 MT2701_PWM_BLS_DEBUG_MASK, 0x3);
>>> +		mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
>>> +					 MT2701_PWM_MANUAL_SEL_MASK,
>>> +					 mdp->data->con0_sel);
>>> +	}
>>> +
>>>    	return 0;
>>>
>>>    disable_clk_mm:
>>> @@ -221,13 +283,6 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
>>>    	return ret;
>>>    }
>>>
>>> -static const struct of_device_id mtk_disp_pwm_of_match[] = {
>>> -	{ .compatible = "mediatek,mt8173-disp-pwm" },
>>> -	{ .compatible = "mediatek,mt6595-disp-pwm" },
>>> -	{ }
>>> -};
>>> -MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
>>> -
>>>    static struct platform_driver mtk_disp_pwm_driver = {
>>>    	.driver = {
>>>    		.name = "mediatek-disp-pwm",
>>>
>
>


From weiqing.kong at mediatek.com  Wed Jun  8 05:05:48 2016
From: weiqing.kong at mediatek.com (weiqing kong)
Date: Wed, 8 Jun 2016 20:05:48 +0800
Subject: [PATCH 2/3] pwm: Add MediaTek MT2701 display PWM driver support
In-Reply-To: <5758000F.7050503@gmail.com>
References: <1464597712-5399-1-git-send-email-weiqing.kong@mediatek.com>
 <1464597712-5399-3-git-send-email-weiqing.kong@mediatek.com>
 <5751A3DD.7020402@gmail.com> <1465370478.5209.30.camel@mhfsdcap03>
 <5758000F.7050503@gmail.com>
Message-ID: <1465387548.5209.32.camel@mhfsdcap03>

On Wed, 2016-06-08 at 13:22 +0200, Matthias Brugger wrote:
> 
> On 08/06/16 09:21, weiqing kong wrote:
> > On Fri, 2016-06-03 at 17:35 +0200, Matthias Brugger wrote:
> >>
> >> On 30/05/16 10:41, Weiqing Kong wrote:
> >>> Use the mtk_pwm_data struction to define different registers
> >>> and add MT2701 specific register operations, such as MT2701
> >>> doesn't have commit register, needs to disable double buffer
> >>> before writing register, and needs to select manual mode
> >>> and use PWM_PERIOD/PWM_HIGH_WIDTH.
> >>>
> >>> Signed-off-by: Weiqing Kong <weiqing.kong at mediatek.com>
> >>> ---
> >>>    drivers/pwm/pwm-mtk-disp.c | 89 +++++++++++++++++++++++++++++++++++++---------
> >>>    1 file changed, 72 insertions(+), 17 deletions(-)
> >>>
> >>> diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c
> >>> index 0ad3385..03b9c9e 100644
> >>> --- a/drivers/pwm/pwm-mtk-disp.c
> >>> +++ b/drivers/pwm/pwm-mtk-disp.c
> >>> @@ -18,33 +18,44 @@
> >>>    #include <linux/io.h>
> >>>    #include <linux/module.h>
> >>>    #include <linux/of.h>
> >>> +#include <linux/of_device.h>
> >>>    #include <linux/platform_device.h>
> >>>    #include <linux/pwm.h>
> >>>    #include <linux/slab.h>
> >>>
> >>>    #define DISP_PWM_EN		0x00
> >>> -#define PWM_ENABLE_MASK		BIT(0)
> >>>
> >>>    #define DISP_PWM_COMMIT		0x08
> >>>    #define PWM_COMMIT_MASK		BIT(0)
> >>>
> >>> -#define DISP_PWM_CON_0		0x10
> >>>    #define PWM_CLKDIV_SHIFT	16
> >>>    #define PWM_CLKDIV_MAX		0x3ff
> >>>    #define PWM_CLKDIV_MASK		(PWM_CLKDIV_MAX << PWM_CLKDIV_SHIFT)
> >>>
> >>> -#define DISP_PWM_CON_1		0x14
> >>>    #define PWM_PERIOD_BIT_WIDTH	12
> >>>    #define PWM_PERIOD_MASK		((1 << PWM_PERIOD_BIT_WIDTH) - 1)
> >>>
> >>>    #define PWM_HIGH_WIDTH_SHIFT	16
> >>>    #define PWM_HIGH_WIDTH_MASK	(0x1fff << PWM_HIGH_WIDTH_SHIFT)
> >>>
> >>> +#define MT2701_PWM_MANUAL_SEL_MASK	BIT(1)
> >>> +#define MT2701_PWM_BLS_DEBUG		0xb0
> >>
> >> Do we need to set magic 0x3 in the debug register? If so, this should be
> >> part of mtk_pwm_data, just as con0_sel.
> >
> > Do you mean that add bls_debug into struct mtk_pwm_data,
> > and set 0x3 in mt2701 data, 0x0 in mt8173 data?
> >
> 
> Yes.
> 
> >>
> >>> +#define MT2701_PWM_BLS_DEBUG_MASK	0x3
> >>> +
> >>> +struct mtk_pwm_data {
> >>> +	unsigned int enable_bit;
> >>> +	unsigned int con0;
> >>> +	unsigned int con0_sel;
> >>> +	unsigned int con1;
> >>> +	bool have_commit_reg;
> >>> +};
> >>> +
> >>>    struct mtk_disp_pwm {
> >>>    	struct pwm_chip chip;
> >>>    	struct clk *clk_main;
> >>>    	struct clk *clk_mm;
> >>>    	void __iomem *base;
> >>> +	const struct mtk_pwm_data *data;
> >>
> >> Couldn't that just be the offset for the commit register and set it to
> >> 0x0 if the commit register is not present. Right now, we suppose the
> >> DISP_PWM_EN is at offset 0x0, so this should not be a problem.
> >>
> >
> > Do you mean that replace have_commit_reg with commit_reg_offset?
> > just like this?
> > bool have_commit_reg;
> > ->
> > unsigned int commit_reg_offset;
> > and set commit_reg_offset 0x0 in mt2701, 0x1 in mt8173?
> >
> 
> No, you can set comit_reg_offset to the value of DISP_PWM_COMMIT for 
> mt8173 and 0x0 for mt2701. Then you can get rid of DISP_PWM_COMMIT.
> 
> Regards,
> Matthias

ok, I got it, thanks.

> 
> >>>    };
> >>>
> >>>    static inline struct mtk_disp_pwm *to_mtk_disp_pwm(struct pwm_chip *chip)
> >>> @@ -106,12 +117,18 @@ static int mtk_disp_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
> >>>    		return err;
> >>>    	}
> >>>
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_0, PWM_CLKDIV_MASK,
> >>> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> >>> +				 PWM_CLKDIV_MASK,
> >>>    				 clk_div << PWM_CLKDIV_SHIFT);
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_CON_1,
> >>> -				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK, value);
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 1);
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT, PWM_COMMIT_MASK, 0);
> >>> +	mtk_disp_pwm_update_bits(mdp, mdp->data->con1,
> >>> +				 PWM_PERIOD_MASK | PWM_HIGH_WIDTH_MASK,
> >>> +				 value);
> >>> +	if (mdp->data->have_commit_reg) {
> >>> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> >>> +					 PWM_COMMIT_MASK, 0x1);
> >>> +		mtk_disp_pwm_update_bits(mdp, DISP_PWM_COMMIT,
> >>> +					 PWM_COMMIT_MASK, 0x0);
> >>
> >> should be renamed to MT8173_PWM_COMMIT_MASK, although I'm not really a
> >> friend of mixing up SoC specific defines with a Soc specific
> >> mtk_disp_pwm structure.
> >>
> >> Regards,
> >> Matthias
> >
> > ok, I will modify it as you said. thanks for you suggestion.
> >
> >>
> >>> +	}
> >>>
> >>>    	clk_disable(mdp->clk_mm);
> >>>    	clk_disable(mdp->clk_main);
> >>> @@ -134,7 +151,9 @@ static int mtk_disp_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
> >>>    		return err;
> >>>    	}
> >>>
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 1);
> >>> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> >>> +				 mdp->data->enable_bit,
> >>> +				 mdp->data->enable_bit);
> >>>
> >>>    	return 0;
> >>>    }
> >>> @@ -143,7 +162,8 @@ static void mtk_disp_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
> >>>    {
> >>>    	struct mtk_disp_pwm *mdp = to_mtk_disp_pwm(chip);
> >>>
> >>> -	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN, PWM_ENABLE_MASK, 0);
> >>> +	mtk_disp_pwm_update_bits(mdp, DISP_PWM_EN,
> >>> +				 mdp->data->enable_bit, 0x0);
> >>>
> >>>    	clk_disable(mdp->clk_mm);
> >>>    	clk_disable(mdp->clk_main);
> >>> @@ -156,12 +176,41 @@ static const struct pwm_ops mtk_disp_pwm_ops = {
> >>>    	.owner = THIS_MODULE,
> >>>    };
> >>>
> >>> +static const struct mtk_pwm_data mt8173_pwm_data = {
> >>> +	.enable_bit = BIT(0),
> >>> +	.con0 = 0x10,
> >>> +	.con0_sel = 0x0,
> >>> +	.con1 = 0x14,
> >>> +	.have_commit_reg = true,
> >>> +};
> >>> +
> >>> +static const struct mtk_pwm_data mt2701_pwm_data = {
> >>> +	.enable_bit = BIT(16),
> >>> +	.con0 = 0xa8,
> >>> +	.con0_sel = 0x2,
> >>> +	.con1 = 0xac,
> >>> +	.have_commit_reg = false,
> >>> +};
> >>> +
> >>> +static const struct of_device_id mtk_disp_pwm_of_match[] = {
> >>> +	{ .compatible = "mediatek,mt2701-disp-bls", .data = &mt2701_pwm_data},
> >>> +	{ .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data},
> >>> +	{ .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data},
> >>> +	{ }
> >>> +};
> >>> +MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> >>> +
> >>>    static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >>>    {
> >>> +	const struct of_device_id *id;
> >>>    	struct mtk_disp_pwm *mdp;
> >>>    	struct resource *r;
> >>>    	int ret;
> >>>
> >>> +	id = of_match_device(mtk_disp_pwm_of_match, &pdev->dev);
> >>> +	if (!id)
> >>> +		return -EINVAL;
> >>> +
> >>>    	mdp = devm_kzalloc(&pdev->dev, sizeof(*mdp), GFP_KERNEL);
> >>>    	if (!mdp)
> >>>    		return -ENOMEM;
> >>> @@ -191,6 +240,7 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >>>    	mdp->chip.ops = &mtk_disp_pwm_ops;
> >>>    	mdp->chip.base = -1;
> >>>    	mdp->chip.npwm = 1;
> >>> +	mdp->data = id->data;
> >>>
> >>>    	ret = pwmchip_add(&mdp->chip);
> >>>    	if (ret < 0) {
> >>> @@ -200,6 +250,18 @@ static int mtk_disp_pwm_probe(struct platform_device *pdev)
> >>>
> >>>    	platform_set_drvdata(pdev, mdp);
> >>>
> >>> +	/*
> >>> +	 * For MT2701, disable double buffer before writing register
> >>> +	 * and select manual mode and use PWM_PERIOD/PWM_HIGH_WIDTH.
> >>> +	 */
> >>> +	if (!mdp->data->have_commit_reg) {
> >>> +		mtk_disp_pwm_update_bits(mdp, MT2701_PWM_BLS_DEBUG,
> >>> +					 MT2701_PWM_BLS_DEBUG_MASK, 0x3);
> >>> +		mtk_disp_pwm_update_bits(mdp, mdp->data->con0,
> >>> +					 MT2701_PWM_MANUAL_SEL_MASK,
> >>> +					 mdp->data->con0_sel);
> >>> +	}
> >>> +
> >>>    	return 0;
> >>>
> >>>    disable_clk_mm:
> >>> @@ -221,13 +283,6 @@ static int mtk_disp_pwm_remove(struct platform_device *pdev)
> >>>    	return ret;
> >>>    }
> >>>
> >>> -static const struct of_device_id mtk_disp_pwm_of_match[] = {
> >>> -	{ .compatible = "mediatek,mt8173-disp-pwm" },
> >>> -	{ .compatible = "mediatek,mt6595-disp-pwm" },
> >>> -	{ }
> >>> -};
> >>> -MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match);
> >>> -
> >>>    static struct platform_driver mtk_disp_pwm_driver = {
> >>>    	.driver = {
> >>>    		.name = "mediatek-disp-pwm",
> >>>
> >
> >




From hs.liao at mediatek.com  Wed Jun  8 05:25:27 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Wed, 8 Jun 2016 20:25:27 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <5757F762.4020908@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
Message-ID: <1465388727.21326.8.camel@mtksdaap41>

Hi Matthias,

On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> 
> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> > Hi Matthias,
> >
> > On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> >>
> >> On 03/06/16 15:11, Matthias Brugger wrote:
> >>>
> >>>
> >> [...]
> >>
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>>>> +        }
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>>>> task->command_size,
> >>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>>>> +    }
> >>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>>>> +}
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>>>> +{
> >>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>>>> +    bool err;
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>>>> +        err = true;
> >>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>>>> +        err = false;
> >>>>>>>>>>>> +    else
> >>>>>>>>>>>> +        return;
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>>>> +
> >>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>>>> +                 list_entry) {
> >>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>>>
> >>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>>>> commands in the
> >>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>>>> guarantees
> >>>>>>>>>>> about dma_handle?
> >>>>>>>>>>
> >>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>>>> 2. Yes.
> >>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>>>> calling
> >>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>>>> use this
> >>>>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>>>> currently
> >>>>>>>>> executing. So I think this mecanism is not working.
> >>>>>>>>
> >>>>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>>>     From CPU's point of view, tasks are linked by the busy list.
> >>>>>>>>     From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>>>
> >>>>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>>>
> >>>>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>>>> when it is running ISR.
> >>>>>>>>
> >>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>>>
> >>>>>>>
> >>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>>>> we need to do it in a thread context.
> >>>>>>
> >>>>>> Actually, our initial implementation is similar to your suggestion,
> >>>>>> but display needs CMDQ to return callback function very precisely,
> >>>>>> else display will drop frame.
> >>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>>>> and CMDQ needs to call callback function in ISR.
> >>>>>> If we defer callback to workqueue, the time interval may be larger than
> >>>>>> 32 ms.sometimes.
> >>>>>>
> >>>>>
> >>>>> I think the problem is, that you implemented the workqueue as a ordered
> >>>>> workqueue, so there is no parallel processing. I'm still not sure why
> >>>>> you need the workqueue to be ordered. Can you please explain.
> >>>>
> >>>> The order should be kept.
> >>>> Let me use mouse cursor as an example.
> >>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> >>>> and task 3 means point C, our expected result is A -> B -> C.
> >>>> If the order is not kept, the result could become A -> C -> B.
> >>>>
> >>>
> >>> Got it, thanks for the clarification.
> >>>
> >>
> >> I think a way to get rid of the workqueue is to use a timer, which gets
> >> programmed to the time a timeout in the first task in the busy list
> >> would happen. Everytime we update the busy list (e.g. because of task
> >> got finished by the thread), we update the timer. When the timer
> >> triggers, which hopefully won't happen too often, we return timeout on
> >> the busy list elements, until the time is lower then the actual time.
> >>
> >> At least with this we can reduce the data structures in this driver and
> >> make it more lightweight.
> >
> >  From my understanding, your proposed method can handle timeout case.
> >
> > However, the workqueue is also in charge of releasing tasks.
> > Do you take releasing tasks into consideration by using the proposed
> > timer method?
> > Furthermore, I think the code will become more complex if we also use
> > timer to implement releasing tasks.
> >
> 
> Can't we call
>          clk_disable_unprepare(cmdq->clock);
>          cmdq_task_release(task);
> after invoking the callback?

Do you mean just call these two functions in ISR?
My major concern is dma_free_coherent() and kfree() in
cmdq_task_release(task).
Therefore, your suggestion is to use GFP_ATOMIC for both
dma_alloc_coherent() and kzalloc(). Right?
If so, I can try to implement timeout by timer, and discuss with you
if I have further questions.

> Regrading the clock, wouldn't it be easier to handle the clock 
> enable/disable depending on the state of task_busy_list? I suppose we 
> can't as we would need to check the task_busy_list of all threads, right?
> 
> Regards,
> Matthias

Thanks,
HS



From matthias.bgg at gmail.com  Wed Jun  8 08:35:33 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Wed, 8 Jun 2016 17:35:33 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1465388727.21326.8.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41>
Message-ID: <57583B45.2080504@gmail.com>



On 08/06/16 14:25, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>
>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>> Hi Matthias,
>>>
>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>
>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>
>>>>>
>>>> [...]
>>>>
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>> +
>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>
>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>
>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>> calling
>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>> use this
>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>> currently
>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>
>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>      From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>      From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>
>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>
>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>> when it is running ISR.
>>>>>>>>>>
>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>> we need to do it in a thread context.
>>>>>>>>
>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>> else display will drop frame.
>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>> 32 ms.sometimes.
>>>>>>>>
>>>>>>>
>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>
>>>>>> The order should be kept.
>>>>>> Let me use mouse cursor as an example.
>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>
>>>>>
>>>>> Got it, thanks for the clarification.
>>>>>
>>>>
>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>> programmed to the time a timeout in the first task in the busy list
>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>> got finished by the thread), we update the timer. When the timer
>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>> the busy list elements, until the time is lower then the actual time.
>>>>
>>>> At least with this we can reduce the data structures in this driver and
>>>> make it more lightweight.
>>>
>>>   From my understanding, your proposed method can handle timeout case.
>>>
>>> However, the workqueue is also in charge of releasing tasks.
>>> Do you take releasing tasks into consideration by using the proposed
>>> timer method?
>>> Furthermore, I think the code will become more complex if we also use
>>> timer to implement releasing tasks.
>>>
>>
>> Can't we call
>>           clk_disable_unprepare(cmdq->clock);
>>           cmdq_task_release(task);
>> after invoking the callback?
>
> Do you mean just call these two functions in ISR?
> My major concern is dma_free_coherent() and kfree() in
> cmdq_task_release(task).

Why do we need the dma calls at all? Can't we just calculate the 
physical address using __pa(x)?

> Therefore, your suggestion is to use GFP_ATOMIC for both
> dma_alloc_coherent() and kzalloc(). Right?

I don't think we need GFP_ATOMIC, the critical path will just free the 
memory.

> If so, I can try to implement timeout by timer, and discuss with you
> if I have further questions.
>

Sounds good :)

Thanks,
Matthias

>> Regrading the clock, wouldn't it be easier to handle the clock
>> enable/disable depending on the state of task_busy_list? I suppose we
>> can't as we would need to check the task_busy_list of all threads, right?
>>
>> Regards,
>> Matthias
>
> Thanks,
> HS
>


From yt.shen at mediatek.com  Wed Jun  8 09:03:40 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:40 +0800
Subject: [RFC v3 0/5] MT2701 DRM support
Message-ID: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>

This is MT2701 DRM support RFC v3, based on 4.7-rc1.
Most codes are the same, except some register changed.

For example:
 - DISP_OVL address offset changed, color format definition changed.
 - DISP_RDMA fifo size changed.
 - DISP_COLOR offset changed.

We add a new component DISP_BLS, and the connection settings are updated.
And we have shadow register support in MT2701.

Changes since v2:
- Rename mtk_ddp_mux_sel to mtk_ddp_sout_sel
- Update mt2701_mtk_ddp_ext components
- Changed to prefix naming
- Reorder the patch series
- Use of_device_get_match_data() to get driver private data
- Use iopoll macros to implement mtk_disp_mutex_acquire()
- Removed empty device tree nodes

Changes since v1:
- Removed BLS bindings and codes, which belong to pwm driver
- Moved mtk_disp_mutex_acquire() just before mtk_crtc_ddp_config()
- Split patch into smaller parts
- Added const keyword to constant structure
- Removed codes for special memory align

The RFC depends on the following patch:
https://patchwork.kernel.org/patch/8832441/ ("dt-bindings: ARM: Mediatek: Document bindings for MT2701"),
https://patchwork.kernel.org/patch/9140487/ ("dt-bindings: pwm: Add MediaTek display PWM bindings"),
https://patchwork.kernel.org/patch/9137919/ ("dt-bindings: mediatek: add descriptions for mediatek mt2701 iommu and smi"),
https://patchwork.kernel.org/patch/9137923/ ("iommu/mediatek: move the common struct into header file"),
https://patchwork.kernel.org/patch/9137921/ ("memory/mediatek: add support for mt2701"),
https://patchwork.kernel.org/patch/9137927/ ("iommu/mediatek: add support for mtk iommu generation one HW"),
https://patchwork.kernel.org/patch/9137931/ ("ARM: dts: mt2701: add iommu/smi dtsi node for mt2701"),

Thanks,
yt.shen

YT Shen (5):
  drm/mediatek: rename macros, add chip prefix
  drm/mediatek: add *driver_data for different hardware settings
  drm/mediatek: add shadow register support
  drm/mediatek: add support for Mediatek SoC MT2701
  arm: dts: mt2701: Add display subsystem related nodes for MT2701

 arch/arm/boot/dts/mt2701.dtsi               |  106 +++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |   32 +++++--
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |   17 +++-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   75 +++++++++------
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c      |  135 ++++++++++++++++++++-------
 drivers/gpu/drm/mediatek/mtk_drm_ddp.h      |    2 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   35 +++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   15 +++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   57 +++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |    9 ++
 10 files changed, 395 insertions(+), 88 deletions(-)

-- 
1.7.9.5



From yt.shen at mediatek.com  Wed Jun  8 09:03:41 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:41 +0800
Subject: [RFC v3 1/5] drm/mediatek: rename macros, add chip prefix
In-Reply-To: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465401825-43642-2-git-send-email-yt.shen@mediatek.com>

Add MT8173 prefix for hardware related macros.

Signed-off-by: YT Shen <yt.shen at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c |   60 ++++++++++++++++----------------
 1 file changed, 30 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
index 17ba935..2fc4321 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -36,21 +36,21 @@
 #define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
 #define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
 
-#define MUTEX_MOD_DISP_OVL0		BIT(11)
-#define MUTEX_MOD_DISP_OVL1		BIT(12)
-#define MUTEX_MOD_DISP_RDMA0		BIT(13)
-#define MUTEX_MOD_DISP_RDMA1		BIT(14)
-#define MUTEX_MOD_DISP_RDMA2		BIT(15)
-#define MUTEX_MOD_DISP_WDMA0		BIT(16)
-#define MUTEX_MOD_DISP_WDMA1		BIT(17)
-#define MUTEX_MOD_DISP_COLOR0		BIT(18)
-#define MUTEX_MOD_DISP_COLOR1		BIT(19)
-#define MUTEX_MOD_DISP_AAL		BIT(20)
-#define MUTEX_MOD_DISP_GAMMA		BIT(21)
-#define MUTEX_MOD_DISP_UFOE		BIT(22)
-#define MUTEX_MOD_DISP_PWM0		BIT(23)
-#define MUTEX_MOD_DISP_PWM1		BIT(24)
-#define MUTEX_MOD_DISP_OD		BIT(25)
+#define MT8173_MUTEX_MOD_DISP_OVL0		BIT(11)
+#define MT8173_MUTEX_MOD_DISP_OVL1		BIT(12)
+#define MT8173_MUTEX_MOD_DISP_RDMA0		BIT(13)
+#define MT8173_MUTEX_MOD_DISP_RDMA1		BIT(14)
+#define MT8173_MUTEX_MOD_DISP_RDMA2		BIT(15)
+#define MT8173_MUTEX_MOD_DISP_WDMA0		BIT(16)
+#define MT8173_MUTEX_MOD_DISP_WDMA1		BIT(17)
+#define MT8173_MUTEX_MOD_DISP_COLOR0		BIT(18)
+#define MT8173_MUTEX_MOD_DISP_COLOR1		BIT(19)
+#define MT8173_MUTEX_MOD_DISP_AAL		BIT(20)
+#define MT8173_MUTEX_MOD_DISP_GAMMA		BIT(21)
+#define MT8173_MUTEX_MOD_DISP_UFOE		BIT(22)
+#define MT8173_MUTEX_MOD_DISP_PWM0		BIT(23)
+#define MT8173_MUTEX_MOD_DISP_PWM1		BIT(24)
+#define MT8173_MUTEX_MOD_DISP_OD		BIT(25)
 
 #define MUTEX_SOF_SINGLE_MODE		0
 #define MUTEX_SOF_DSI0			1
@@ -80,21 +80,21 @@ struct mtk_ddp {
 };
 
 static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
-	[DDP_COMPONENT_AAL] = MUTEX_MOD_DISP_AAL,
-	[DDP_COMPONENT_COLOR0] = MUTEX_MOD_DISP_COLOR0,
-	[DDP_COMPONENT_COLOR1] = MUTEX_MOD_DISP_COLOR1,
-	[DDP_COMPONENT_GAMMA] = MUTEX_MOD_DISP_GAMMA,
-	[DDP_COMPONENT_OD] = MUTEX_MOD_DISP_OD,
-	[DDP_COMPONENT_OVL0] = MUTEX_MOD_DISP_OVL0,
-	[DDP_COMPONENT_OVL1] = MUTEX_MOD_DISP_OVL1,
-	[DDP_COMPONENT_PWM0] = MUTEX_MOD_DISP_PWM0,
-	[DDP_COMPONENT_PWM1] = MUTEX_MOD_DISP_PWM1,
-	[DDP_COMPONENT_RDMA0] = MUTEX_MOD_DISP_RDMA0,
-	[DDP_COMPONENT_RDMA1] = MUTEX_MOD_DISP_RDMA1,
-	[DDP_COMPONENT_RDMA2] = MUTEX_MOD_DISP_RDMA2,
-	[DDP_COMPONENT_UFOE] = MUTEX_MOD_DISP_UFOE,
-	[DDP_COMPONENT_WDMA0] = MUTEX_MOD_DISP_WDMA0,
-	[DDP_COMPONENT_WDMA1] = MUTEX_MOD_DISP_WDMA1,
+	[DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
+	[DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
+	[DDP_COMPONENT_COLOR1] = MT8173_MUTEX_MOD_DISP_COLOR1,
+	[DDP_COMPONENT_GAMMA] = MT8173_MUTEX_MOD_DISP_GAMMA,
+	[DDP_COMPONENT_OD] = MT8173_MUTEX_MOD_DISP_OD,
+	[DDP_COMPONENT_OVL0] = MT8173_MUTEX_MOD_DISP_OVL0,
+	[DDP_COMPONENT_OVL1] = MT8173_MUTEX_MOD_DISP_OVL1,
+	[DDP_COMPONENT_PWM0] = MT8173_MUTEX_MOD_DISP_PWM0,
+	[DDP_COMPONENT_PWM1] = MT8173_MUTEX_MOD_DISP_PWM1,
+	[DDP_COMPONENT_RDMA0] = MT8173_MUTEX_MOD_DISP_RDMA0,
+	[DDP_COMPONENT_RDMA1] = MT8173_MUTEX_MOD_DISP_RDMA1,
+	[DDP_COMPONENT_RDMA2] = MT8173_MUTEX_MOD_DISP_RDMA2,
+	[DDP_COMPONENT_UFOE] = MT8173_MUTEX_MOD_DISP_UFOE,
+	[DDP_COMPONENT_WDMA0] = MT8173_MUTEX_MOD_DISP_WDMA0,
+	[DDP_COMPONENT_WDMA1] = MT8173_MUTEX_MOD_DISP_WDMA1,
 };
 
 static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
-- 
1.7.9.5



From yt.shen at mediatek.com  Wed Jun  8 09:03:42 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:42 +0800
Subject: [RFC v3 2/5] drm/mediatek: add *driver_data for different hardware
 settings
In-Reply-To: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465401825-43642-3-git-send-email-yt.shen@mediatek.com>

There are some hardware settings changed, between MT8173 & MT2701:
DISP_OVL address offset changed, color format definition changed.
DISP_RDMA fifo size changed.
DISP_COLOR offset changed.
And add prefix for mtk_ddp_main & mtk_ddp_ext & mutex_mod.

Signed-off-by: YT Shen <yt.shen at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |   26 ++++++++++++++++----------
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |   11 +++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c      |   11 +++++++----
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   27 +++++++++++++++++++++------
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   13 +++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   25 ++++++++++++++++++-------
 drivers/gpu/drm/mediatek/mtk_drm_drv.h      |    8 ++++++++
 7 files changed, 92 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 8f62671f..eb5c05e 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -40,8 +40,6 @@
 #define	OVL_RDMA_MEM_GMC	0x40402020
 
 #define OVL_CON_BYTE_SWAP	BIT(24)
-#define OVL_CON_CLRFMT_RGB565	(0 << 12)
-#define OVL_CON_CLRFMT_RGB888	(1 << 12)
 #define OVL_CON_CLRFMT_RGBA8888	(2 << 12)
 #define OVL_CON_CLRFMT_ARGB8888	(3 << 12)
 #define	OVL_CON_AEN		BIT(8)
@@ -136,18 +134,18 @@ static void mtk_ovl_layer_off(struct mtk_ddp_comp *comp, unsigned int idx)
 	writel(0x0, comp->regs + DISP_REG_OVL_RDMA_CTRL(idx));
 }
 
-static unsigned int ovl_fmt_convert(unsigned int fmt)
+static unsigned int ovl_fmt_convert(struct mtk_ddp_comp *comp, unsigned int fmt)
 {
 	switch (fmt) {
 	default:
 	case DRM_FORMAT_RGB565:
-		return OVL_CON_CLRFMT_RGB565;
+		return comp->data->ovl.fmt_rgb565;
 	case DRM_FORMAT_BGR565:
-		return OVL_CON_CLRFMT_RGB565 | OVL_CON_BYTE_SWAP;
+		return comp->data->ovl.fmt_rgb565 | OVL_CON_BYTE_SWAP;
 	case DRM_FORMAT_RGB888:
-		return OVL_CON_CLRFMT_RGB888;
+		return comp->data->ovl.fmt_rgb888;
 	case DRM_FORMAT_BGR888:
-		return OVL_CON_CLRFMT_RGB888 | OVL_CON_BYTE_SWAP;
+		return comp->data->ovl.fmt_rgb888 | OVL_CON_BYTE_SWAP;
 	case DRM_FORMAT_RGBX8888:
 	case DRM_FORMAT_RGBA8888:
 		return OVL_CON_CLRFMT_ARGB8888;
@@ -177,7 +175,7 @@ static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
 	if (!pending->enable)
 		mtk_ovl_layer_off(comp, idx);
 
-	con = ovl_fmt_convert(fmt);
+	con = ovl_fmt_convert(comp, fmt);
 	if (idx != 0)
 		con |= OVL_CON_AEN | OVL_CON_ALPHA;
 
@@ -185,7 +183,8 @@ static void mtk_ovl_layer_config(struct mtk_ddp_comp *comp, unsigned int idx,
 	writel_relaxed(pitch, comp->regs + DISP_REG_OVL_PITCH(idx));
 	writel_relaxed(src_size, comp->regs + DISP_REG_OVL_SRC_SIZE(idx));
 	writel_relaxed(offset, comp->regs + DISP_REG_OVL_OFFSET(idx));
-	writel_relaxed(addr, comp->regs + DISP_REG_OVL_ADDR(idx));
+	writel_relaxed(addr, comp->regs + comp->data->ovl.addr_offset
+					+ idx * 0x20);
 
 	if (pending->enable)
 		mtk_ovl_layer_on(comp, idx);
@@ -269,6 +268,8 @@ static int mtk_disp_ovl_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	priv->ddp_comp.data = of_device_get_match_data(dev);
+
 	platform_set_drvdata(pdev, priv);
 
 	ret = component_add(dev, &mtk_disp_ovl_component_ops);
@@ -285,8 +286,13 @@ static int mtk_disp_ovl_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct mtk_ddp_comp_driver_data mt8173_ovl_driver_data = {
+	.ovl = {0x0f40, 0, 1 << 12}
+};
+
 static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
-	{ .compatible = "mediatek,mt8173-disp-ovl", },
+	{ .compatible = "mediatek,mt8173-disp-ovl",
+	  .data = &mt8173_ovl_driver_data},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index 5fb80cb..fb0db50 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -122,7 +122,7 @@ static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
 	 */
 	threshold = width * height * vrefresh * 4 * 7 / 1000000;
 	reg = RDMA_FIFO_UNDERFLOW_EN |
-	      RDMA_FIFO_PSEUDO_SIZE(SZ_8K) |
+	      RDMA_FIFO_PSEUDO_SIZE(comp->data->rdma_fifo_pseudo_size) |
 	      RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold);
 	writel(reg, comp->regs + DISP_REG_RDMA_FIFO_CON);
 }
@@ -207,6 +207,8 @@ static int mtk_disp_rdma_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	priv->ddp_comp.data = of_device_get_match_data(dev);
+
 	platform_set_drvdata(pdev, priv);
 
 	ret = component_add(dev, &mtk_disp_rdma_component_ops);
@@ -223,8 +225,13 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct mtk_ddp_comp_driver_data mt8173_rdma_driver_data = {
+	.rdma_fifo_pseudo_size = SZ_8K,
+};
+
 static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
-	{ .compatible = "mediatek,mt8173-disp-rdma", },
+	{ .compatible = "mediatek,mt8173-disp-rdma",
+	  .data = &mt8173_rdma_driver_data},
 	{},
 };
 MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
index 2fc4321..8030769 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -77,9 +77,10 @@ struct mtk_ddp {
 	struct clk			*clk;
 	void __iomem			*regs;
 	struct mtk_disp_mutex		mutex[10];
+	const unsigned int		*mutex_mod;
 };
 
-static const unsigned int mutex_mod[DDP_COMPONENT_ID_MAX] = {
+static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
 	[DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
 	[DDP_COMPONENT_COLOR1] = MT8173_MUTEX_MOD_DISP_COLOR1,
@@ -247,7 +248,7 @@ void mtk_disp_mutex_add_comp(struct mtk_disp_mutex *mutex,
 		break;
 	default:
 		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
-		reg |= mutex_mod[id];
+		reg |= ddp->mutex_mod[id];
 		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
 		return;
 	}
@@ -273,7 +274,7 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
 		break;
 	default:
 		reg = readl_relaxed(ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
-		reg &= ~mutex_mod[id];
+		reg &= ~(ddp->mutex_mod[id]);
 		writel_relaxed(reg, ddp->regs + DISP_REG_MUTEX_MOD(mutex->id));
 		break;
 	}
@@ -326,6 +327,8 @@ static int mtk_ddp_probe(struct platform_device *pdev)
 		return PTR_ERR(ddp->regs);
 	}
 
+	ddp->mutex_mod = of_device_get_match_data(dev);
+
 	platform_set_drvdata(pdev, ddp);
 
 	return 0;
@@ -337,7 +340,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id ddp_driver_dt_match[] = {
-	{ .compatible = "mediatek,mt8173-disp-mutex" },
+	{ .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
 	{},
 };
 MODULE_DEVICE_TABLE(of, ddp_driver_dt_match);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 3970fcf..4b4e449 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -34,9 +34,8 @@
 #define DISP_REG_UFO_START			0x0000
 
 #define DISP_COLOR_CFG_MAIN			0x0400
-#define DISP_COLOR_START			0x0c00
-#define DISP_COLOR_WIDTH			0x0c50
-#define DISP_COLOR_HEIGHT			0x0c54
+#define DISP_COLOR_WIDTH			0x50
+#define DISP_COLOR_HEIGHT			0x54
 
 #define	OD_RELAY_MODE		BIT(0)
 
@@ -48,15 +47,15 @@
 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
 			     unsigned int h, unsigned int vrefresh)
 {
-	writel(w, comp->regs + DISP_COLOR_WIDTH);
-	writel(h, comp->regs + DISP_COLOR_HEIGHT);
+	writel(w, comp->regs + comp->data->color_offset + DISP_COLOR_WIDTH);
+	writel(h, comp->regs + comp->data->color_offset + DISP_COLOR_HEIGHT);
 }
 
 static void mtk_color_start(struct mtk_ddp_comp *comp)
 {
 	writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL,
 	       comp->regs + DISP_COLOR_CFG_MAIN);
-	writel(0x1, comp->regs + DISP_COLOR_START);
+	writel(0x1, comp->regs + comp->data->color_offset);
 }
 
 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
@@ -131,6 +130,16 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
 };
 
+static const struct mtk_ddp_comp_driver_data mt8173_color_driver_data = {
+	.color_offset = 0x0c00,
+};
+
+static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt8173-disp-color",
+	  .data = &mt8173_color_driver_data},
+	{},
+};
+
 int mtk_ddp_comp_get_id(struct device_node *node,
 			enum mtk_ddp_comp_type comp_type)
 {
@@ -153,6 +162,7 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 	enum mtk_ddp_comp_type type;
 	struct device_node *larb_node;
 	struct platform_device *larb_pdev;
+	const struct of_device_id *match;
 
 	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
 		return -EINVAL;
@@ -177,6 +187,11 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 
 	type = mtk_ddp_matches[comp_id].type;
 
+	if (type == MTK_DISP_COLOR) {
+		match = of_match_node(mtk_disp_color_driver_dt_match, node);
+		comp->data = match->data;
+	}
+
 	/* Only DMA capable components need the LARB property */
 	comp->larb_dev = NULL;
 	if (type != MTK_DISP_OVL &&
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 6b13ba9..53065c7 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -75,6 +75,18 @@ struct mtk_ddp_comp_funcs {
 			     struct mtk_plane_state *state);
 };
 
+struct mtk_ddp_comp_driver_data {
+	union {
+		struct ovl {
+			unsigned int addr_offset;
+			unsigned int fmt_rgb565;
+			unsigned int fmt_rgb888;
+		} ovl;
+		unsigned int rdma_fifo_pseudo_size;
+		unsigned int color_offset;
+	};
+};
+
 struct mtk_ddp_comp {
 	struct clk *clk;
 	void __iomem *regs;
@@ -82,6 +94,7 @@ struct mtk_ddp_comp {
 	struct device *larb_dev;
 	enum mtk_ddp_comp_id id;
 	const struct mtk_ddp_comp_funcs *funcs;
+	const struct mtk_ddp_comp_driver_data *data;
 };
 
 static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index b1223d5..80b4f54 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -109,7 +109,7 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
 	.atomic_commit = mtk_atomic_commit,
 };
 
-static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
+static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
 	DDP_COMPONENT_OVL0,
 	DDP_COMPONENT_COLOR0,
 	DDP_COMPONENT_AAL,
@@ -120,7 +120,7 @@ static const enum mtk_ddp_comp_id mtk_ddp_main[] = {
 	DDP_COMPONENT_PWM0,
 };
 
-static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
+static const enum mtk_ddp_comp_id mt8173_mtk_ddp_ext[] = {
 	DDP_COMPONENT_OVL1,
 	DDP_COMPONENT_COLOR1,
 	DDP_COMPONENT_GAMMA,
@@ -128,6 +128,13 @@ static const enum mtk_ddp_comp_id mtk_ddp_ext[] = {
 	DDP_COMPONENT_DPI0,
 };
 
+static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
+	.main_path = mt8173_mtk_ddp_main,
+	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
+	.ext_path = mt8173_mtk_ddp_ext,
+	.ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
+};
+
 static int mtk_drm_kms_init(struct drm_device *drm)
 {
 	struct mtk_drm_private *private = drm->dev_private;
@@ -170,17 +177,19 @@ static int mtk_drm_kms_init(struct drm_device *drm)
 	 * and each statically assigned to a crtc:
 	 * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
 	 */
-	ret = mtk_drm_crtc_create(drm, mtk_ddp_main, ARRAY_SIZE(mtk_ddp_main));
+	ret = mtk_drm_crtc_create(drm, private->data->main_path,
+				  private->data->main_len);
 	if (ret < 0)
 		goto err_component_unbind;
 	/* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
-	ret = mtk_drm_crtc_create(drm, mtk_ddp_ext, ARRAY_SIZE(mtk_ddp_ext));
+	ret = mtk_drm_crtc_create(drm, private->data->ext_path,
+				  private->data->ext_len);
 	if (ret < 0)
 		goto err_component_unbind;
 
 	/* Use OVL device for all DMA memory allocations */
-	np = private->comp_node[mtk_ddp_main[0]] ?:
-	     private->comp_node[mtk_ddp_ext[0]];
+	np = private->comp_node[private->data->main_path[0]] ?:
+	     private->comp_node[private->data->ext_path[0]];
 	pdev = of_find_device_by_node(np);
 	if (!pdev) {
 		ret = -ENODEV;
@@ -353,6 +362,7 @@ static int mtk_drm_probe(struct platform_device *pdev)
 
 	mutex_init(&private->commit.lock);
 	INIT_WORK(&private->commit.work, mtk_atomic_work);
+	private->data = of_device_get_match_data(dev);
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	private->config_regs = devm_ioremap_resource(dev, mem);
@@ -504,7 +514,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
 			 mtk_drm_sys_resume);
 
 static const struct of_device_id mtk_drm_of_ids[] = {
-	{ .compatible = "mediatek,mt8173-mmsys", },
+	{ .compatible = "mediatek,mt8173-mmsys",
+	  .data = &mt8173_mmsys_driver_data},
 	{ }
 };
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index aa93894..fa0b106 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -28,6 +28,13 @@ struct drm_fb_helper;
 struct drm_property;
 struct regmap;
 
+struct mtk_mmsys_driver_data {
+	const enum mtk_ddp_comp_id *main_path;
+	unsigned int main_len;
+	const enum mtk_ddp_comp_id *ext_path;
+	unsigned int ext_len;
+};
+
 struct mtk_drm_private {
 	struct drm_device *drm;
 	struct device *dma_dev;
@@ -40,6 +47,7 @@ struct mtk_drm_private {
 	void __iomem *config_regs;
 	struct device_node *comp_node[DDP_COMPONENT_ID_MAX];
 	struct mtk_ddp_comp *ddp_comp[DDP_COMPONENT_ID_MAX];
+	const struct mtk_mmsys_driver_data *data;
 
 	struct {
 		struct drm_atomic_state *state;
-- 
1.7.9.5



From yt.shen at mediatek.com  Wed Jun  8 09:03:43 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:43 +0800
Subject: [RFC v3 3/5] drm/mediatek: add shadow register support
In-Reply-To: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465401825-43642-4-git-send-email-yt.shen@mediatek.com>

We need to acquire mutex before using the resources,
and need to release it after finished.
So we don't need to write registers in the blanking period.

Signed-off-by: YT Shen <yt.shen at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c |   75 +++++++++++++++++++------------
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c  |   22 +++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.h  |    2 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
 5 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 24aa3ba..80d9641 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -315,6 +315,42 @@ static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
 	pm_runtime_put(drm->dev);
 }
 
+static void mtk_crtc_ddp_config(struct drm_crtc *crtc)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
+	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
+	unsigned int i;
+
+	/*
+	 * TODO: instead of updating the registers here, we should prepare
+	 * working registers in atomic_commit and let the hardware command
+	 * queue update module registers on vblank.
+	 */
+	if (state->pending_config) {
+		mtk_ddp_comp_config(ovl, state->pending_width,
+				    state->pending_height,
+				    state->pending_vrefresh);
+
+		state->pending_config = false;
+	}
+
+	if (mtk_crtc->pending_planes) {
+		for (i = 0; i < OVL_LAYER_NR; i++) {
+			struct drm_plane *plane = &mtk_crtc->planes[i].base;
+			struct mtk_plane_state *plane_state;
+
+			plane_state = to_mtk_plane_state(plane->state);
+
+			if (plane_state->pending.config) {
+				mtk_ddp_comp_layer_config(ovl, i, plane_state);
+				plane_state->pending.config = false;
+			}
+		}
+		mtk_crtc->pending_planes = false;
+	}
+}
+
 static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
@@ -391,6 +427,7 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 				      struct drm_crtc_state *old_crtc_state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_drm_private *priv = crtc->dev->dev_private;
 	unsigned int pending_planes = 0;
 	int i;
 
@@ -409,6 +446,12 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 	}
 	if (pending_planes)
 		mtk_crtc->pending_planes = true;
+
+	if (priv->data->shadow_register) {
+		mtk_disp_mutex_acquire(mtk_crtc->mutex);
+		mtk_crtc_ddp_config(crtc);
+		mtk_disp_mutex_release(mtk_crtc->mutex);
+	}
 }
 
 static const struct drm_crtc_funcs mtk_crtc_funcs = {
@@ -453,36 +496,10 @@ err_cleanup_crtc:
 void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
-	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
-	unsigned int i;
+	struct mtk_drm_private *priv = crtc->dev->dev_private;
 
-	/*
-	 * TODO: instead of updating the registers here, we should prepare
-	 * working registers in atomic_commit and let the hardware command
-	 * queue update module registers on vblank.
-	 */
-	if (state->pending_config) {
-		mtk_ddp_comp_config(ovl, state->pending_width,
-				    state->pending_height,
-				    state->pending_vrefresh);
-
-		state->pending_config = false;
-	}
-
-	if (mtk_crtc->pending_planes) {
-		for (i = 0; i < OVL_LAYER_NR; i++) {
-			struct drm_plane *plane = &mtk_crtc->planes[i].base;
-			struct mtk_plane_state *plane_state;
-
-			plane_state = to_mtk_plane_state(plane->state);
-
-			if (plane_state->pending.config) {
-				mtk_ddp_comp_layer_config(ovl, i, plane_state);
-				plane_state->pending.config = false;
-			}
-		}
-		mtk_crtc->pending_planes = false;
-	}
+	if (!priv->data->shadow_register)
+		mtk_crtc_ddp_config(crtc);
 
 	mtk_drm_finish_page_flip(mtk_crtc);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
index 8030769..fa53806 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -12,6 +12,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
@@ -32,6 +33,7 @@
 #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
 
 #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
+#define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
 #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
 #define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
 #define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
@@ -300,6 +302,26 @@ void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
 	writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
 }
 
+void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+	u32 tmp;
+
+	writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
+	writel(1, ddp->regs + DISP_REG_MUTEX(mutex->id));
+	readl_poll_timeout_atomic(ddp->regs + DISP_REG_MUTEX(mutex->id), tmp,
+				  tmp & 0x2, 1, 10000);
+}
+
+void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex)
+{
+	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
+					   mutex[mutex->id]);
+
+	writel(0, ddp->regs + DISP_REG_MUTEX(mutex->id));
+}
+
 static int mtk_ddp_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
index 92c1175..f9a7991 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
@@ -37,5 +37,7 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
 				enum mtk_ddp_comp_id id);
 void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
 void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex);
+void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex);
 
 #endif /* MTK_DRM_DDP_H */
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 80b4f54..e9eb535 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -133,6 +133,7 @@ static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
 	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
 	.ext_path = mt8173_mtk_ddp_ext,
 	.ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
+	.shadow_register = false,
 };
 
 static int mtk_drm_kms_init(struct drm_device *drm)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
index fa0b106..94f8b66 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
@@ -33,6 +33,7 @@ struct mtk_mmsys_driver_data {
 	unsigned int main_len;
 	const enum mtk_ddp_comp_id *ext_path;
 	unsigned int ext_len;
+	bool shadow_register;
 };
 
 struct mtk_drm_private {
-- 
1.7.9.5



From yt.shen at mediatek.com  Wed Jun  8 09:03:44 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:44 +0800
Subject: [RFC v3 4/5] drm/mediatek: add support for Mediatek SoC MT2701
In-Reply-To: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465401825-43642-5-git-send-email-yt.shen@mediatek.com>

This patch add support for the Mediatek MT2701 DISP subsystem.
There is only one OVL engine in MT2701.

Signed-off-by: YT Shen <yt.shen at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    6 ++++
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    6 ++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp.c      |   42 +++++++++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |    8 +++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    2 ++
 drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   31 ++++++++++++++++++++
 6 files changed, 95 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index eb5c05e..1da0a71 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -286,11 +286,17 @@ static int mtk_disp_ovl_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct mtk_ddp_comp_driver_data mt2701_ovl_driver_data = {
+	.ovl = {0x0040, 1 << 12, 0}
+};
+
 static const struct mtk_ddp_comp_driver_data mt8173_ovl_driver_data = {
 	.ovl = {0x0f40, 0, 1 << 12}
 };
 
 static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-disp-ovl",
+	  .data = &mt2701_ovl_driver_data},
 	{ .compatible = "mediatek,mt8173-disp-ovl",
 	  .data = &mt8173_ovl_driver_data},
 	{},
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index fb0db50..506a353 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -225,11 +225,17 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct mtk_ddp_comp_driver_data mt2701_rdma_driver_data = {
+	.rdma_fifo_pseudo_size = SZ_4K,
+};
+
 static const struct mtk_ddp_comp_driver_data mt8173_rdma_driver_data = {
 	.rdma_fifo_pseudo_size = SZ_8K,
 };
 
 static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-disp-rdma",
+	  .data = &mt2701_rdma_driver_data},
 	{ .compatible = "mediatek,mt8173-disp-rdma",
 	  .data = &mt8173_rdma_driver_data},
 	{},
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
index fa53806..7ab6986 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
@@ -32,6 +32,10 @@
 #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN	0x0c8
 #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
 
+#define DISP_REG_CONFIG_DISP_OVL_MOUT_EN	0x030
+#define DISP_REG_CONFIG_OUT_SEL			0x04c
+#define DISP_REG_CONFIG_DSI_SEL			0x050
+
 #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
 #define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
 #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
@@ -54,6 +58,13 @@
 #define MT8173_MUTEX_MOD_DISP_PWM1		BIT(24)
 #define MT8173_MUTEX_MOD_DISP_OD		BIT(25)
 
+#define MT2701_MUTEX_MOD_DISP_OVL		BIT(3)
+#define MT2701_MUTEX_MOD_DISP_WDMA		BIT(6)
+#define MT2701_MUTEX_MOD_DISP_COLOR		BIT(7)
+#define MT2701_MUTEX_MOD_DISP_BLS		BIT(9)
+#define MT2701_MUTEX_MOD_DISP_RDMA0		BIT(10)
+#define MT2701_MUTEX_MOD_DISP_RDMA1		BIT(12)
+
 #define MUTEX_SOF_SINGLE_MODE		0
 #define MUTEX_SOF_DSI0			1
 #define MUTEX_SOF_DSI1			2
@@ -69,6 +80,10 @@
 #define DPI0_SEL_IN_RDMA1		0x1
 #define COLOR1_SEL_IN_OVL1		0x1
 
+#define OVL_MOUT_EN_RDMA		0x1
+#define BLS_TO_DSI_RDMA1_TO_DPI1	0x8
+#define DSI_SEL_IN_BLS			0x0
+
 struct mtk_disp_mutex {
 	int id;
 	bool claimed;
@@ -82,6 +97,15 @@ struct mtk_ddp {
 	const unsigned int		*mutex_mod;
 };
 
+static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
+	[DDP_COMPONENT_BLS] = MT2701_MUTEX_MOD_DISP_BLS,
+	[DDP_COMPONENT_COLOR0] = MT2701_MUTEX_MOD_DISP_COLOR,
+	[DDP_COMPONENT_OVL0] = MT2701_MUTEX_MOD_DISP_OVL,
+	[DDP_COMPONENT_RDMA0] = MT2701_MUTEX_MOD_DISP_RDMA0,
+	[DDP_COMPONENT_RDMA1] = MT2701_MUTEX_MOD_DISP_RDMA1,
+	[DDP_COMPONENT_WDMA0] = MT2701_MUTEX_MOD_DISP_WDMA,
+};
+
 static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
 	[DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
@@ -109,6 +133,9 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
 	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
 		*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
 		value = OVL0_MOUT_EN_COLOR0;
+	} else if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_RDMA0) {
+		*addr = DISP_REG_CONFIG_DISP_OVL_MOUT_EN;
+		value = OVL_MOUT_EN_RDMA;
 	} else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
 		*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
 		value = OD_MOUT_EN_RDMA0;
@@ -146,6 +173,9 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
 	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
 		*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
 		value = COLOR1_SEL_IN_OVL1;
+	} else if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
+		*addr = DISP_REG_CONFIG_DSI_SEL;
+		value = DSI_SEL_IN_BLS;
 	} else {
 		value = 0;
 	}
@@ -153,6 +183,15 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
 	return value;
 }
 
+static void mtk_ddp_sout_sel(void __iomem *config_regs,
+			    enum mtk_ddp_comp_id cur, enum mtk_ddp_comp_id next)
+{
+	if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
+		writel_relaxed(BLS_TO_DSI_RDMA1_TO_DPI1,
+			       config_regs + DISP_REG_CONFIG_OUT_SEL);
+	}
+}
+
 void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
 			      enum mtk_ddp_comp_id cur,
 			      enum mtk_ddp_comp_id next)
@@ -165,6 +204,8 @@ void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
 		writel_relaxed(reg, config_regs + addr);
 	}
 
+	mtk_ddp_sout_sel(config_regs, cur, next);
+
 	value = mtk_ddp_sel_in(cur, next, &addr);
 	if (value) {
 		reg = readl_relaxed(config_regs + addr) | value;
@@ -362,6 +403,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id ddp_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-disp-mutex", .data = mt2701_mutex_mod},
 	{ .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
 	{},
 };
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 4b4e449..87e0436 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -102,6 +102,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_PWM] = "pwm",
 	[MTK_DISP_MUTEX] = "mutex",
 	[MTK_DISP_OD] = "od",
+	[MTK_DISP_BLS] = "bls",
 };
 
 struct mtk_ddp_comp_match {
@@ -112,6 +113,7 @@ struct mtk_ddp_comp_match {
 
 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
+	[DDP_COMPONENT_BLS]	= { MTK_DISP_BLS,	0, NULL },
 	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
 	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
@@ -130,11 +132,17 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
 	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
 };
 
+static const struct mtk_ddp_comp_driver_data mt2701_color_driver_data = {
+	.color_offset = 0x0f00,
+};
+
 static const struct mtk_ddp_comp_driver_data mt8173_color_driver_data = {
 	.color_offset = 0x0c00,
 };
 
 static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-disp-color",
+	  .data = &mt2701_color_driver_data},
 	{ .compatible = "mediatek,mt8173-disp-color",
 	  .data = &mt8173_color_driver_data},
 	{},
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 53065c7..6e2d918 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -35,6 +35,7 @@ enum mtk_ddp_comp_type {
 	MTK_DISP_PWM,
 	MTK_DISP_MUTEX,
 	MTK_DISP_OD,
+	MTK_DISP_BLS,
 	MTK_DDP_COMP_TYPE_MAX,
 };
 
@@ -57,6 +58,7 @@ enum mtk_ddp_comp_id {
 	DDP_COMPONENT_UFOE,
 	DDP_COMPONENT_WDMA0,
 	DDP_COMPONENT_WDMA1,
+	DDP_COMPONENT_BLS,
 	DDP_COMPONENT_ID_MAX,
 };
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index e9eb535..b4b66a7 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -109,6 +109,19 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
 	.atomic_commit = mtk_atomic_commit,
 };
 
+static const enum mtk_ddp_comp_id mt2701_mtk_ddp_main[] = {
+	DDP_COMPONENT_OVL0,
+	DDP_COMPONENT_RDMA0,
+	DDP_COMPONENT_COLOR0,
+	DDP_COMPONENT_BLS,
+	DDP_COMPONENT_DSI0,
+};
+
+static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
+	DDP_COMPONENT_RDMA1,
+	DDP_COMPONENT_DPI0,
+};
+
 static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
 	DDP_COMPONENT_OVL0,
 	DDP_COMPONENT_COLOR0,
@@ -128,6 +141,14 @@ static const enum mtk_ddp_comp_id mt8173_mtk_ddp_ext[] = {
 	DDP_COMPONENT_DPI0,
 };
 
+static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
+	.main_path = mt2701_mtk_ddp_main,
+	.main_len = ARRAY_SIZE(mt2701_mtk_ddp_main),
+	.ext_path = mt2701_mtk_ddp_ext,
+	.ext_len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
+	.shadow_register = true,
+};
+
 static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
 	.main_path = mt8173_mtk_ddp_main,
 	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
@@ -332,18 +353,26 @@ static const struct component_master_ops mtk_drm_ops = {
 };
 
 static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
+	{ .compatible = "mediatek,mt2701-disp-ovl",   .data = (void *)MTK_DISP_OVL },
 	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
+	{ .compatible = "mediatek,mt2701-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
 	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
+	{ .compatible = "mediatek,mt2701-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
 	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
+	{ .compatible = "mediatek,mt2701-disp-color", .data = (void *)MTK_DISP_COLOR },
 	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
 	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
 	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
 	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
+	{ .compatible = "mediatek,mt2701-dsi",	      .data = (void *)MTK_DSI },
 	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
+	{ .compatible = "mediatek,mt2701-dpi",	      .data = (void *)MTK_DPI },
 	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
+	{ .compatible = "mediatek,mt2701-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
 	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
 	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
+	{ .compatible = "mediatek,mt2701-disp-bls",   .data = (void *)MTK_DISP_BLS },
 	{ }
 };
 
@@ -515,6 +544,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
 			 mtk_drm_sys_resume);
 
 static const struct of_device_id mtk_drm_of_ids[] = {
+	{ .compatible = "mediatek,mt2701-mmsys",
+	  .data = &mt2701_mmsys_driver_data},
 	{ .compatible = "mediatek,mt8173-mmsys",
 	  .data = &mt8173_mmsys_driver_data},
 	{ }
-- 
1.7.9.5



From yt.shen at mediatek.com  Wed Jun  8 09:03:45 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Thu, 9 Jun 2016 00:03:45 +0800
Subject: [RFC v3 5/5] arm: dts: mt2701: Add display subsystem related nodes
 for MT2701
In-Reply-To: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465401825-43642-6-git-send-email-yt.shen@mediatek.com>

This patch adds the device nodes for the DISP function blocks for MT2701

Signed-off-by: YT Shen <yt.shen at mediatek.com>
---
 arch/arm/boot/dts/mt2701.dtsi |  106 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 363de0d..b9054a8 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -24,6 +24,11 @@
 	compatible = "mediatek,mt2701";
 	interrupt-parent = <&sysirq>;
 
+	aliases {
+		rdma0 = &rdma0;
+		rdma1 = &rdma1;
+	};
+
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
@@ -171,6 +176,16 @@
 		power-domains = <&scpsys MT2701_POWER_DOMAIN_DISP>;
 	};
 
+	mipi_tx0: mipi-dphy at 10010000 {
+		compatible = "mediatek,mt2701-mipi-tx";
+		reg = <0 0x10010000 0 0x90>;
+		clocks = <&clk26m>;
+		clock-output-names = "mipi_tx0_pll";
+		#clock-cells = <0>;
+		#phy-cells = <0>;
+		status = "disabled";
+	};
+
 	sysirq: interrupt-controller at 10200100 {
 		compatible = "mediatek,mt2701-sysirq",
 			     "mediatek,mt6577-sysirq";
@@ -255,6 +270,68 @@
 		status = "disabled";
 	};
 
+	ovl at 14007000 {
+		compatible = "mediatek,mt2701-disp-ovl";
+		reg = <0 0x14007000 0 0x1000>;
+		interrupts = <GIC_SPI 153 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_OVL>;
+		iommus = <&iommu MT2701_M4U_PORT_DISP_OVL_0>;
+		mediatek,larb = <&larb0>;
+	};
+
+	rdma0: rdma at 14008000 {
+		compatible = "mediatek,mt2701-disp-rdma";
+		reg = <0 0x14008000 0 0x1000>;
+		interrupts = <GIC_SPI 152 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_RDMA>;
+		iommus = <&iommu MT2701_M4U_PORT_DISP_RDMA>;
+		mediatek,larb = <&larb0>;
+	};
+
+	wdma at 14009000 {
+		compatible = "mediatek,mt2701-disp-wdma";
+		reg = <0 0x14009000 0 0x1000>;
+		interrupts = <GIC_SPI 154 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_WDMA>;
+		iommus = <&iommu MT2701_M4U_PORT_DISP_WDMA>;
+		mediatek,larb = <&larb0>;
+	};
+
+	bls at 1400a000 {
+		compatible = "mediatek,mt2701-disp-bls";
+		reg = <0 0x1400a000 0 0x1000>;
+		interrupts = <GIC_SPI 155 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_BLS>;
+	};
+
+	color at 1400b000 {
+		compatible = "mediatek,mt2701-disp-color";
+		reg = <0 0x1400b000 0 0x1000>;
+		interrupts = <GIC_SPI 156 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_COLOR>;
+	};
+
+	dsi0: dsi at 1400c000 {
+		compatible = "mediatek,mt2701-dsi";
+		reg = <0 0x1400c000 0 0x1000>;
+		interrupts = <GIC_SPI 157 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DSI_ENGINE>, <&mmsys CLK_MM_DSI_DIG>,
+			 <&mipi_tx0>;
+		clock-names = "engine", "digital", "hs";
+		phys = <&mipi_tx0>;
+		phy-names = "dphy";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		status = "disabled";
+	};
+
+	mutex: mutex at 1400e000 {
+		compatible = "mediatek,mt2701-disp-mutex";
+		reg = <0 0x1400e000 0 0x1000>;
+		interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_MUTEX_32K>;
+	};
+
 	larb0: larb at 14010000 {
 		compatible = "mediatek,mt2701-smi-larb";
 		reg = <0 0x14010000 0 0x1000>;
@@ -265,6 +342,35 @@
 		power-domains = <&scpsys MT2701_POWER_DOMAIN_DISP>;
 	};
 
+	rdma1: rdma at 14012000 {
+		compatible = "mediatek,mt2701-disp-rdma";
+		reg = <0 0x14012000 0 0x1000>;
+		interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DISP_RDMA1>;
+		iommus = <&iommu MT2701_M4U_PORT_DISP_RDMA1>;
+		mediatek,larb = <&larb0>;
+	};
+
+	ufoe at 14013000 {
+		compatible = "mediatek,mt2701-disp-ufoe";
+		reg = <0 0x14013000 0 0x1000>;
+		clocks = <&mmsys CLK_MM_DISP_UFOE>;
+	};
+
+	dpi0: dpi at 14014000 {
+		compatible = "mediatek,mt2701-dpi";
+		reg = <0 0x14014000 0 0x1000>;
+		interrupts = <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&mmsys CLK_MM_DPI1_DIGL>,
+			 <&mmsys CLK_MM_DPI1_ENGINE>,
+			 <&topckgen CLK_TOP_DPI1_SEL>,
+			 <&topckgen CLK_TOP_TVDPLL>,
+			 <&topckgen CLK_TOP_TVDPLL_D2>,
+			 <&topckgen CLK_TOP_TVDPLL_D4>;
+		clock-names = "pixel", "engine", "sel","div1","div2","div4";
+		status = "disabled";
+	};
+
 	imgsys: syscon at 15000000 {
 		compatible = "mediatek,mt2701-imgsys", "syscon";
 		reg = <0 0x15000000 0 0x1000>;
-- 
1.7.9.5



From broonie at kernel.org  Thu Jun  9 03:44:24 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 9 Jun 2016 11:44:24 +0100
Subject: [alsa-devel] [PATCH v2 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1465356742.18601.7.camel@mtksdaap41>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-7-git-send-email-garlic.tseng@mediatek.com>
 <20160607163112.GD7510@sirena.org.uk>
 <1465356742.18601.7.camel@mtksdaap41>
Message-ID: <20160609104424.GU7510@sirena.org.uk>

On Wed, Jun 08, 2016 at 11:32:22AM +0800, Garlic Tseng wrote:
> On Tue, 2016-06-07 at 17:31 +0100, Mark Brown wrote:

> > > +	SOC_DAPM_SINGLE_AUTODISABLE("Multi ch asrc out3", PWR2_TOP_CON, 7, 1,
> > > +				    1),
> > > +};

> > On/off controls should end in Switch.

> Do you means that the name should end in Switch? Something like "Multi
> ch asrc out3 Switch" (or maybe a shorter one)

Yes, exactly.
-------------- 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-mediatek/attachments/20160609/7b7f7530/attachment.sig>

From broonie at kernel.org  Thu Jun  9 03:44:44 2016
From: broonie at kernel.org (Mark Brown)
Date: Thu, 9 Jun 2016 11:44:44 +0100
Subject: [alsa-devel] [PATCH v2 2/9] ASoC: mediatek: implement mediatek
 common structure
In-Reply-To: <1465351572.4197.17.camel@mtksdaap41>
References: <1464929784-5489-1-git-send-email-garlic.tseng@mediatek.com>
 <1464929784-5489-3-git-send-email-garlic.tseng@mediatek.com>
 <20160607155427.GB7510@sirena.org.uk>
 <1465351572.4197.17.camel@mtksdaap41>
Message-ID: <20160609104444.GV7510@sirena.org.uk>

On Wed, Jun 08, 2016 at 10:06:12AM +0800, Garlic Tseng wrote:
> On Tue, 2016-06-07 at 16:54 +0100, Mark Brown wrote:

> > Do you need to export the individual ops rather than just the ops
> > structure?

> Yes, in 2701 driver we modify some ops.

OK.
-------------- 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-mediatek/attachments/20160609/6f3c920d/attachment.sig>

From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:41 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:41 +0800
Subject: [PATCH v3,4/5] usb: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465543962-11678-5-git-send-email-chunfeng.yun@mediatek.com>

This patch adds support for the MediaTek USB3 controller
integrated into MT8173. It can be configured as Dual-Role
Device (DRD), Peripheral Only and Host Only (xHCI) modes.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/Kconfig                |    2 +
 drivers/usb/Makefile               |    1 +
 drivers/usb/mtu3/Kconfig           |   47 ++
 drivers/usb/mtu3/Makefile          |   20 +
 drivers/usb/mtu3/mtu3.h            |  422 +++++++++++++++++
 drivers/usb/mtu3/mtu3_core.c       |  879 ++++++++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_dr.c         |  348 ++++++++++++++
 drivers/usb/mtu3/mtu3_dr.h         |  108 +++++
 drivers/usb/mtu3/mtu3_gadget.c     |  731 ++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_gadget_ep0.c |  879 ++++++++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_host.c       |  294 ++++++++++++
 drivers/usb/mtu3/mtu3_hw_regs.h    |  474 +++++++++++++++++++
 drivers/usb/mtu3/mtu3_plat.c       |  490 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_qmu.c        |  599 ++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_qmu.h        |   43 ++
 15 files changed, 5337 insertions(+)
 create mode 100644 drivers/usb/mtu3/Kconfig
 create mode 100644 drivers/usb/mtu3/Makefile
 create mode 100644 drivers/usb/mtu3/mtu3.h
 create mode 100644 drivers/usb/mtu3/mtu3_core.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.h
 create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
 create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
 create mode 100644 drivers/usb/mtu3/mtu3_host.c
 create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
 create mode 100644 drivers/usb/mtu3/mtu3_plat.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..9ca0bf0 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -95,6 +95,8 @@ source "drivers/usb/usbip/Kconfig"
 
 endif
 
+source "drivers/usb/mtu3/Kconfig"
+
 source "drivers/usb/musb/Kconfig"
 
 source "drivers/usb/dwc3/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..7791af6 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
 obj-$(CONFIG_USB_MON)		+= mon/
+obj-$(CONFIG_USB_MTU3)		+= mtu3/
 
 obj-$(CONFIG_PCI)		+= host/
 obj-$(CONFIG_USB_EHCI_HCD)	+= host/
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
new file mode 100644
index 0000000..6850fb6
--- /dev/null
+++ b/drivers/usb/mtu3/Kconfig
@@ -0,0 +1,47 @@
+# For MTK USB3.0 IP
+
+config USB_MTU3
+	tristate "MediaTek USB3 Dual Role controller"
+	depends on (USB || USB_GADGET) && HAS_DMA
+	select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
+	help
+	  Say Y or M here if your system runs on MediaTek SoCs with
+	  Dual Role SuperSpeed USB controller. You can select usb
+	  mode as peripheral role or host role, or both.
+
+	  If you don't know what this is, please say N.
+
+	  Choose M here to compile this driver as a module, and it
+	  will be called mtu3.ko.
+
+
+if USB_MTU3
+choice
+	bool "MTU3 Mode Selection"
+	default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET)
+	default USB_MTU3_HOST if (USB && !USB_GADGET)
+	default USB_MTU3_GADGET if (!USB && USB_GADGET)
+
+config USB_MTU3_HOST
+	bool "Host only mode"
+	depends on USB=y || USB=USB_MTU3
+	help
+	  Select this when you want to use MTU3 in host mode only,
+	  thereby the gadget feature will be regressed.
+
+config USB_MTU3_GADGET
+	bool "Gadget only mode"
+	depends on USB_GADGET=y || USB_GADGET=USB_MTU3
+	help
+	  Select this when you want to use MTU3 in gadget mode only,
+	  thereby the host feature will be regressed.
+
+config USB_MTU3_DUAL_ROLE
+	bool "Dual Role mode"
+	depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
+	help
+	  This is the default mode of working of MTU3 controller where
+	  both host and gadget features are enabled.
+
+endchoice
+endif
diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile
new file mode 100644
index 0000000..4a66b01
--- /dev/null
+++ b/drivers/usb/mtu3/Makefile
@@ -0,0 +1,20 @@
+
+#ifeq ($(CONFIG_USB_DEBUG),y)
+	ccflags-y	+= -DDEBUG
+#endif
+
+obj-$(CONFIG_USB_MTU3)	+= mtu3.o
+
+mtu3-y	:= mtu3_plat.o
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+	mtu3-y	+= mtu3_host.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_GADGET) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+	mtu3-y	+= mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o mtu3_qmu.o
+endif
+
+ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),)
+	mtu3-y	+= mtu3_dr.o
+endif
\ No newline at end of file
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
new file mode 100644
index 0000000..8e2bed8
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3.h
@@ -0,0 +1,422 @@
+/*
+ * mtu3.h - MediaTek USB3 DRD header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 __MTU3_H__
+#define __MTU3_H__
+
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/workqueue.h>
+
+struct mtu3;
+struct mtu3_ep;
+struct mtu3_request;
+
+#include "mtu3_hw_regs.h"
+#include "mtu3_qmu.h"
+
+#define	MU3D_EP_TXCR0(epnum)	(U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_TXCR1(epnum)	(U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_TXCR2(epnum)	(U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
+
+#define	MU3D_EP_RXCR0(epnum)	(U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR1(epnum)	(U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR2(epnum)	(U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR3(epnum)	(U3D_RX1CSR3 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_RQCSR(epnum)	(U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQSAR(epnum)	(U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQCPR(epnum)	(U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQLDPR(epnum)	(U3D_RXQLDPR1 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_TQCSR(epnum)	(U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQSAR(epnum)	(U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQCPR(epnum)	(U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
+
+#define SSUSB_U3_CTRL(p)	(U3D_SSUSB_U3_CTRL_0P + ((p) * 0x08))
+#define SSUSB_U2_CTRL(p)	(U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
+
+#define MTU3_DRIVER_NAME	"mtu3"
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+#define MTU3_EP_ENABLED		BIT(0)
+#define MTU3_EP_STALL		BIT(1)
+#define MTU3_EP_WEDGE		BIT(2)
+#define MTU3_EP_BUSY		BIT(3)
+
+#define MTU3_U3_IP_SLOT_DEFAULT 2
+#define MTU3_U2_IP_SLOT_DEFAULT 1
+
+/**
+ * Normally the device works on HS or SS, to simplify fifo management,
+ * devide fifo into some 512B parts, use bitmap to manage it; And
+ * 128 bits size of bitmap is large enough, that means it can manage
+ * up to 64KB fifo size.
+ * NOTE: MTU3_EP_FIFO_UNIT should be power of two
+ */
+#define MTU3_EP_FIFO_UNIT		(1 << 9)
+#define MTU3_FIFO_BIT_SIZE		128
+#define MTU3_U2_IP_EP0_FIFO_SIZE	64
+
+/**
+ * Maximum size of ep0 response buffer for ch9 requests,
+ * the set_sel request uses 6 so far, get_status is 2
+ */
+#define EP0_RESPONSE_BUF  6
+
+/* device operated link and speed got from DEVICE_CONF register */
+enum mtu3_speed {
+	MTU3_SPEED_INACTIVE = 0,
+	MTU3_SPEED_FULL = 1,
+	MTU3_SPEED_HIGH = 3,
+	MTU3_SPEED_SUPER = 4,
+};
+
+/**
+ * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
+ *		without data stage.
+ * @MU3D_EP0_STATE_TX: IN data stage
+ * @MU3D_EP0_STATE_RX: OUT data stage
+ * @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
+ *		waits for its completion ineterrupt
+ * @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
+ *		after receives a SETUP.
+ */
+enum mtu3_g_ep0_state {
+	MU3D_EP0_STATE_SETUP = 1,
+	MU3D_EP0_STATE_TX,
+	MU3D_EP0_STATE_RX,
+	MU3D_EP0_STATE_TX_END,
+	MU3D_EP0_STATE_STALL,
+};
+
+/**
+ * @base: the base address of fifo
+ * @limit: the bitmap size in bits
+ * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
+ */
+struct mtu3_fifo_info {
+	u32 base;
+	u32 limit;
+	DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
+};
+
+/**
+ * General Purpose Descriptor (GPD):
+ *	The format of TX GPD is a little different from RX one.
+ *	And the size of GPD is 16 bytes.
+ *
+ * @flag:
+ *	bit0: Hardware Own (HWO);
+ *	bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported;
+ *	bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1;
+ *	bit7: Interrupt On Completion (IOC);
+ * @chksum: This is used to validate the contents of this GPD;
+ *	If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
+ *	when checksum validation fails;
+ *	Checksum value is calculated over the 16 bytes of the GPD by default;
+ * @data_buf_len: (RX ONLY) This value indicates the length of
+ *	the assigned data buffer
+ * @next_gpd: Physical address of the next GPD
+ * @buffer: Physical address of the data buffer
+ * @buf_len:
+ *	(TX): This value indicates the length of the assigned data buffer
+ *	(RX): The total length of data received
+ * @ext_len: reserved
+ * @ext_flag:
+ *	bit5: (TX ONLY) Zero Length Packet (ZLP),
+ */
+struct qmu_gpd {
+	u8 flag;
+	u8 chksum;
+	u16 data_buf_len;
+	u32 next_gpd;
+	u32 buffer;
+	u16 buf_len;
+	u8 ext_len;
+	u8 ext_flag;
+} __packed;
+
+/**
+* dma: physical base address of GPD segment
+* start: virtual base address of GPD segment
+* end: the last gpd element
+* enqueue: the first empty gpd to use
+* dequeue: the first completed gpd serviced by ISR
+* NOTE: the ring elements of the gpd should be >= 2
+*/
+struct mtu3_gpd_ring {
+	dma_addr_t dma;
+	struct qmu_gpd *start;
+	struct qmu_gpd *end;
+	struct qmu_gpd *enqueue;
+	struct qmu_gpd *dequeue;
+};
+
+/**
+* @vbus: vbus 5V used by host mode
+* @edev: external connector used to detect vbus and iddig changes
+* @vbus_nb: notifier for vbus detection
+* @vbus_nb: notifier for iddig(idpin) detection
+* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
+*		xHCI driver initialized, it's necessary for system bootup
+*		as device.
+* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
+* @id_*: used to maually switch between host and device modes by inpin
+* @manual_drd_enabled: it's true when receptacle is TYPE-A, and supports dual
+*		role devices by an interface to switch host/device modes
+*		depending on user input.
+*/
+struct otg_switch_mtk {
+	struct regulator *vbus;
+	struct extcon_dev *edev;
+	struct notifier_block vbus_nb;
+	struct notifier_block id_nb;
+	struct delayed_work extcon_reg_dwork;
+	bool is_u3_drd;
+	/* dual-role switch by sysfs interface */
+	struct pinctrl *id_pinctrl;
+	struct pinctrl_state *id_float;
+	struct pinctrl_state *id_ground;
+	bool manual_drd_enabled;
+};
+
+/**
+ * @mac_base: register base address of mu3d device MAC, exclude xhci's
+ * @ippc_base: register base address of ip port controller interface
+ * @vusb33: usb3.3V shared by device/host ip
+ * @sys_clk: system clock of usb, shared by device/host ip
+ * @dr_mode: supports which driver mode:
+ *		host only, device only or dual-role mode
+ * @u2_ports: number of usb2.0 host ports
+ * @u3_ports: number of usb3.0 host ports
+ * @wakeup_en: it's true when supports remote wakeup in host mode
+ * @wk_deb_p0: port0's wakeup debounce clock
+ * @wk_deb_p1: it's optional, and depends on port1 is supported or not
+ */
+struct ssusb_mtk {
+	struct device *dev;
+	struct mtu3 *u3d;
+	void __iomem *mac_base;
+	void __iomem *ippc_base;
+	struct phy **phys;
+	int num_phys;
+	/* common power & clock */
+	struct regulator *vusb33;
+	struct clk *sys_clk;
+	/* otg */
+	struct otg_switch_mtk otg_switch;
+	enum usb_dr_mode dr_mode;
+	bool is_host;
+	int u2_ports;
+	int u3_ports;
+	/* usb wakeup for host mode */
+	bool wakeup_en;
+	struct clk *wk_deb_p0;
+	struct clk *wk_deb_p1;
+	struct regmap *pericfg;
+};
+
+/**
+ * @fifo_size: it is (@slot + 1) * @fifo_seg_size
+ * @fifo_seg_size: it is roundup_pow_of_two(@maxp)
+ */
+struct mtu3_ep {
+	struct usb_ep ep;
+	char name[12];
+	struct mtu3 *mtu;
+	u8 epnum;
+	u8 type;
+	u8 is_in;
+	u16 maxp;
+	int slot;
+	u32 fifo_size;
+	u32 fifo_addr;
+	u32 fifo_seg_size;
+	struct mtu3_fifo_info *fifo;
+
+	struct list_head req_list;
+	struct mtu3_gpd_ring gpd_ring;
+	const struct usb_ss_ep_comp_descriptor *comp_desc;
+	const struct usb_endpoint_descriptor *desc;
+
+	int flags;
+	u8 wedged;
+	u8 busy;
+};
+
+struct mtu3_request {
+	struct usb_request request;
+	struct list_head list;
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	struct qmu_gpd *gpd;
+	int epnum;
+};
+
+static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev)
+{
+	return dev_get_drvdata(dev);
+}
+
+/**
+ * struct mtu3 - device driver instance data.
+ * @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP only,
+ *		MTU3_U3_IP_SLOT_DEFAULT for U3 IP
+ * @may_wakeup: means device's remote wakeup is enabled
+ * @is_self_powered: is reported in device status and the config descriptor
+ * @ep0_req: dummy request used while handling standard USB requests
+ *		for GET_STATUS and SET_SEL
+ * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
+ */
+struct mtu3 {
+	spinlock_t lock;
+	struct ssusb_mtk *ssusb;
+	struct device *dev;
+	void __iomem *mac_base;
+	void __iomem *ippc_base;
+	int irq;
+
+	struct mtu3_fifo_info tx_fifo;
+	struct mtu3_fifo_info rx_fifo;
+
+	struct mtu3_ep *ep_array;
+	struct mtu3_ep *in_eps;
+	struct mtu3_ep *out_eps;
+	struct mtu3_ep *ep0;
+	int num_eps;
+	int slot;
+	int active_ep;
+
+	struct dma_pool	*qmu_gpd_pool;
+	struct tasklet_struct qmu_done;
+	u32 qmu_done_intr;
+
+	enum mtu3_g_ep0_state ep0_state;
+	struct usb_gadget g;	/* the gadget */
+	struct usb_gadget_driver *gadget_driver;
+	struct mtu3_request ep0_req;
+	u8 setup_buf[EP0_RESPONSE_BUF];
+	u32 max_speed;
+
+	unsigned is_active:1;
+	unsigned may_wakeup:1;
+	unsigned is_self_powered:1;
+	unsigned test_mode:1;
+	unsigned softconnect:1;
+	unsigned u1_enable:1;
+	unsigned u2_enable:1;
+	unsigned is_u3_ip:1;
+
+	u8 address;
+	u8 test_mode_nr;
+	u32 hw_version;
+};
+
+static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
+{
+	return container_of(g, struct mtu3, g);
+}
+
+static inline int is_first_entry(const struct list_head *list,
+	const struct list_head *head)
+{
+	return list_is_last(head, list);
+}
+
+static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
+{
+	return req ? container_of(req, struct mtu3_request, request) : NULL;
+}
+
+static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
+{
+	return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
+}
+
+static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
+{
+	struct list_head *queue = &mep->req_list;
+
+	if (list_empty(queue))
+		return NULL;
+
+	return list_first_entry(queue, struct mtu3_request, list);
+}
+
+static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
+{
+	writel(data, base + offset);
+}
+
+static inline u32 mtu3_readl(void __iomem *base, u32 offset)
+{
+	return readl(base + offset);
+}
+
+static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
+{
+	void __iomem *addr = base + offset;
+	u32 tmp = readl(addr);
+
+	writel((tmp | (bits)), addr);
+}
+
+static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
+{
+	void __iomem *addr = base + offset;
+	u32 tmp = readl(addr);
+
+	writel((tmp & ~(bits)), addr);
+}
+
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks);
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
+void mtu3_req_complete(struct mtu3_ep *mep,
+		struct usb_request *req, int status);
+
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+		int interval, int burst, int mult);
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
+void mtu3_ep0_setup(struct mtu3 *mtu);
+void mtu3_start(struct mtu3 *mtu);
+void mtu3_stop(struct mtu3 *mtu);
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
+
+int mtu3_gadget_setup(struct mtu3 *mtu);
+void mtu3_gadget_cleanup(struct mtu3 *mtu);
+void mtu3_gadget_reset(struct mtu3 *mtu);
+void mtu3_gadget_suspend(struct mtu3 *mtu);
+void mtu3_gadget_resume(struct mtu3 *mtu);
+void mtu3_gadget_disconnect(struct mtu3 *mtu);
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
+extern const struct usb_ep_ops mtu3_ep0_ops;
+
+#endif
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
new file mode 100644
index 0000000..756824f
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -0,0 +1,879 @@
+/*
+ * mtu3_core.c - hardware access layer and gadget init/exit of
+ *                     MediaTek usb3 Dual-Role Controller Driver
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+
+static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
+{
+	struct mtu3_fifo_info *fifo = mep->fifo;
+	u32 free_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
+	u32 start_bit;
+
+	free_bits = roundup_pow_of_two(free_bits);
+	if (free_bits > fifo->limit)
+		return -EINVAL;
+
+	mep->fifo_seg_size = free_bits * MTU3_EP_FIFO_UNIT;
+	free_bits = free_bits * (mep->slot + 1);
+	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
+			fifo->limit, 0, free_bits, 0);
+	if (start_bit >= fifo->limit)
+		return -EOVERFLOW;
+
+	bitmap_set(fifo->bitmap, start_bit, free_bits);
+	mep->fifo_size = free_bits * MTU3_EP_FIFO_UNIT;
+	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
+
+	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
+		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+
+	return mep->fifo_addr;
+}
+
+static void ep_fifo_free(struct mtu3_ep *mep)
+{
+	struct mtu3_fifo_info *fifo = mep->fifo;
+	u32 addr = mep->fifo_addr;
+	u32 bits = mep->fifo_size / MTU3_EP_FIFO_UNIT;
+	u32 start_bit;
+
+	if (unlikely(addr < fifo->base || bits > fifo->limit))
+		return;
+
+	start_bit = (addr - fifo->base) / MTU3_EP_FIFO_UNIT;
+	bitmap_clear(fifo->bitmap, start_bit, bits);
+	mep->fifo_size = 0;
+	mep->fifo_seg_size = 0;
+
+	dev_dbg(mep->mtu->dev, "%s size:%#x/%#x, start_bit: %d\n",
+		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+}
+
+/* enable/disable U3D SS function */
+static void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
+{
+	/* If usb3_en==0, LTSSM will go to SS.Disable state */
+	if (enable)
+		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+	else
+		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+
+	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
+}
+
+/* set/clear U3D HS device soft connect */
+static void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable)
+{
+	if (enable) {
+		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+			SOFT_CONN | SUSPENDM_ENABLE);
+	} else {
+		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+			SOFT_CONN | SUSPENDM_ENABLE);
+	}
+	dev_dbg(mtu->dev, "SOFTCONN = %d\n", !!enable);
+}
+
+/* only port0 of U2/U3 supports device mode */
+static int mtu3_device_enable(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+	u32 check_clk = 0;
+
+	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+	if (mtu->is_u3_ip) {
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+		mtu3_clrbits(ibase, SSUSB_U3_CTRL(0),
+			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN |
+			SSUSB_U3_PORT_HOST_SEL));
+	}
+	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),
+		(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
+		SSUSB_U2_PORT_HOST_SEL));
+
+	mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+
+	return ssusb_check_clocks(mtu->ssusb, check_clk);
+}
+
+static void mtu3_device_disable(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+
+	if (mtu->is_u3_ip)
+		mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
+			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN));
+
+	mtu3_setbits(ibase, SSUSB_U2_CTRL(0),
+		SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN);
+
+	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+
+	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+/* reset U3D all dev module. */
+static void mtu3_device_reset(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+
+	mtu3_setbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+	udelay(1);
+	mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+}
+
+/* disable all interrupts */
+static void mtu3_intr_disable(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	/* Disable level 1 interrupts */
+	mtu3_writel(mbase, U3D_LV1IECR, ~0x0);
+
+	/* Disable endpoint interrupts */
+	mtu3_writel(mbase, U3D_EPIECR, ~0x0);
+}
+
+static void mtu3_intr_status_clear(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	/* Clear EP0 and Tx/Rx EPn interrupts status */
+	mtu3_writel(mbase, U3D_EPISR, ~0x0);
+
+	/* Clear U2 USB common interrupt status */
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR, ~0x0);
+
+	/* Clear U3 LTSSM interrupt status */
+	mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
+
+	/* Clear speed change interrupt status */
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
+}
+
+/* enable system global interrupt */
+static void mtu3_intr_enable(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 value;
+
+	/*Enable level 1 interrupt (BMU, QMU, MAC3, DMA, MAC2, EPCTL) */
+	value = BMU_INTR | QMU_INTR | MAC3_INTR | MAC2_INTR | EP_CTRL_INTR;
+	mtu3_writel(mbase, U3D_LV1IESR, value);
+
+	/* Enable U2 common USB interrupt */
+	value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
+
+	if (mtu->is_u3_ip) {
+		/* Enable U3 LTSSM interrupt */
+		value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
+		    VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
+		mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
+	}
+
+	/* Enable QMU interrupt. */
+	value = TXQ_CSERR_INT | TXQ_LENERR_INT | RXQ_CSERR_INT |
+			RXQ_LENERR_INT | RXQ_ZLPERR_INT;
+	mtu3_writel(mbase, U3D_QIESR1, value);
+
+	/* Enable speed change interrupt */
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
+}
+
+/* set/clear the stall and toggle bits for non-ep0 */
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	u8 epnum = mep->epnum;
+	u32 csr;
+
+	if (mep->is_in) {	/* TX */
+		csr = mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)) & TX_W1C_BITS;
+		if (set)
+			csr |= TX_SENDSTALL;
+		else
+			csr = (csr & (~TX_SENDSTALL)) | TX_SENTSTALL;
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr);
+
+	} else {	/* RX */
+		csr = mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)) & RX_W1C_BITS;
+		if (set)
+			csr |= RX_SENDSTALL;
+		else
+			csr = (csr & (~RX_SENDSTALL)) | RX_SENTSTALL;
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr);
+
+	}
+
+	if (!set) {
+		mtu3_setbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
+		mtu3_clrbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
+		mep->flags &= ~MTU3_EP_STALL;
+	} else {
+		mep->flags |= MTU3_EP_STALL;
+	}
+
+	dev_dbg(mtu->dev, "%s: %s\n", mep->name,
+		set ? "SEND STALL" : "CLEAR STALL, with EP RESET");
+}
+
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
+{
+	if (mtu->is_u3_ip && (mtu->max_speed == USB_SPEED_SUPER))
+		mtu3_ss_func_set(mtu, is_on);
+	else
+		mtu3_hs_softconn_set(mtu, is_on);
+
+	dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
+		usb_speed_string(mtu->max_speed), is_on ? "+" : "-");
+}
+
+void mtu3_start(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	dev_dbg(mtu->dev, "%s devctl 0x%02x\n", __func__,
+		(u8)mtu3_readl(mbase, U3D_DEVICE_CONTROL));
+
+	mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+	/*
+	 * When disable U2 port, USB2_CSR's register will be reset to
+	 * default value after re-enable it again(HS is enabled by default).
+	 * So if force mac to work as FS, disable HS function.
+	 */
+	if (mtu->max_speed == USB_SPEED_FULL)
+		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+
+	/* Initialize the default interrupts */
+	mtu3_intr_enable(mtu);
+
+	mtu->is_active = 1;
+
+	if (mtu->softconnect)
+		mtu3_dev_on_off(mtu, 1);
+
+}
+
+void mtu3_stop(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	mtu3_intr_disable(mtu);
+	mtu3_intr_status_clear(mtu);
+
+	if (mtu->softconnect)
+		mtu3_dev_on_off(mtu, 0);
+
+	mtu->is_active = 0;
+	mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+			int interval, int burst, int mult)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 csr0, csr1, csr2;
+	u32 fifosz = 0;
+	u32 max_pkt;
+	int fifo_addr;
+
+	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
+	if (fifo_addr < 0) {
+		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", fifosz);
+		return -ENOMEM;
+	}
+	fifosz = ilog2(mep->fifo_seg_size);
+	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifosz,
+		mep->fifo_seg_size, mep->fifo_size);
+
+	if (mep->is_in) {
+		csr0 = TX_TXMAXPKTSZ(mep->maxp);
+		csr0 |= TX_DMAREQEN;
+
+		max_pkt = (burst + 1) * (mult + 1) - 1;
+		csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot);
+		csr1 |= TX_MAX_PKT(max_pkt) | TX_MULT(mult);
+
+		csr2 = TX_FIFOADDR(fifo_addr >> 4);
+		csr2 |= TX_FIFOSEGSIZE(fifosz);
+
+		switch (mep->type) {
+		case USB_ENDPOINT_XFER_BULK:
+			csr1 |= TX_TYPE(TYPE_BULK);
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			csr1 |= TX_TYPE(TYPE_ISO);
+			csr2 |= TX_BINTERVAL(interval);
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			csr1 |= TX_TYPE(TYPE_INT);
+			csr2 |= TX_BINTERVAL(interval);
+			break;
+		}
+
+		/* Enable QMU Done interrupt */
+		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
+
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
+		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
+		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
+
+		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
+			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
+			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
+
+	} else {
+
+		csr0 = RX_RXMAXPKTSZ(mep->maxp);
+		csr0 |= RX_DMAREQEN;
+
+		max_pkt = (burst + 1) * (mult + 1) - 1;
+		csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot);
+		csr1 |= RX_MAX_PKT(max_pkt) | RX_MULT(mult);
+
+		csr2 = RX_FIFOADDR(fifo_addr >> 4);
+		csr2 |= RX_FIFOSEGSIZE(fifosz);
+
+		switch (mep->type) {
+		case USB_ENDPOINT_XFER_BULK:
+			csr1 |= RX_TYPE(TYPE_BULK);
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			csr1 |= RX_TYPE(TYPE_ISO);
+			csr2 |= RX_BINTERVAL(interval);
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			csr1 |= RX_TYPE(TYPE_INT);
+			csr2 |= RX_BINTERVAL(interval);
+			break;
+		}
+
+		/*Enable QMU Done interrupt */
+		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
+
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
+		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
+		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
+
+		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
+			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
+			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
+
+	}
+
+	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
+	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
+		__func__, mep->name, mep->fifo_addr, mep->fifo_size, fifosz,
+		mep->fifo_seg_size);
+
+	return 0;
+}
+
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+
+	if (mep->is_in) {
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), 0);
+		mtu3_setbits(mbase, U3D_QIECR0, QMU_TX_DONE_INT(epnum));
+	} else {
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), 0);
+		mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
+	}
+
+	ep_fifo_free(mep);
+
+	dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
+}
+
+/*
+ * Two scenarios:
+ * 1. when device IP supports SS, the fifo of EP0, TX EPs, RX EPs
+ *	are separated;
+ * 2. when only supports HS, the fifo is shared for all eps, and
+ *	the capability registers of @EPNTXFFSZ or @EPNRXFFSZ indicate
+ *	the total fifo size of non-ep0, and ep0's is 64B, so the total fifo
+ *	size is 64B + @EPNTXFFSZ;
+ *	Because the first 64B should be reserved for EP0, so non-ep0's fifo
+ *	starts from offset 64, and are divided into two equal parts for
+ *	TX or RX EPs for simplification.
+ */
+static void get_ep_fifo_config(struct mtu3 *mtu)
+{
+	struct mtu3_fifo_info *tx_fifo;
+	struct mtu3_fifo_info *rx_fifo;
+	u32 fifosize;
+
+	if (mtu->is_u3_ip) {
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+		tx_fifo = &mtu->tx_fifo;
+		tx_fifo->base = 0;
+		tx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNRXFFSZ);
+		rx_fifo = &mtu->rx_fifo;
+		rx_fifo->base = 0;
+		rx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+		mtu->slot = MTU3_U3_IP_SLOT_DEFAULT;
+	} else {
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+		tx_fifo = &mtu->tx_fifo;
+		tx_fifo->base = MTU3_U2_IP_EP0_FIFO_SIZE;
+		tx_fifo->limit = (fifosize / MTU3_EP_FIFO_UNIT) >> 1;
+		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+		rx_fifo = &mtu->rx_fifo;
+		rx_fifo->base =
+			tx_fifo->base + tx_fifo->limit * MTU3_EP_FIFO_UNIT;
+		rx_fifo->limit = tx_fifo->limit;
+		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+		mtu->slot = MTU3_U2_IP_SLOT_DEFAULT;
+	}
+
+	dev_dbg(mtu->dev, "%s, TX: base-%d, limit-%d; RX: base-%d, limit-%d\n",
+		__func__, tx_fifo->base, tx_fifo->limit,
+		rx_fifo->base, rx_fifo->limit);
+}
+
+void mtu3_ep0_setup(struct mtu3 *mtu)
+{
+	u32 maxpacket = mtu->g.ep0->maxpacket;
+	u32 csr;
+
+	dev_dbg(mtu->dev, "%s maxpacket: %d\n", __func__, maxpacket);
+
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR);
+	csr &= ~EP0_MAXPKTSZ_MSK;
+	csr |= EP0_MAXPKTSZ(maxpacket);
+	csr &= EP0_W1C_BITS;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+	/* Enable EP0 interrupt */
+	mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR);
+}
+
+static int mtu3_mem_alloc(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_ep *ep_array;
+	int in_ep_num, out_ep_num;
+	u32 cap_epinfo, cap_dev;
+	int ret;
+	int i;
+
+	mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID);
+
+	cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP);
+	mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev);
+
+	cap_epinfo = mtu3_readl(mbase, U3D_CAP_EPINFO);
+	in_ep_num = CAP_TX_EP_NUM(cap_epinfo);
+	out_ep_num = CAP_RX_EP_NUM(cap_epinfo);
+
+	dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
+		mtu->is_u3_ip ? "U3" : "U2");
+	dev_info(mtu->dev, "fifosz/epnum: Tx=%#x/%d, Rx=%#x/%d\n",
+		 mtu3_readl(mbase, U3D_CAP_EPNTXFFSZ), in_ep_num,
+		 mtu3_readl(mbase, U3D_CAP_EPNRXFFSZ), out_ep_num);
+
+	/* one for ep0, another is reserved */
+	mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
+	ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
+	if (ep_array == NULL)
+		return -ENOMEM;
+
+	mtu->ep_array = ep_array;
+	mtu->in_eps = ep_array;
+	mtu->out_eps = &ep_array[mtu->num_eps];
+	/* ep0 use in_eps[0], out_eps[0] is reserved */
+	mtu->ep0 = mtu->in_eps;
+
+	mtu->ep0->mtu = mtu;
+	mtu->ep0->epnum = 0;
+
+	for (i = 1; i < mtu->num_eps; i++) {
+		struct mtu3_ep *mep = mtu->in_eps + i;
+
+		mep->fifo = &mtu->tx_fifo;
+		mep = mtu->out_eps + i;
+		mep->fifo = &mtu->rx_fifo;
+	}
+
+	get_ep_fifo_config(mtu);
+
+	ret = mtu3_qmu_init(mtu);
+	if (ret)
+		kfree(mtu->ep_array);
+
+	return ret;
+}
+
+static void mtu3_mem_free(struct mtu3 *mtu)
+{
+	mtu3_qmu_exit(mtu);
+	kfree(mtu->ep_array);
+}
+
+static void mtu3_set_speed(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
+		mtu->max_speed = USB_SPEED_HIGH;
+
+	if (mtu->max_speed == USB_SPEED_FULL) {
+		/* disable U3 SS function */
+		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+		/* disable HS function */
+		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+	} else if (mtu->max_speed == USB_SPEED_HIGH) {
+		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+		/* HS/FS detected by HW */
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+	}
+	dev_info(mtu->dev, "max_speed: %s\n",
+		usb_speed_string(mtu->max_speed));
+}
+
+static void mtu3_regs_init(struct mtu3 *mtu)
+{
+
+	void __iomem *mbase = mtu->mac_base;
+
+	/* be sure interrupts are disabled before registration of ISR */
+	mtu3_intr_disable(mtu);
+	mtu3_intr_status_clear(mtu);
+
+	if (mtu->is_u3_ip) {
+		/* disable LGO_U1/U2 by default */
+		mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
+				SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE |
+				SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
+		/* device responses to u3_exit from host automatically */
+		mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
+	}
+
+	mtu3_set_speed(mtu);
+
+	/* delay about 0.1us from detecting reset to send chirp-K */
+	mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
+
+	/* U2/U3 detected by HW */
+	mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
+
+	/* enable QMU 16B checksum */
+	mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN);
+
+	/* vbus detected by HW */
+	mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
+}
+
+static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	enum usb_device_speed udev_speed;
+	u32 maxpkt = 64;
+	u32 link;
+	u32 speed;
+
+	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
+	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
+	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
+
+	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
+		return IRQ_NONE;
+
+	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
+
+	switch (speed) {
+	case MTU3_SPEED_FULL:
+		udev_speed = USB_SPEED_FULL;
+		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+				LPM_BESL_STALL | LPM_BESLD_STALL);
+		break;
+	case MTU3_SPEED_HIGH:
+		udev_speed = USB_SPEED_HIGH;
+		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+				LPM_BESL_STALL | LPM_BESLD_STALL);
+		break;
+	case MTU3_SPEED_SUPER:
+		udev_speed = USB_SPEED_SUPER;
+		maxpkt = 512;
+		break;
+	default:
+		udev_speed = USB_SPEED_UNKNOWN;
+		break;
+	}
+	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
+
+	mtu->g.speed = udev_speed;
+	mtu->g.ep0->maxpacket = maxpkt;
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+	if (udev_speed == USB_SPEED_UNKNOWN)
+		mtu3_gadget_disconnect(mtu);
+	else
+		mtu3_ep0_setup(mtu);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 ltssm;
+
+	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
+	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
+	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
+
+	if (ltssm & HOT_RST_INTR)
+		mtu3_gadget_reset(mtu);
+
+	if (ltssm & WARM_RST_INTR)
+		mtu3_gadget_reset(mtu);
+
+	if (ltssm & VBUS_FALL_INTR)
+		mtu3_ss_func_set(mtu, false);
+
+	if (ltssm & VBUS_RISE_INTR)
+		mtu3_ss_func_set(mtu, true);
+
+	if (ltssm & EXIT_U3_INTR)
+		mtu3_gadget_resume(mtu);
+
+	if (ltssm & ENTER_U3_INTR)
+		mtu3_gadget_suspend(mtu);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 u2comm;
+
+	u2comm = mtu3_readl(mbase, U3D_COMMON_USB_INTR);
+	u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */
+	dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm);
+
+	if (u2comm & SUSPEND_INTR)
+		mtu3_gadget_suspend(mtu);
+
+	if (u2comm & RESUME_INTR)
+		mtu3_gadget_resume(mtu);
+
+	if (u2comm & RESET_INTR)
+		mtu3_gadget_reset(mtu);
+
+	return IRQ_HANDLED;
+}
+
+irqreturn_t mtu3_irq(int irq, void *data)
+{
+	struct mtu3 *mtu = (struct mtu3 *)data;
+	unsigned long flags;
+	u32 level1;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	/* U3D_LV1ISR is RU */
+	level1 = mtu3_readl(mtu->mac_base, U3D_LV1ISR);
+	level1 &= mtu3_readl(mtu->mac_base, U3D_LV1IER);
+
+	if (level1 & EP_CTRL_INTR)
+		mtu3_link_isr(mtu);
+
+	if (level1 & MAC2_INTR)
+		mtu3_u2_common_isr(mtu);
+
+	if (level1 & MAC3_INTR)
+		mtu3_u3_ltssm_isr(mtu);
+
+	if (level1 & BMU_INTR)
+		mtu3_ep0_isr(mtu);
+
+	if (level1 & QMU_INTR)
+		mtu3_qmu_isr(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int mtu3_hw_init(struct mtu3 *mtu)
+{
+	int ret;
+
+	mtu3_device_reset(mtu);
+
+	ret = mtu3_device_enable(mtu);
+	if (ret) {
+		dev_err(mtu->dev, "device enable failed %d\n", ret);
+		return ret;
+	}
+
+	ret = mtu3_mem_alloc(mtu);
+	if (ret) {
+		dev_err(mtu->dev, "mem alloc failed, aborting\n");
+		return -ENOMEM;
+	}
+
+	mtu3_regs_init(mtu);
+
+	return 0;
+}
+
+static void mtu3_hw_exit(struct mtu3 *mtu)
+{
+	mtu3_device_disable(mtu);
+	mtu3_mem_free(mtu);
+}
+
+/*-------------------------------------------------------------------------*/
+
+int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+	struct device *dev = ssusb->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mtu3 *mtu = NULL;
+	struct resource *res;
+	int ret = -ENOMEM;
+
+	mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
+	if (mtu == NULL)
+		return -ENOMEM;
+
+	mtu->irq = platform_get_irq(pdev, 0);
+	if (mtu->irq <= 0) {
+		dev_err(dev, "fail to get irq number\n");
+		return -ENODEV;
+	}
+	dev_info(dev, "irq %d\n", mtu->irq);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
+	mtu->mac_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mtu->mac_base)) {
+		dev_err(dev, "error mapping memory for dev mac\n");
+		return PTR_ERR(mtu->mac_base);
+	}
+
+	spin_lock_init(&mtu->lock);
+	mtu->dev = dev;
+	mtu->ippc_base = ssusb->ippc_base;
+	ssusb->mac_base	= mtu->mac_base;
+	ssusb->u3d = mtu;
+	mtu->ssusb = ssusb;
+	mtu->max_speed = usb_get_maximum_speed(dev);
+
+	/* check the max_speed parameter */
+	switch (mtu->max_speed) {
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(dev, "invalid max_speed: %s\n",
+			usb_speed_string(mtu->max_speed));
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default as SS */
+		mtu->max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
+	dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n",
+		mtu->mac_base, mtu->ippc_base);
+
+	ret = mtu3_hw_init(mtu);
+	if (ret) {
+		dev_err(dev, "mtu3 hw init failed:%d\n", ret);
+		return ret;
+	}
+
+	ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu);
+	if (ret) {
+		dev_err(dev, "request irq %d failed!\n", mtu->irq);
+		goto irq_err;
+	}
+
+	device_init_wakeup(dev, true);
+
+	ret = mtu3_gadget_setup(mtu);
+	if (ret) {
+		dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
+		goto gadget_err;
+	}
+
+	/* init as host mode, power down device IP for power saving */
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
+		mtu3_stop(mtu);
+
+	dev_dbg(dev, " %s() done...\n", __func__);
+
+	return 0;
+
+gadget_err:
+	device_init_wakeup(dev, false);
+
+irq_err:
+	mtu3_hw_exit(mtu);
+	ssusb->u3d = NULL;
+	dev_err(dev, " %s() fail...\n", __func__);
+
+	return ret;
+}
+
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{
+	struct mtu3 *mtu = ssusb->u3d;
+
+	mtu3_gadget_cleanup(mtu);
+	device_init_wakeup(ssusb->dev, false);
+	mtu3_hw_exit(mtu);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
new file mode 100644
index 0000000..f7db524
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -0,0 +1,348 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/kernel.h>
+#include <linux/of_device.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define USB2_PORT 2
+#define USB3_PORT 3
+
+enum mtu3_vbus_id_state {
+	MTU3_ID_FLOAT = 1,
+	MTU3_ID_GROUND,
+	MTU3_VBUS_OFF,
+	MTU3_VBUS_VALID,
+};
+
+static void toggle_opstate(struct ssusb_mtk *ssusb)
+{
+	if (!ssusb->otg_switch.is_u3_drd) {
+		mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
+		mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
+	}
+}
+
+/* only port0 supports dual-role mode */
+static int ssusb_port0_switch(struct ssusb_mtk *ssusb,
+		int version, bool tohost)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	u32 temp;
+
+	dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
+		version, tohost ? "host" : "device");
+
+	if (version == USB2_PORT) {
+		/* 1. power off and disable u2 port0 */
+		temp = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+		temp |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(0), temp);
+
+		/* 2. power on, enable u2 port0 and select its mode */
+		temp = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+		temp &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+		temp = tohost ? (temp | SSUSB_U2_PORT_HOST_SEL) :
+			(temp & (~SSUSB_U2_PORT_HOST_SEL));
+		mtu3_writel(ibase, SSUSB_U2_CTRL(0), temp);
+	} else {
+		/* 1. power off and disable u3 port0 */
+		temp = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+		temp |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(0), temp);
+
+		/* 2. power on, enable u3 port0 and select its mode */
+		temp = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+		temp &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+		temp = tohost ? (temp | SSUSB_U3_PORT_HOST_SEL) :
+			(temp & (~SSUSB_U3_PORT_HOST_SEL));
+		mtu3_writel(ibase, SSUSB_U3_CTRL(0), temp);
+	}
+
+	return 0;
+}
+
+static void switch_port_to_host(struct ssusb_mtk *ssusb)
+{
+	u32 check_clk = 0;
+
+	dev_dbg(ssusb->dev, "%s\n", __func__);
+
+	ssusb_port0_switch(ssusb, USB2_PORT, true);
+
+	if (ssusb->otg_switch.is_u3_drd) {
+		ssusb_port0_switch(ssusb, USB3_PORT, true);
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+	}
+
+	ssusb_check_clocks(ssusb, check_clk);
+
+	/* after all clocks are stable */
+	toggle_opstate(ssusb);
+}
+
+static void switch_port_to_device(struct ssusb_mtk *ssusb)
+{
+	u32 check_clk = 0;
+
+	dev_dbg(ssusb->dev, "%s\n", __func__);
+
+	ssusb_port0_switch(ssusb, USB2_PORT, false);
+
+	if (ssusb->otg_switch.is_u3_drd) {
+		ssusb_port0_switch(ssusb, USB3_PORT, false);
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+	}
+
+	ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct regulator *vbus = otg_sx->vbus;
+	int ret;
+
+	/* vbus is optional */
+	if (!vbus)
+		return 0;
+
+	dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
+
+	if (is_on) {
+		ret = regulator_enable(vbus);
+		if (ret) {
+			dev_err(ssusb->dev, "vbus regulator enable failed\n");
+			return ret;
+		}
+	} else {
+		regulator_disable(vbus);
+	}
+
+	return 0;
+}
+
+/*
+ * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
+ * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
+ */
+static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
+	enum mtu3_vbus_id_state status)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct mtu3 *mtu = ssusb->u3d;
+
+	dev_dbg(ssusb->dev, "mailbox state(%d)\n", status);
+
+	switch (status) {
+	case MTU3_ID_GROUND:
+		switch_port_to_host(ssusb);
+		ssusb_set_vbus(otg_sx, 1);
+		ssusb->is_host = true;
+		break;
+	case MTU3_ID_FLOAT:
+		ssusb->is_host = false;
+		ssusb_set_vbus(otg_sx, 0);
+		switch_port_to_device(ssusb);
+		break;
+	case MTU3_VBUS_OFF:
+		mtu3_stop(mtu);
+		pm_relax(ssusb->dev);
+		break;
+	case MTU3_VBUS_VALID:
+		/* avoid suspend when works as device */
+		pm_stay_awake(ssusb->dev);
+		mtu3_start(mtu);
+		break;
+	default:
+		dev_err(ssusb->dev, "invalid state\n");
+	}
+}
+
+static int ssusb_id_notifier(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct otg_switch_mtk *otg_sx =
+		container_of(nb, struct otg_switch_mtk, id_nb);
+
+	if (event)
+		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+	else
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+
+	return NOTIFY_DONE;
+}
+
+static int ssusb_vbus_notifier(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct otg_switch_mtk *otg_sx =
+		container_of(nb, struct otg_switch_mtk, vbus_nb);
+
+	if (event)
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+	else
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+
+	return NOTIFY_DONE;
+}
+
+static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct extcon_dev *edev = otg_sx->edev;
+	int ret;
+
+	/* extcon is optional */
+	if (!edev)
+		return 0;
+
+	otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
+	ret = extcon_register_notifier(edev, EXTCON_USB,
+					&otg_sx->vbus_nb);
+	if (ret < 0)
+		dev_err(ssusb->dev, "failed to register notifier for USB\n");
+
+	otg_sx->id_nb.notifier_call = ssusb_id_notifier;
+	ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+					&otg_sx->id_nb);
+	if (ret < 0)
+		dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
+
+	dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
+		extcon_get_cable_state_(edev, EXTCON_USB),
+		extcon_get_cable_state_(edev, EXTCON_USB_HOST));
+
+	/* default as host, switch to device mode if needed */
+	if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == false)
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+	if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+
+	return 0;
+}
+
+static void extcon_register_dwork(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct otg_switch_mtk *otg_sx =
+	    container_of(dwork, struct otg_switch_mtk, extcon_reg_dwork);
+
+	ssusb_extcon_register(otg_sx);
+}
+
+/*
+ * When use TYPE-A receptacle but also want to support dual-role mode,
+ * we provide an interface via sysfs to switch between host and device modes
+ * depending on user input.
+ * It generates cable state changes by pulling up/down IDPIN and
+ * notifies driver to switch mode by "extcon-usb-gpio".
+ * NOTE: when use MICRO receptacle, should not enable this interface.
+ */
+static void ssusb_mode_sysfs_switch(struct ssusb_mtk *ssusb, int to_host)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	if (!otg_sx->manual_drd_enabled)
+		return;
+
+	if (to_host)
+		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
+	else
+		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+}
+
+static ssize_t ssusb_mode_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = sprintf(buf, "current mode: %s(%s drd)\n(echo device/host)\n",
+		ssusb->is_host ? "host" : "device",
+		ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto");
+
+	return ret;
+}
+
+static ssize_t ssusb_mode_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	if (sysfs_streq(buf, "host") && !ssusb->is_host)
+		ssusb_mode_sysfs_switch(ssusb, 1);
+	else if (sysfs_streq(buf, "device") && ssusb->is_host)
+		ssusb_mode_sysfs_switch(ssusb, 0);
+	else
+		dev_err(dev, "wrong or duplicated setting\n");
+
+	return count;
+}
+
+static DEVICE_ATTR(mode, 0664, ssusb_mode_show, ssusb_mode_store);
+
+static struct attribute *ssusb_attributes[] = {
+	&dev_attr_mode.attr,
+	NULL
+};
+
+static const struct attribute_group ssusb_attr_group = {
+	.attrs = ssusb_attributes,
+};
+
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+	int ret;
+
+	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
+
+	ret = sysfs_create_group(&ssusb->dev->kobj, &ssusb_attr_group);
+	if (ret) {
+		dev_err(ssusb->dev, "Cannot register sysfs attributes: %d\n",
+			ret);
+		return ret;
+	}
+
+	/* It is enough to delay 1s for waiting for host initialization */
+	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+
+	return 0;
+}
+
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	if (otg_sx->edev) {
+		extcon_unregister_notifier(otg_sx->edev,
+			EXTCON_USB, &otg_sx->vbus_nb);
+		extcon_unregister_notifier(otg_sx->edev,
+			EXTCON_USB_HOST, &otg_sx->id_nb);
+	}
+	sysfs_remove_group(&ssusb->dev->kobj, &ssusb_attr_group);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
new file mode 100644
index 0000000..9b228b5
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -0,0 +1,108 @@
+/*
+ * mtu3_dr.h - dual role switch and host glue layer header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 _MTU3_DR_H_
+#define _MTU3_DR_H_
+
+#if IS_ENABLED(CONFIG_USB_MTU3_HOST) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn);
+void ssusb_host_exit(struct ssusb_mtk *ssusb);
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+				struct device_node *dn);
+int ssusb_host_enable(struct ssusb_mtk *ssusb);
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
+
+#else
+
+static inline int ssusb_host_init(struct ssusb_mtk *ssusb,
+
+	struct device_node *parent_dn)
+{
+	return 0;
+}
+
+static inline void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_wakeup_of_property_parse(
+	struct ssusb_mtk *ssusb, struct device_node *dn)
+{
+	return 0;
+}
+
+static inline int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+	return 0;
+}
+
+static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{}
+
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_gadget_init(struct ssusb_mtk *ssusb);
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
+#else
+static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{}
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+
+#else
+
+static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+	return 0;
+}
+
+#endif
+
+#endif		/* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
new file mode 100644
index 0000000..2a255e7
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -0,0 +1,731 @@
+/*
+ * mtu3_gadget.c - MediaTek usb3 DRD peripheral support
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 "mtu3.h"
+
+void mtu3_req_complete(struct mtu3_ep *mep,
+		     struct usb_request *req, int status)
+__releases(mep->mtu->lock)
+__acquires(mep->mtu->lock)
+{
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	int busy = mep->busy;
+
+	mreq = to_mtu3_request(req);
+	list_del(&mreq->list);
+	if (mreq->request.status == -EINPROGRESS)
+		mreq->request.status = status;
+
+	mtu = mreq->mtu;
+	mep->busy = 1;
+	spin_unlock(&mtu->lock);
+
+	/* ep0 makes use of PIO, needn't unmap it */
+	if (mep->epnum)
+		usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
+
+	dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name,
+		req, req->status, mreq->request.actual, mreq->request.length);
+
+	usb_gadget_giveback_request(&mep->ep, &mreq->request);
+
+	spin_lock(&mtu->lock);
+	mep->busy = busy;
+}
+
+static void nuke(struct mtu3_ep *mep, const int status)
+{
+	struct mtu3_request *mreq = NULL;
+
+	mep->busy = 1;
+	if (list_empty(&mep->req_list))
+		return;
+
+	dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
+
+	/* exclude EP0 */
+	if (mep->epnum)
+		mtu3_qmu_flush(mep);
+
+	while (!list_empty(&mep->req_list)) {
+		mreq = list_first_entry(&mep->req_list,
+					struct mtu3_request, list);
+		mtu3_req_complete(mep, &mreq->request, status);
+	}
+}
+
+static int mtu3_ep_enable(struct mtu3_ep *mep)
+{
+	const struct usb_endpoint_descriptor *desc;
+	const struct usb_ss_ep_comp_descriptor *comp_desc;
+	struct mtu3 *mtu = mep->mtu;
+	u32 interval = 0;
+	u32 multi = 0;
+	u32 burst = 0;
+	int max_packet;
+	int ret;
+
+	desc = mep->desc;
+	comp_desc = mep->comp_desc;
+	mep->type = usb_endpoint_type(desc);
+	max_packet = usb_endpoint_maxp(desc);
+	mep->maxp = max_packet & GENMASK(10, 0);
+
+	switch (mtu->g.speed) {
+	case USB_SPEED_SUPER:
+		if (usb_endpoint_xfer_int(desc) ||
+				usb_endpoint_xfer_isoc(desc)) {
+			interval = desc->bInterval;
+			interval = clamp_val(interval, 1, 16) - 1;
+			if (usb_endpoint_xfer_isoc(desc) && comp_desc)
+				multi = comp_desc->bmAttributes;
+		}
+		if (comp_desc)
+			burst = comp_desc->bMaxBurst;
+
+		break;
+	case USB_SPEED_HIGH:
+		if (usb_endpoint_xfer_isoc(desc) ||
+				usb_endpoint_xfer_int(desc)) {
+			interval = desc->bInterval;
+			interval = clamp_val(interval, 1, 16) - 1;
+			burst = (max_packet & GENMASK(12, 11)) >> 11;
+		}
+		break;
+	default:
+		break; /*others are ignored */
+	}
+
+	dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, multi:%d\n",
+		__func__, mep->maxp, interval, burst, multi);
+
+	mep->ep.maxpacket = mep->maxp;
+	mep->ep.desc = desc;
+	mep->ep.comp_desc = comp_desc;
+
+	/* slot mainly affects bulk/isoc transfer, so ignore int */
+	mep->slot = usb_endpoint_xfer_int(desc) ? 0 : mtu->slot;
+
+	ret = mtu3_config_ep(mtu, mep, interval, burst, multi);
+	if (ret < 0)
+		return ret;
+
+	ret = mtu3_gpd_ring_alloc(mep);
+	if (ret < 0) {
+		mtu3_deconfig_ep(mtu, mep);
+		return ret;
+	}
+
+	mtu3_qmu_start(mep);
+
+	return 0;
+}
+
+static int mtu3_ep_disable(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+
+	mtu3_qmu_stop(mep);
+
+	/* abort all pending requests */
+	nuke(mep, -ESHUTDOWN);
+	mtu3_deconfig_ep(mtu, mep);
+	mtu3_gpd_ring_free(mep);
+
+	mep->desc = NULL;
+	mep->ep.desc = NULL;
+	mep->comp_desc = NULL;
+	mep->type = 0;
+	mep->flags = 0;
+
+	return 0;
+}
+
+static int mtu3_gadget_ep_enable(struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_debug("%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		pr_debug("%s missing wMaxPacketSize\n", __func__);
+		return -EINVAL;
+	}
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+
+	/* check ep number and direction against endpoint */
+	if (usb_endpoint_num(desc) != mep->epnum)
+		return -EINVAL;
+
+	if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
+
+	if (mep->flags & MTU3_EP_ENABLED) {
+		dev_WARN_ONCE(mtu->dev, true, "%s is already enabled\n",
+				mep->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	mep->desc = desc;
+	mep->comp_desc = ep->comp_desc;
+
+	ret = mtu3_ep_enable(mep);
+	if (ret)
+		goto error;
+
+	mep->busy = 0;
+	mep->wedged = 0;
+	mep->flags |= MTU3_EP_ENABLED;
+	mtu->active_ep++;
+
+error:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
+
+	return ret;
+}
+
+static int mtu3_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3 *mtu = mep->mtu;
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
+
+	if (!(mep->flags & MTU3_EP_ENABLED)) {
+		dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	mtu3_ep_disable(mep);
+	mep->flags &= ~MTU3_EP_ENABLED;
+	mtu->active_ep--;
+	spin_unlock_irqrestore(&(mtu->lock), flags);
+
+	dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
+		__func__, mtu->active_ep, mtu->is_active);
+
+	return 0;
+}
+
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3_request *mreq;
+
+	mreq = kzalloc(sizeof(*mreq), gfp_flags);
+	if (!mreq)
+		return NULL;
+
+	mreq->request.dma = DMA_ADDR_INVALID;
+	mreq->epnum = mep->epnum;
+	mreq->mep = mep;
+
+	return &mreq->request;
+}
+
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(to_mtu3_request(req));
+}
+
+static int mtu3_gadget_queue(struct usb_ep *ep,
+		struct usb_request *req, gfp_t gfp_flags)
+{
+	struct mtu3_ep *mep;
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !req)
+		return -EINVAL;
+
+	if (!req->buf)
+		return -ENODATA;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+	mreq = to_mtu3_request(req);
+	mreq->mtu = mtu;
+
+	if (mreq->mep != mep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
+		__func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
+		mreq, ep->maxpacket, mreq->request.length);
+
+	if (req->length > GPD_BUF_SIZE) {
+		dev_warn(mtu->dev,
+			"req length > supported MAX:%d requested:%d\n",
+			GPD_BUF_SIZE, req->length);
+		return -EOPNOTSUPP;
+	}
+
+	/* don't queue if the ep is down */
+	if (!mep->desc) {
+		dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
+			req, ep->name);
+		return -ESHUTDOWN;
+	}
+
+	mreq->request.actual = 0;
+	mreq->request.status = -EINPROGRESS;
+
+	ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
+	if (ret) {
+		dev_err(mtu->dev, "dma mapping failed\n");
+		return ret;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (mtu3_prepare_transfer(mep)) {
+		ret = -EAGAIN;
+		goto error;
+	}
+
+	list_add_tail(&mreq->list, &mep->req_list);
+	mtu3_insert_gpd(mep, mreq);
+	mtu3_qmu_resume(mep);
+
+error:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3_request *mreq = to_mtu3_request(req);
+	struct mtu3_request *r;
+	unsigned long flags;
+	int ret = 0;
+	struct mtu3 *mtu = mep->mtu;
+
+	if (!ep || !req || mreq->mep != mep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	list_for_each_entry(r, &mep->req_list, list) {
+		if (r == mreq)
+			break;
+	}
+	if (r != mreq) {
+		dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mtu3_qmu_flush(mep);  /* REVISIT: set BPS ?? */
+	mtu3_req_complete(mep, req, -ECONNRESET);
+	mtu3_qmu_start(mep);
+
+done:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+/*
+ * Set or clear the halt bit of an EP.
+ * A halted EP won't TX/RX any data but will queue requests.
+ */
+static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3 *mtu = mep->mtu;
+	struct mtu3_request *mreq;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (mep->type == USB_ENDPOINT_XFER_ISOC) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mreq = next_request(mep);
+	if (value) {
+		/*
+		 * If there is not request for TX-EP, QMU will not transfer
+		 * data to TX-FIFO, so no need check whether TX-FIFO
+		 * holds bytes or not here
+		 */
+		if (mreq) {
+			dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
+				ep->name);
+			ret = -EAGAIN;
+			goto done;
+		}
+	} else {
+		mep->wedged = 0;
+	}
+
+	dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
+
+	mtu3_ep_stall_set(mep, value);
+
+done:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+/* Sets the halt feature with the clear requests ignored */
+static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+
+	if (!ep)
+		return -EINVAL;
+
+	mep->wedged = 1;
+
+	return usb_ep_set_halt(ep);
+}
+
+static const struct usb_ep_ops mtu3_ep_ops = {
+	.enable = mtu3_gadget_ep_enable,
+	.disable = mtu3_gadget_ep_disable,
+	.alloc_request = mtu3_alloc_request,
+	.free_request = mtu3_free_request,
+	.queue = mtu3_gadget_queue,
+	.dequeue = mtu3_gadget_dequeue,
+	.set_halt = mtu3_gadget_ep_set_halt,
+	.set_wedge = mtu3_gadget_ep_set_wedge,
+};
+
+static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+	return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
+}
+
+static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	/* remote wakeup feature not enabled by host */
+	if (!mtu->may_wakeup)
+		return  -EOPNOTSUPP;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	if (mtu->g.speed == USB_SPEED_SUPER) {
+		mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
+	} else {
+		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+		spin_unlock_irqrestore(&mtu->lock, flags);
+		usleep_range(10000, 11000);
+		spin_lock_irqsave(&mtu->lock, flags);
+		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+	}
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return 0;
+}
+
+static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
+		int is_selfpowered)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+	mtu->is_self_powered = !!is_selfpowered;
+	return 0;
+}
+
+static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
+		is_on ? "on" : "off", mtu->is_active ? "" : "in");
+
+	/* we'd rather not pullup unless the device is active. */
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	is_on = !!is_on;
+	if (mtu->is_active == 0) {
+		/* save it for mtu3_start() to process the request */
+		mtu->softconnect = is_on;
+	} else if (is_on != mtu->softconnect) {
+		mtu->softconnect = is_on;
+		mtu3_dev_on_off(mtu, is_on);
+	}
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static int mtu3_gadget_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	if (mtu->gadget_driver) {
+		dev_err(mtu->dev, "%s is already bound to %s\n",
+			mtu->g.name, mtu->gadget_driver->driver.name);
+		return -EBUSY;
+	}
+
+	dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	mtu->softconnect = 0;
+	mtu->gadget_driver = driver;
+
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		mtu3_start(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static void stop_activity(struct mtu3 *mtu)
+{
+	struct usb_gadget_driver *driver = mtu->gadget_driver;
+	int i;
+
+	/* don't disconnect if it's not connected */
+	if (mtu->g.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	else
+		mtu->g.speed = USB_SPEED_UNKNOWN;
+
+	/* deactivate the hardware */
+	if (mtu->softconnect) {
+		mtu->softconnect = 0;
+		mtu3_dev_on_off(mtu, 0);
+	}
+
+	/*
+	 * killing any outstanding requests will quiesce the driver;
+	 * then report disconnect
+	 */
+	nuke(mtu->ep0, -ESHUTDOWN);
+	for (i = 1; i < mtu->num_eps; i++) {
+		nuke(mtu->in_eps + i, -ESHUTDOWN);
+		nuke(mtu->out_eps + i, -ESHUTDOWN);
+	}
+
+	if (driver) {
+		spin_unlock(&mtu->lock);
+		driver->disconnect(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+static int mtu3_gadget_stop(struct usb_gadget *g)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(g);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	stop_activity(mtu);
+	mtu->gadget_driver = NULL;
+
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		mtu3_stop(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops mtu3_gadget_ops = {
+	.get_frame = mtu3_gadget_get_frame,
+	.wakeup = mtu3_gadget_wakeup,
+	.set_selfpowered = mtu3_gadget_set_self_powered,
+	.pullup = mtu3_gadget_pullup,
+	.udc_start = mtu3_gadget_start,
+	.udc_stop = mtu3_gadget_stop,
+};
+
+static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+		u32 epnum, u32 is_in)
+{
+	mep->epnum = epnum;
+	mep->mtu = mtu;
+	mep->is_in = is_in;
+
+	INIT_LIST_HEAD(&mep->req_list);
+
+	sprintf(mep->name, "ep%d%s", epnum,
+		!epnum ? "" : (is_in ? "in" : "out"));
+
+	mep->ep.name = mep->name;
+	INIT_LIST_HEAD(&mep->ep.ep_list);
+
+	/* initialize maxpacket as SS */
+	if (!epnum) {
+		usb_ep_set_maxpacket_limit(&mep->ep, 512);
+		mep->ep.caps.type_control = true;
+		mep->ep.ops = &mtu3_ep0_ops;
+		mtu->g.ep0 = &mep->ep;
+	} else {
+		usb_ep_set_maxpacket_limit(&mep->ep, 1024);
+		mep->ep.caps.type_iso = true;
+		mep->ep.caps.type_bulk = true;
+		mep->ep.caps.type_int = true;
+		mep->ep.ops = &mtu3_ep_ops;
+		list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
+	}
+
+	dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
+		 mep->ep.maxpacket);
+
+	if (!epnum) {
+		mep->ep.caps.dir_in = true;
+		mep->ep.caps.dir_out = true;
+	} else if (is_in) {
+		mep->ep.caps.dir_in = true;
+	} else {
+		mep->ep.caps.dir_out = true;
+	}
+}
+
+static void mtu3_gadget_init_eps(struct mtu3 *mtu)
+{
+	u8 epnum;
+
+	/* initialize endpoint list just once */
+	INIT_LIST_HEAD(&(mtu->g.ep_list));
+
+	dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
+		__func__, mtu->num_eps);
+
+	init_hw_ep(mtu, mtu->ep0, 0, 0);
+	for (epnum = 1; epnum < mtu->num_eps; epnum++) {
+		init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
+		init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
+	}
+}
+
+int mtu3_gadget_setup(struct mtu3 *mtu)
+{
+	int ret;
+
+	mtu->g.ops = &mtu3_gadget_ops;
+	mtu->g.max_speed = mtu->max_speed;
+	mtu->g.speed = USB_SPEED_UNKNOWN;
+	mtu->g.sg_supported = 0;
+	mtu->g.name = MTU3_DRIVER_NAME;
+	mtu->is_active = 0;
+
+	mtu3_gadget_init_eps(mtu);
+
+	ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
+	if (ret) {
+		dev_err(mtu->dev, "failed to register udc\n");
+		return ret;
+	}
+
+	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+
+	return 0;
+}
+
+void mtu3_gadget_cleanup(struct mtu3 *mtu)
+{
+	usb_del_gadget_udc(&mtu->g);
+}
+
+void mtu3_gadget_resume(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget RESUME\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->resume) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->resume(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+/* called when SOF packets stop for 3+ msec */
+void mtu3_gadget_suspend(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget SUSPEND\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->suspend) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->suspend(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+/* called when VBUS drops below session threshold, and in other cases */
+void mtu3_gadget_disconnect(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget DISCONNECT\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->disconnect) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->disconnect(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+
+	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+}
+
+void mtu3_gadget_reset(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "HS RESET\n");
+
+	/* report disconnect, if we didn't flush EP state */
+	if (mtu->g.speed != USB_SPEED_UNKNOWN)
+		mtu3_gadget_disconnect(mtu);
+
+	mtu->address = 0;
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+	mtu->may_wakeup = 0;
+}
+
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
new file mode 100644
index 0000000..14bff37
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -0,0 +1,879 @@
+/*
+ * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Author:  Chunfeng.Yun <chunfeng.yun at mediatek.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 "mtu3.h"
+
+/* ep0 is always mtu3->in_eps[0] */
+#define	next_ep0_request(mtu)	next_request((mtu)->ep0)
+
+/* for high speed test mode; see USB 2.0 spec 7.1.20 */
+static const u8 mtu3_test_packet[53] = {
+	/* implicit SYNC then DATA0 to start */
+
+	/* JKJKJKJK x9 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* JJKKJJKK x8 */
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	/* JJJJKKKK x8 */
+	0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+	/* JJJJJJJKKKKKKK x8 */
+	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	/* JJJJJJJK x8 */
+	0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
+	/* JKKKKKKK x10, JK */
+	0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e,
+	/* implicit CRC16 then EOP to end */
+};
+
+static char *decode_ep0_state(struct mtu3 *mtu)
+{
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_SETUP:
+		return "SETUP";
+	case MU3D_EP0_STATE_TX:
+		return "IN";
+	case MU3D_EP0_STATE_RX:
+		return "OUT";
+	case MU3D_EP0_STATE_TX_END:
+		return "TX-END";
+	case MU3D_EP0_STATE_STALL:
+		return "STALL";
+	default:
+		return "??";
+	}
+}
+
+static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req)
+{
+	mtu3_req_complete(mtu->ep0, req, 0);
+}
+
+static int
+forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+	int ret;
+
+	if (!mtu->gadget_driver)
+		return -EOPNOTSUPP;
+
+	spin_unlock(&mtu->lock);
+	ret = mtu->gadget_driver->setup(&mtu->g, setup);
+	spin_lock(&mtu->lock);
+
+	dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret);
+	return ret;
+}
+
+static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len)
+{
+	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+	u16 index = 0;
+
+	dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n",
+		__func__, mep->epnum, len, src);
+
+	if (len >= 4) {
+		iowrite32_rep(fifo, src, len >> 2);
+		index = len & ~0x03;
+	}
+	if (len & 0x02) {
+		writew(*(u16 *)&src[index], fifo);
+		index += 2;
+	}
+	if (len & 0x01)
+		writeb(src[index], fifo);
+}
+
+static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len)
+{
+	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+	u32 temp;
+	u16 index = 0;
+
+	dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n",
+		 __func__, mep->epnum, len, dst);
+
+	if (len >= 4) {
+		ioread32_rep(fifo, dst, len >> 2);
+		index = len & ~0x03;
+	}
+	if (len & 0x3) {
+		temp = readl(fifo);
+		memcpy(&dst[index], &temp, len & 0x3);
+	}
+
+}
+
+static void ep0_load_test_packet(struct mtu3 *mtu)
+{
+	/*
+	 * because the length of test packet is less than max packet of HS ep0,
+	 * write it into fifo directly.
+	 */
+	ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet));
+}
+
+/*
+ * A. send STALL for setup transfer without data stage:
+ *		set SENDSTALL and SETUPPKTRDY at the same time;
+ * B. send STALL for other cases:
+ *		set SENDSTALL only.
+ */
+static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
+{
+	struct mtu3 *mtu = mep0->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	u32 csr;
+
+	/* EP0_SENTSTALL is W1C */
+	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+	if (set)
+		csr |= EP0_SENDSTALL | pktrdy;
+	else
+		csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+	dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n",
+		set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
+}
+
+static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
+
+static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
+{}
+
+static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	struct usb_set_sel_req sel;
+
+	memcpy(&sel, req->buf, sizeof(sel));
+
+	mreq = to_mtu3_request(req);
+	mtu = mreq->mtu;
+	dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n",
+		sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel);
+}
+
+/* queue data stage to handle 6 byte SET_SEL request */
+static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	int ret;
+
+	if (unlikely(setup->wLength != 6)) {
+		dev_err(mtu->dev, "%s wrong wLength:%d\n",
+			__func__, setup->wLength);
+		return -EINVAL;
+	}
+
+	mtu->ep0_req.mep = mtu->ep0;
+	mtu->ep0_req.request.length = 6;
+	mtu->ep0_req.request.buf = mtu->setup_buf;
+	mtu->ep0_req.request.complete = ep0_set_sel_complete;
+	ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+
+	return ret < 0 ? ret : 1;
+}
+
+static int
+ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+{
+	struct mtu3_ep *mep = NULL;
+	int handled = 1;
+	u8 result[2] = {0, 0};
+	u8 epnum = 0;
+	int is_in;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;
+		result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+		/* superspeed only */
+		if (mtu->g.speed == USB_SPEED_SUPER) {
+			result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;
+			result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;
+		}
+
+		dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__,
+			result[0], mtu->u1_enable, mtu->u2_enable);
+
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = (u8) setup->wIndex;
+		is_in = epnum & USB_DIR_IN;
+		epnum &= USB_ENDPOINT_NUMBER_MASK;
+
+		if (epnum >= mtu->num_eps) {
+			handled = -EINVAL;
+			break;
+		}
+		if (!epnum)
+			break;
+
+		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+		if (!mep->desc) {
+			handled = -EINVAL;
+			break;
+		}
+
+		if (mep->flags & MTU3_EP_STALL)
+			result[0] |= 1 << USB_ENDPOINT_HALT;
+
+		break;
+	default:
+		/* class, vendor, etc ... delegate */
+		handled = 0;
+		break;
+	}
+
+	if (handled > 0) {
+		int ret;
+
+		/* prepare a data stage for GET_STATUS */
+		dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result);
+		memcpy(mtu->setup_buf, result, sizeof(result));
+		mtu->ep0_req.mep = mtu->ep0;
+		mtu->ep0_req.request.length = 2;
+		mtu->ep0_req.request.buf = &mtu->setup_buf;
+		mtu->ep0_req.request.complete = ep0_dummy_complete;
+		ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+		if (ret < 0)
+			handled = ret;
+	}
+	return handled;
+}
+
+static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int handled = 1;
+
+	switch (setup->wIndex >> 8) {
+	case TEST_J:
+		dev_dbg(mtu->dev, "TEST_J\n");
+		mtu->test_mode_nr = TEST_J_MODE;
+		break;
+	case TEST_K:
+		dev_dbg(mtu->dev, "TEST_K\n");
+		mtu->test_mode_nr = TEST_K_MODE;
+		break;
+	case TEST_SE0_NAK:
+		dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
+		mtu->test_mode_nr = TEST_SE0_NAK_MODE;
+		break;
+	case TEST_PACKET:
+		dev_dbg(mtu->dev, "TEST_PACKET\n");
+		mtu->test_mode_nr = TEST_PACKET_MODE;
+		break;
+	default:
+		handled = -EINVAL;
+		goto out;
+	}
+
+	mtu->test_mode = true;
+
+	/* no TX completion interrupt, and need restart platform after test */
+	if (mtu->test_mode_nr == TEST_PACKET_MODE)
+		ep0_load_test_packet(mtu);
+
+	mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
+
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+out:
+	return handled;
+}
+
+static int ep0_handle_feature_dev(struct mtu3 *mtu,
+		struct usb_ctrlrequest *setup, bool set)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int handled = -EINVAL;
+	u32 lpc;
+
+	switch (setup->wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		mtu->may_wakeup = !!set;
+		handled = 1;
+		break;
+	case USB_DEVICE_TEST_MODE:
+		if (!set || (mtu->g.speed != USB_SPEED_HIGH) ||
+			(setup->wIndex & 0xff))
+			break;
+
+		handled = handle_test_mode(mtu, setup);
+		break;
+	case USB_DEVICE_U1_ENABLE:
+		if (mtu->g.speed != USB_SPEED_SUPER ||
+			mtu->g.state != USB_STATE_CONFIGURED)
+			break;
+
+		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+		if (set)
+			lpc |= SW_U1_ACCEPT_ENABLE;
+		else
+			lpc &= ~SW_U1_ACCEPT_ENABLE;
+		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+		mtu->u1_enable = !!set;
+		handled = 1;
+		break;
+	case USB_DEVICE_U2_ENABLE:
+		if (mtu->g.speed != USB_SPEED_SUPER ||
+			mtu->g.state != USB_STATE_CONFIGURED)
+			break;
+
+		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+		if (set)
+			lpc |= SW_U2_ACCEPT_ENABLE;
+		else
+			lpc &= ~SW_U2_ACCEPT_ENABLE;
+		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+		mtu->u2_enable = !!set;
+		handled = 1;
+		break;
+	default:
+		handled = -EINVAL;
+		break;
+	}
+	return handled;
+}
+
+static int ep0_handle_feature(struct mtu3 *mtu,
+		struct usb_ctrlrequest *setup, bool set)
+{
+	struct mtu3_ep *mep;
+	int handled = -EINVAL;
+	int is_in;
+	u8 epnum;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		handled = ep0_handle_feature_dev(mtu, setup, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		/* superspeed only */
+		if ((setup->wValue == USB_INTRF_FUNC_SUSPEND)
+			&& (mtu->g.speed == USB_SPEED_SUPER)) {
+			/*
+			 * forward the request because function drivers
+			 * should handle it
+			 */
+			handled = 0;
+		}
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum == 0 || epnum >= mtu->num_eps ||
+			setup->wValue != USB_ENDPOINT_HALT)
+			break;
+
+		is_in = setup->wIndex & USB_DIR_IN;
+		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+		if (!mep->desc)
+			break;
+
+		handled = 1;
+		/* ignore request if endpoint is wedged */
+		if (mep->wedged)
+			break;
+
+		mtu3_ep_stall_set(mep, set);
+		break;
+	default:
+		/* class, vendor, etc ... delegate */
+		handled = 0;
+		break;
+	}
+	return handled;
+}
+
+/*
+ * handle all control requests can be handled
+ * returns:
+ *	negative errno - error happened
+ *	zero - need delegate SETUP to gadget driver
+ *	positive - already handled
+ */
+static int handle_standard_request(struct mtu3 *mtu,
+			  struct usb_ctrlrequest *setup)
+{
+	void __iomem *mbase = mtu->mac_base;
+	enum usb_device_state state = mtu->g.state;
+	int handled = -EINVAL;
+	u32 dev_conf;
+
+	/* the gadget driver handles everything except what we must handle */
+	switch (setup->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		/* change it after the status stage */
+		mtu->address = (u8) (setup->wValue & 0x7f);
+		dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address);
+
+		dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF);
+		dev_conf &= ~DEV_ADDR_MSK;
+		dev_conf |= DEV_ADDR(mtu->address);
+		mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf);
+
+		if (mtu->address)
+			usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS);
+		else
+			usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT);
+
+		handled = 1;
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		if (state == USB_STATE_ADDRESS) {
+			usb_gadget_set_state(&mtu->g,
+					USB_STATE_CONFIGURED);
+		} else if (state == USB_STATE_CONFIGURED) {
+			/*
+			 * USB2 spec sec 9.4.7, if wValue is 0 then dev
+			 * is moved to addressed state
+			 */
+			if (!setup->wValue)
+				usb_gadget_set_state(&mtu->g,
+						USB_STATE_ADDRESS);
+		}
+		handled = 0;
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		handled = ep0_handle_feature(mtu, setup, 0);
+		break;
+	case USB_REQ_SET_FEATURE:
+		handled = ep0_handle_feature(mtu, setup, 1);
+		break;
+	case USB_REQ_GET_STATUS:
+		handled = ep0_get_status(mtu, setup);
+		break;
+	case USB_REQ_SET_SEL:
+		handled = ep0_set_sel(mtu, setup);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		handled = 1;
+		break;
+	default:
+		/* delegate SET_CONFIGURATION, etc */
+		handled = 0;
+	}
+
+	return handled;
+}
+
+/* receive an data packet (OUT) */
+static void ep0_rx_state(struct mtu3 *mtu)
+{
+	struct mtu3_request *mreq;
+	struct usb_request *req;
+	void __iomem *mbase = mtu->mac_base;
+	u32 maxp;
+	u32 csr;
+	u16 count = 0;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+	mreq = next_ep0_request(mtu);
+	req = &mreq->request;
+
+	/* read packet and ack; or stall because of gadget driver bug */
+	if (req) {
+		void *buf = req->buf + req->actual;
+		unsigned int len = req->length - req->actual;
+
+		/* read the buffer */
+		count = mtu3_readl(mbase, U3D_RXCOUNT0);
+		if (count > len) {
+			req->status = -EOVERFLOW;
+			count = len;
+		}
+		ep0_read_fifo(mtu->ep0, buf, count);
+		req->actual += count;
+		csr |= EP0_RXPKTRDY;
+
+		maxp = mtu->g.ep0->maxpacket;
+		if (count < maxp || req->actual == req->length) {
+			mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+			dev_dbg(mtu->dev, "ep0 state: %s\n",
+				decode_ep0_state(mtu));
+
+			csr |= EP0_DATAEND;
+		} else {
+			req = NULL;
+		}
+	} else {
+		csr |= EP0_RXPKTRDY | EP0_SENDSTALL;
+		dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__);
+	}
+
+	mtu3_writel(mbase, U3D_EP0CSR, csr);
+
+	/* give back the request if have received all data */
+	if (req)
+		ep0_req_giveback(mtu, req);
+
+}
+
+/* transmitting to the host (IN) */
+static void ep0_tx_state(struct mtu3 *mtu)
+{
+	struct mtu3_request *mreq = next_ep0_request(mtu);
+	struct usb_request *req;
+	u32 csr;
+	u8 *src;
+	u8 count;
+	u32 maxp;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	if (!mreq)
+		return;
+
+	maxp = mtu->g.ep0->maxpacket;
+	req = &mreq->request;
+
+	/* load the data */
+	src = (u8 *)req->buf + req->actual;
+	count = min(maxp, req->length - req->actual);
+	if (count)
+		ep0_write_fifo(mtu->ep0, src, count);
+
+	dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n",
+		 __func__, req->actual, req->length, count, maxp, req->zero);
+
+	req->actual += count;
+
+	if ((count < maxp)
+		|| ((req->actual == req->length) && !req->zero))
+		mtu->ep0_state = MU3D_EP0_STATE_TX_END;
+
+	/* send it out, triggering a "txpktrdy cleared" irq */
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY);
+
+	dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__,
+		mtu3_readl(mtu->mac_base, U3D_EP0CSR));
+}
+
+/*
+ * USB2 spec sec8.1, multiple byte fields in standard descriptors, requests,
+ * and responses are interpreted as and moved over the bus in little-endian
+ * order, and MTK SoCs based on ARM is also little-endian, so no need
+ * to convert setup data temp. If need, will add it later
+ */
+static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	struct mtu3_request *mreq;
+	u32 count;
+	u32 csr;
+
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+	count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0);
+
+	ep0_read_fifo(mtu->ep0, (u8 *)setup, count);
+
+	dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n",
+		 setup->bRequestType, setup->bRequest, setup->wValue,
+		 setup->wIndex, setup->wLength);
+
+	/* clean up any leftover transfers */
+	mreq = next_ep0_request(mtu);
+	if (mreq)
+		ep0_req_giveback(mtu, &mreq->request);
+
+	if (setup->wLength == 0) {
+		;	/* no data stage, nothing to do */
+	} else if (setup->bRequestType & USB_DIR_IN) {
+		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+			csr | EP0_SETUPPKTRDY | EP0_DPHTX);
+		mtu->ep0_state = MU3D_EP0_STATE_TX;
+	} else {
+		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+			(csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX));
+		mtu->ep0_state = MU3D_EP0_STATE_RX;
+	}
+}
+
+static int ep0_handle_setup(struct mtu3 *mtu)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+	struct usb_ctrlrequest setup;
+	struct mtu3_request *mreq;
+	void __iomem *mbase = mtu->mac_base;
+	int handled = 0;
+
+	ep0_read_setup(mtu, &setup);
+
+	if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		handled = handle_standard_request(mtu, &setup);
+
+	dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n",
+		 handled, decode_ep0_state(mtu));
+
+	if (handled < 0)
+		goto stall;
+	else if (handled > 0)
+		goto finish;
+
+	handled = forward_to_driver(mtu, &setup);
+	if (handled < 0) {
+stall:
+		dev_err(mtu->dev, "%s stall (%d)\n", __func__, handled);
+
+		ep0_stall_set(mtu->ep0, true,
+				setup.wLength ? 0 : EP0_SETUPPKTRDY);
+
+		return 0;
+	}
+
+finish:
+	if (mtu->test_mode) {
+		;	/* nothing to do */
+	} else if (setup.wLength == 0) { /* no data stage */
+
+		mtu3_writel(mbase, U3D_EP0CSR,
+			(mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
+			| EP0_SETUPPKTRDY | EP0_DATAEND);
+
+		/* complete zlp request directly */
+		mreq = next_ep0_request(mtu);
+		if (mreq && !mreq->request.length)
+			ep0_req_giveback(mtu, &mreq->request);
+	}
+
+	return 0;
+}
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_request *mreq;
+	u32 int_status;
+	irqreturn_t ret = IRQ_NONE;
+	u32 csr;
+	u32 len;
+
+	int_status = mtu3_readl(mbase, U3D_EPISR);
+	int_status &= mtu3_readl(mbase, U3D_EPIER);
+	mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
+
+	/* only handle ep0's */
+	if (!(int_status & EP0ISR))
+		return IRQ_NONE;
+
+	csr = mtu3_readl(mbase, U3D_EP0CSR);
+
+	dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
+
+	/* we sent a stall.. need to clear it now.. */
+	if (csr & EP0_SENTSTALL) {
+		ep0_stall_set(mtu->ep0, false, 0);
+		csr = mtu3_readl(mbase, U3D_EP0CSR);
+		ret = IRQ_HANDLED;
+	}
+	dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_TX:
+		/* irq on clearing txpktrdy */
+		if ((csr & EP0_FIFOFULL) == 0) {
+			ep0_tx_state(mtu);
+			ret = IRQ_HANDLED;
+		}
+		break;
+	case MU3D_EP0_STATE_RX:
+		/* irq on set rxpktrdy */
+		if (csr & EP0_RXPKTRDY) {
+			ep0_rx_state(mtu);
+			ret = IRQ_HANDLED;
+		}
+		break;
+	case MU3D_EP0_STATE_TX_END:
+		mtu3_writel(mbase, U3D_EP0CSR,
+			(csr & EP0_W1C_BITS) | EP0_DATAEND);
+
+		mreq = next_ep0_request(mtu);
+		if (mreq)
+			ep0_req_giveback(mtu, &mreq->request);
+
+		mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+		ret = IRQ_HANDLED;
+		dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+		break;
+	case MU3D_EP0_STATE_SETUP:
+		if (!(csr & EP0_SETUPPKTRDY))
+			break;
+
+		len = mtu3_readl(mbase, U3D_RXCOUNT0);
+		if (len != 8) {
+			dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len);
+			break;
+		}
+
+		ep0_handle_setup(mtu);
+		ret = IRQ_HANDLED;
+		break;
+	default:
+		/* can't happen */
+		ep0_stall_set(mtu->ep0, true, 0);
+		WARN_ON(1);
+		break;
+	}
+
+	return ret;
+}
+
+
+static int mtu3_ep0_enable(struct usb_ep *ep,
+	const struct usb_endpoint_descriptor *desc)
+{
+	/* always enabled */
+	return -EINVAL;
+}
+
+static int mtu3_ep0_disable(struct usb_ep *ep)
+{
+	/* always enabled */
+	return -EINVAL;
+}
+
+static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct mtu3 *mtu = mep->mtu;
+
+	mreq->mtu = mtu;
+	mreq->request.actual = 0;
+	mreq->request.status = -EINPROGRESS;
+
+	dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__,
+		mep->name, decode_ep0_state(mtu), mreq->request.length);
+
+	if (!list_empty(&mep->req_list))
+		return -EBUSY;
+
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_SETUP:
+	case MU3D_EP0_STATE_RX:	/* control-OUT data */
+	case MU3D_EP0_STATE_TX:	/* control-IN data */
+		break;
+	default:
+		dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__,
+			decode_ep0_state(mtu));
+		return -EINVAL;
+	}
+
+	list_add_tail(&mreq->list, &mep->req_list);
+
+	/* sequence #1, IN ... start writing the data */
+	if (mtu->ep0_state == MU3D_EP0_STATE_TX)
+		ep0_tx_state(mtu);
+
+	return 0;
+}
+
+static int mtu3_ep0_queue(struct usb_ep *ep,
+	struct usb_request *req, gfp_t gfp)
+{
+	struct mtu3_ep *mep;
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !req)
+		return -EINVAL;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+	mreq = to_mtu3_request(req);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	ret = ep0_queue(mep, mreq);
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return ret;
+}
+
+static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	/* we just won't support this */
+	return -EINVAL;
+}
+
+static int mtu3_ep0_halt(struct usb_ep *ep, int value)
+{
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !value)
+		return -EINVAL;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (!list_empty(&mep->req_list)) {
+		ret = -EBUSY;
+		goto cleanup;
+	}
+
+	switch (mtu->ep0_state) {
+	/*
+	 * stalls are usually issued after parsing SETUP packet, either
+	 * directly in irq context from setup() or else later.
+	 */
+	case MU3D_EP0_STATE_TX:
+	case MU3D_EP0_STATE_TX_END:
+	case MU3D_EP0_STATE_RX:
+	case MU3D_EP0_STATE_SETUP:
+		ep0_stall_set(mtu->ep0, true, 0);
+		break;
+	default:
+		dev_dbg(mtu->dev, "ep0 can't halt in state %s\n",
+			decode_ep0_state(mtu));
+		ret = -EINVAL;
+	}
+
+cleanup:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return ret;
+}
+
+const struct usb_ep_ops mtu3_ep0_ops = {
+	.enable = mtu3_ep0_enable,
+	.disable = mtu3_ep0_disable,
+	.alloc_request = mtu3_alloc_request,
+	.free_request = mtu3_free_request,
+	.queue = mtu3_ep0_queue,
+	.dequeue = mtu3_ep0_dequeue,
+	.set_halt = mtu3_ep0_halt,
+};
+
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
new file mode 100644
index 0000000..373c0c4
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -0,0 +1,294 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/clk.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define PERI_WK_CTRL1		0x404
+#define UWK_CTL1_IS_C(x)	(((x) & 0xf) << 26)
+#define UWK_CTL1_IS_E		BIT(25)
+#define UWK_CTL1_IDDIG_C(x)	(((x) & 0xf) << 11)  /* cycle debounce */
+#define UWK_CTL1_IDDIG_E	BIT(10) /* enable debounce */
+#define UWK_CTL1_IDDIG_P	BIT(9)  /* polarity */
+#define UWK_CTL1_IS_P		BIT(6)  /* polarity for ip sleep */
+
+/*
+ * ip-sleep wakeup mode:
+ * all clocks can be turn off, but power domain should be kept on
+ */
+static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
+{
+	u32 tmp;
+	struct regmap *pericfg = ssusb->pericfg;
+
+	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+	tmp &= ~UWK_CTL1_IS_P;
+	tmp &= ~(UWK_CTL1_IS_C(0xf));
+	tmp |= UWK_CTL1_IS_C(0x8);
+	regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+	regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
+
+	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+	dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
+		__func__, tmp);
+}
+
+static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
+{
+	u32 tmp;
+
+	regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
+	tmp &= ~UWK_CTL1_IS_E;
+	regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
+}
+
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+				struct device_node *dn)
+{
+	struct device *dev = ssusb->dev;
+
+	/*
+	 * Wakeup function is optional, so it is not an error if this property
+	 * does not exist, and in such case, no need to get relative
+	 * properties anymore.
+	 */
+	ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
+	if (!ssusb->wakeup_en)
+		return 0;
+
+	ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
+	if (IS_ERR(ssusb->wk_deb_p0)) {
+		dev_err(dev, "fail to get wakeup_deb_p0\n");
+		return PTR_ERR(ssusb->wk_deb_p0);
+	}
+
+	if (of_property_read_bool(dn, "wakeup_deb_p1")) {
+		ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
+		if (IS_ERR(ssusb->wk_deb_p1)) {
+			dev_err(dev, "fail to get wakeup_deb_p1\n");
+			return PTR_ERR(ssusb->wk_deb_p1);
+		}
+	}
+
+	ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
+						"mediatek,syscon-wakeup");
+	if (IS_ERR(ssusb->pericfg)) {
+		dev_err(dev, "fail to get pericfg regs\n");
+		return PTR_ERR(ssusb->pericfg);
+	}
+
+	return 0;
+}
+
+static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb)
+{
+	int ret;
+
+	ret = clk_prepare_enable(ssusb->wk_deb_p0);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable wk_deb_p0\n");
+		goto usb_p0_err;
+	}
+
+	ret = clk_prepare_enable(ssusb->wk_deb_p1);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable wk_deb_p1\n");
+		goto usb_p1_err;
+	}
+
+	return 0;
+
+usb_p1_err:
+	clk_disable_unprepare(ssusb->wk_deb_p0);
+usb_p0_err:
+	return -EINVAL;
+}
+
+static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb)
+{
+	clk_disable_unprepare(ssusb->wk_deb_p1);
+	clk_disable_unprepare(ssusb->wk_deb_p0);
+}
+
+static void host_ports_num_get(struct ssusb_mtk *ssusb)
+{
+	u32 xhci_cap;
+
+	xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
+	ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
+	ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
+
+	dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
+		 ssusb->u2_ports, ssusb->u3_ports);
+}
+
+/* only configure ports will be used later */
+int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	int num_u3p = ssusb->u3_ports;
+	int num_u2p = ssusb->u2_ports;
+	u32 check_clk;
+	u32 value;
+	int i;
+
+	/* power on host ip */
+	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+	/* power on and enable all u3 ports */
+	for (i = 0; i < num_u3p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+		value |= SSUSB_U3_PORT_HOST_SEL;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+	}
+
+	/* power on and enable all u2 ports */
+	for (i = 0; i < num_u2p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+		value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+		value |= SSUSB_U2_PORT_HOST_SEL;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+	}
+
+	check_clk = SSUSB_XHCI_RST_B_STS;
+	if (num_u3p)
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+
+	return ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	int num_u3p = ssusb->u3_ports;
+	int num_u2p = ssusb->u2_ports;
+	u32 value;
+	int ret;
+	int i;
+
+	/* power down and disable all u3 ports */
+	for (i = 0; i < num_u3p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+		value |= SSUSB_U3_PORT_PDN;
+		value |= suspend ? 0 : SSUSB_U3_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+	}
+
+	/* power down and disable all u2 ports */
+	for (i = 0; i < num_u2p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+		value |= SSUSB_U2_PORT_PDN;
+		value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+	}
+
+	/* power down host ip */
+	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+	if (!suspend)
+		return 0;
+
+	/* wait for host ip to sleep */
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+			  (value & SSUSB_IP_SLEEP_STS), 100, 100000);
+	if (ret)
+		dev_err(ssusb->dev, "ip sleep failed!!!\n");
+
+	return ret;
+}
+
+static void ssusb_host_setup(struct ssusb_mtk *ssusb)
+{
+	host_ports_num_get(ssusb);
+
+	/*
+	 * power on host and power on/enable all ports
+	 * if support OTG, gadget driver will switch port0 to device mode
+	 */
+	ssusb_host_enable(ssusb);
+
+	/* if port0 supports dual-role, works as host mode by default */
+	ssusb_set_vbus(&ssusb->otg_switch, 1);
+}
+
+static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
+{
+	if (ssusb->is_host)
+		ssusb_set_vbus(&ssusb->otg_switch, 0);
+
+	ssusb_host_disable(ssusb, false);
+}
+
+/*
+ * If host supports multiple ports, the VBUSes(5V) of ports except port0
+ * when it supports OTG are better to be enabled by default in DTS.
+ * Because the host driver will keep link with devices attached when system
+ * enters suspend mode, so no need to control VBUSes after initialization.
+ */
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
+{
+	struct device *parent_dev = ssusb->dev;
+	int ret;
+
+	ssusb_host_setup(ssusb);
+
+	ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
+	if (ret) {
+		dev_dbg(parent_dev, "failed to create child devices at %s\n",
+				parent_dn->full_name);
+		return ret;
+	}
+
+	dev_info(parent_dev, "xHCI platform device register success...\n");
+
+	return 0;
+}
+
+void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{
+	of_platform_depopulate(ssusb->dev);
+	ssusb_host_cleanup(ssusb);
+}
+
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+	int ret = 0;
+
+	if (ssusb->wakeup_en) {
+		ret = ssusb_wakeup_clks_enable(ssusb);
+		ssusb_wakeup_ip_sleep_en(ssusb);
+	}
+	return ret;
+}
+
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{
+	if (ssusb->wakeup_en) {
+		ssusb_wakeup_ip_sleep_dis(ssusb);
+		ssusb_wakeup_clks_disable(ssusb);
+	}
+}
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
new file mode 100644
index 0000000..713a75c
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -0,0 +1,474 @@
+/*
+ * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 _SSUSB_HW_REGS_H_
+#define _SSUSB_HW_REGS_H_
+
+/* segment offset of MAC register */
+#define SSUSB_DEV_BASE		0x0000
+#define SSUSB_EPCTL_CSR_BASE	0x0800
+#define SSUSB_USB3_MAC_CSR_BASE	0x1400
+#define SSUSB_USB3_SYS_CSR_BASE	0x1400
+#define SSUSB_USB2_CSR_BASE	0x2400
+
+/* IPPC register in Infra */
+#define SSUSB_SIFSLV_IPPC_BASE	0x0000
+
+/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
+
+#define U3D_LV1ISR		(SSUSB_DEV_BASE + 0x0000)
+#define U3D_LV1IER		(SSUSB_DEV_BASE + 0x0004)
+#define U3D_LV1IESR		(SSUSB_DEV_BASE + 0x0008)
+#define U3D_LV1IECR		(SSUSB_DEV_BASE + 0x000C)
+
+#define U3D_EPISR		(SSUSB_DEV_BASE + 0x0080)
+#define U3D_EPIER		(SSUSB_DEV_BASE + 0x0084)
+#define U3D_EPIESR		(SSUSB_DEV_BASE + 0x0088)
+#define U3D_EPIECR		(SSUSB_DEV_BASE + 0x008C)
+
+#define U3D_EP0CSR		(SSUSB_DEV_BASE + 0x0100)
+#define U3D_RXCOUNT0		(SSUSB_DEV_BASE + 0x0108)
+#define U3D_RESERVED		(SSUSB_DEV_BASE + 0x010C)
+#define U3D_TX1CSR0		(SSUSB_DEV_BASE + 0x0110)
+#define U3D_TX1CSR1		(SSUSB_DEV_BASE + 0x0114)
+#define U3D_TX1CSR2		(SSUSB_DEV_BASE + 0x0118)
+
+#define U3D_RX1CSR0		(SSUSB_DEV_BASE + 0x0210)
+#define U3D_RX1CSR1		(SSUSB_DEV_BASE + 0x0214)
+#define U3D_RX1CSR2		(SSUSB_DEV_BASE + 0x0218)
+#define U3D_RX1CSR3		(SSUSB_DEV_BASE + 0x021C)
+
+#define U3D_FIFO0		(SSUSB_DEV_BASE + 0x0300)
+
+#define U3D_QCR0		(SSUSB_DEV_BASE + 0x0400)
+#define U3D_QCR1		(SSUSB_DEV_BASE + 0x0404)
+#define U3D_QCR2		(SSUSB_DEV_BASE + 0x0408)
+#define U3D_QCR3		(SSUSB_DEV_BASE + 0x040C)
+
+#define U3D_TXQCSR1		(SSUSB_DEV_BASE + 0x0510)
+#define U3D_TXQSAR1		(SSUSB_DEV_BASE + 0x0514)
+#define U3D_TXQCPR1		(SSUSB_DEV_BASE + 0x0518)
+
+#define U3D_RXQCSR1		(SSUSB_DEV_BASE + 0x0610)
+#define U3D_RXQSAR1		(SSUSB_DEV_BASE + 0x0614)
+#define U3D_RXQCPR1		(SSUSB_DEV_BASE + 0x0618)
+#define U3D_RXQLDPR1		(SSUSB_DEV_BASE + 0x061C)
+
+#define U3D_QISAR0		(SSUSB_DEV_BASE + 0x0700)
+#define U3D_QIER0		(SSUSB_DEV_BASE + 0x0704)
+#define U3D_QIESR0		(SSUSB_DEV_BASE + 0x0708)
+#define U3D_QIECR0		(SSUSB_DEV_BASE + 0x070C)
+#define U3D_QISAR1		(SSUSB_DEV_BASE + 0x0710)
+#define U3D_QIER1		(SSUSB_DEV_BASE + 0x0714)
+#define U3D_QIESR1		(SSUSB_DEV_BASE + 0x0718)
+#define U3D_QIECR1		(SSUSB_DEV_BASE + 0x071C)
+
+#define U3D_TQERRIR0		(SSUSB_DEV_BASE + 0x0780)
+#define U3D_TQERRIER0		(SSUSB_DEV_BASE + 0x0784)
+#define U3D_TQERRIESR0		(SSUSB_DEV_BASE + 0x0788)
+#define U3D_TQERRIECR0		(SSUSB_DEV_BASE + 0x078C)
+#define U3D_RQERRIR0		(SSUSB_DEV_BASE + 0x07C0)
+#define U3D_RQERRIER0		(SSUSB_DEV_BASE + 0x07C4)
+#define U3D_RQERRIESR0		(SSUSB_DEV_BASE + 0x07C8)
+#define U3D_RQERRIECR0		(SSUSB_DEV_BASE + 0x07CC)
+#define U3D_RQERRIR1		(SSUSB_DEV_BASE + 0x07D0)
+#define U3D_RQERRIER1		(SSUSB_DEV_BASE + 0x07D4)
+#define U3D_RQERRIESR1		(SSUSB_DEV_BASE + 0x07D8)
+#define U3D_RQERRIECR1		(SSUSB_DEV_BASE + 0x07DC)
+
+#define U3D_CAP_EP0FFSZ		(SSUSB_DEV_BASE + 0x0C04)
+#define U3D_CAP_EPNTXFFSZ	(SSUSB_DEV_BASE + 0x0C08)
+#define U3D_CAP_EPNRXFFSZ	(SSUSB_DEV_BASE + 0x0C0C)
+#define U3D_CAP_EPINFO		(SSUSB_DEV_BASE + 0x0C10)
+#define U3D_MISC_CTRL		(SSUSB_DEV_BASE + 0x0C84)
+
+/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
+
+/* U3D_LV1ISR */
+#define EP_CTRL_INTR		BIT(5)
+#define MAC2_INTR		BIT(4)
+#define DMA_INTR		BIT(3)
+#define MAC3_INTR		BIT(2)
+#define QMU_INTR		BIT(1)
+#define BMU_INTR		BIT(0)
+
+/* U3D_LV1IECR */
+#define LV1IECR_MSK		GENMASK(31, 0)
+
+/* U3D_EPISR */
+#define EPRISR(x)		(BIT(16) << (x))
+#define EPTISR(x)		(BIT(0) << (x))
+#define EP0ISR			BIT(0)
+
+/* U3D_EP0CSR */
+#define EP0_SENDSTALL		BIT(25)
+#define EP0_FIFOFULL		BIT(23)
+#define EP0_SENTSTALL		BIT(22)
+#define EP0_DPHTX		BIT(20)
+#define EP0_DATAEND		BIT(19)
+#define EP0_TXPKTRDY		BIT(18)
+#define EP0_SETUPPKTRDY		BIT(17)
+#define EP0_RXPKTRDY		BIT(16)
+#define EP0_MAXPKTSZ_MSK	GENMASK(9, 0)
+#define EP0_MAXPKTSZ(x)		((x) & EP0_MAXPKTSZ_MSK)
+#define EP0_W1C_BITS	(~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
+
+/* U3D_TX1CSR0 */
+#define TX_DMAREQEN		BIT(29)
+#define TX_FIFOFULL		BIT(25)
+#define TX_FIFOEMPTY		BIT(24)
+#define TX_SENTSTALL		BIT(22)
+#define TX_SENDSTALL		BIT(21)
+#define TX_TXPKTRDY		BIT(16)
+#define TX_TXMAXPKTSZ_MSK	GENMASK(10, 0)
+#define TX_TXMAXPKTSZ(x)	((x) & TX_TXMAXPKTSZ_MSK)
+#define TX_W1C_BITS		(~(TX_SENTSTALL))
+
+/* U3D_TX1CSR1 */
+#define TX_MULT(x)		(((x) & 0x3) << 22)
+#define TX_MAX_PKT(x)		(((x) & 0x3f) << 16)
+#define TX_SLOT(x)		(((x) & 0x3f) << 8)
+#define TX_TYPE(x)		(((x) & 0x3) << 4)
+#define TX_SS_BURST(x)		(((x) & 0xf) << 0)
+
+/* for TX_TYPE & RX_TYPE */
+#define TYPE_BULK		(0x0)
+#define TYPE_INT		(0x1)
+#define TYPE_ISO		(0x2)
+#define TYPE_MASK		(0x3)
+
+/* U3D_TX1CSR2 */
+#define TX_BINTERVAL(x)		(((x) & 0xff) << 24)
+#define TX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
+#define TX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
+
+/* U3D_RX1CSR0 */
+#define RX_DMAREQEN		BIT(29)
+#define RX_SENTSTALL		BIT(22)
+#define RX_SENDSTALL		BIT(21)
+#define RX_RXPKTRDY		BIT(16)
+#define RX_RXMAXPKTSZ_MSK	GENMASK(10, 0)
+#define RX_RXMAXPKTSZ(x)	((x) & RX_RXMAXPKTSZ_MSK)
+#define RX_W1C_BITS		(~(RX_SENTSTALL | RX_RXPKTRDY))
+
+/* U3D_RX1CSR1 */
+#define RX_MULT(x)		(((x) & 0x3) << 22)
+#define RX_MAX_PKT(x)		(((x) & 0x3f) << 16)
+#define RX_SLOT(x)		(((x) & 0x3f) << 8)
+#define RX_TYPE(x)		(((x) & 0x3) << 4)
+#define RX_SS_BURST(x)		(((x) & 0xf) << 0)
+
+/* U3D_RX1CSR2 */
+#define RX_BINTERVAL(x)		(((x) & 0xff) << 24)
+#define RX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
+#define RX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
+
+/* U3D_QCR0 */
+#define QMU_RX_CS_EN(x)		(BIT(16) << (x))
+#define QMU_TX_CS_EN(x)		(BIT(0) << (x))
+#define QMU_CS16B_EN		BIT(0)
+
+/* U3D_QCR1 */
+#define QMU_TX_ZLP(x)		(BIT(0) << (x))
+
+/* U3D_QCR3 */
+#define QMU_RX_COZ(x)		(BIT(16) << (x))
+#define QMU_RX_ZLP(x)		(BIT(0) << (x))
+
+/* U3D_TXQCSR1 */
+/* U3D_RXQCSR1 */
+#define QMU_Q_ACTIVE		BIT(15)
+#define QMU_Q_STOP		BIT(2)
+#define QMU_Q_RESUME		BIT(1)
+#define QMU_Q_START		BIT(0)
+
+/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
+#define QMU_RX_DONE_INT(x)	(BIT(16) << (x))
+#define QMU_TX_DONE_INT(x)	(BIT(0) << (x))
+
+/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
+#define RXQ_ZLPERR_INT		BIT(20)
+#define RXQ_LENERR_INT		BIT(18)
+#define RXQ_CSERR_INT		BIT(17)
+#define RXQ_EMPTY_INT		BIT(16)
+#define TXQ_LENERR_INT		BIT(2)
+#define TXQ_CSERR_INT		BIT(1)
+#define TXQ_EMPTY_INT		BIT(0)
+
+/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
+#define QMU_TX_LEN_ERR(x)	(BIT(16) << (x))
+#define QMU_TX_CS_ERR(x)	(BIT(0) << (x))
+
+/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
+#define QMU_RX_LEN_ERR(x)	(BIT(16) << (x))
+#define QMU_RX_CS_ERR(x)	(BIT(0) << (x))
+
+/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
+#define QMU_RX_ZLP_ERR(n)	(BIT(16) << (n))
+
+/* U3D_CAP_EPINFO */
+#define CAP_RX_EP_NUM(x)	(((x) >> 8) & 0x1f)
+#define CAP_TX_EP_NUM(x)	((x) & 0x1f)
+
+/* U3D_MISC_CTRL */
+#define VBUS_ON			BIT(1)
+#define VBUS_FRC_EN		BIT(0)
+
+
+/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_DEVICE_CONF			(SSUSB_EPCTL_CSR_BASE + 0x0000)
+#define U3D_EP_RST			(SSUSB_EPCTL_CSR_BASE + 0x0004)
+
+#define U3D_DEV_LINK_INTR_ENABLE	(SSUSB_EPCTL_CSR_BASE + 0x0050)
+#define U3D_DEV_LINK_INTR		(SSUSB_EPCTL_CSR_BASE + 0x0054)
+
+/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_DEVICE_CONF */
+#define DEV_ADDR_MSK		GENMASK(30, 24)
+#define DEV_ADDR(x)		((0x7f & (x)) << 24)
+#define HW_USB2_3_SEL		BIT(18)
+#define SW_USB2_3_SEL_EN	BIT(17)
+#define SW_USB2_3_SEL		BIT(16)
+#define SSUSB_DEV_SPEED(x)	((x) & 0x7)
+
+/* U3D_EP_RST */
+#define EP1_IN_RST		BIT(17)
+#define EP1_OUT_RST		BIT(1)
+#define EP_RST(is_in, epnum)	(((is_in) ? BIT(16) : BIT(0)) << (epnum))
+#define EP0_RST			BIT(0)
+
+/* U3D_DEV_LINK_INTR_ENABLE */
+/* U3D_DEV_LINK_INTR */
+#define SSUSB_DEV_SPEED_CHG_INTR	BIT(0)
+
+
+/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LTSSM_CTRL		(SSUSB_USB3_MAC_CSR_BASE + 0x0010)
+#define U3D_USB3_CONFIG		(SSUSB_USB3_MAC_CSR_BASE + 0x001C)
+
+#define U3D_LTSSM_INTR_ENABLE	(SSUSB_USB3_MAC_CSR_BASE + 0x013C)
+#define U3D_LTSSM_INTR		(SSUSB_USB3_MAC_CSR_BASE + 0x0140)
+
+/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LTSSM_CTRL */
+#define FORCE_POLLING_FAIL	BIT(4)
+#define FORCE_RXDETECT_FAIL	BIT(3)
+#define SOFT_U3_EXIT_EN		BIT(2)
+#define COMPLIANCE_EN		BIT(1)
+#define U1_GO_U2_EN		BIT(0)
+
+/* U3D_USB3_CONFIG */
+#define USB3_EN			BIT(0)
+
+/* U3D_LTSSM_INTR_ENABLE */
+/* U3D_LTSSM_INTR */
+#define U3_RESUME_INTR		BIT(18)
+#define U3_LFPS_TMOUT_INTR	BIT(17)
+#define VBUS_FALL_INTR		BIT(16)
+#define VBUS_RISE_INTR		BIT(15)
+#define RXDET_SUCCESS_INTR	BIT(14)
+#define EXIT_U3_INTR		BIT(13)
+#define EXIT_U2_INTR		BIT(12)
+#define EXIT_U1_INTR		BIT(11)
+#define ENTER_U3_INTR		BIT(10)
+#define ENTER_U2_INTR		BIT(9)
+#define ENTER_U1_INTR		BIT(8)
+#define ENTER_U0_INTR		BIT(7)
+#define RECOVERY_INTR		BIT(6)
+#define WARM_RST_INTR		BIT(5)
+#define HOT_RST_INTR		BIT(4)
+#define LOOPBACK_INTR		BIT(3)
+#define COMPLIANCE_INTR		BIT(2)
+#define SS_DISABLE_INTR		BIT(1)
+#define SS_INACTIVE_INTR	BIT(0)
+
+/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LINK_UX_INACT_TIMER	(SSUSB_USB3_SYS_CSR_BASE + 0x020C)
+#define U3D_LINK_POWER_CONTROL	(SSUSB_USB3_SYS_CSR_BASE + 0x0210)
+#define U3D_LINK_ERR_COUNT	(SSUSB_USB3_SYS_CSR_BASE + 0x0214)
+
+/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LINK_UX_INACT_TIMER */
+#define DEV_U2_INACT_TIMEOUT_MSK	GENMASK(23, 16)
+#define DEV_U2_INACT_TIMEOUT_VALUE(x)	(((x) & 0xff) << 16)
+#define U2_INACT_TIMEOUT_MSK		GENMASK(15, 8)
+#define U1_INACT_TIMEOUT_MSK		GENMASK(7, 0)
+#define U1_INACT_TIMEOUT_VALUE(x)	((x) & 0xff)
+
+/* U3D_LINK_POWER_CONTROL */
+#define SW_U2_ACCEPT_ENABLE	BIT(9)
+#define SW_U1_ACCEPT_ENABLE	BIT(8)
+#define UX_EXIT			BIT(5)
+#define LGO_U3			BIT(4)
+#define LGO_U2			BIT(3)
+#define LGO_U1			BIT(2)
+#define SW_U2_REQUEST_ENABLE	BIT(1)
+#define SW_U1_REQUEST_ENABLE	BIT(0)
+
+/* U3D_LINK_ERR_COUNT */
+#define CLR_LINK_ERR_CNT	BIT(16)
+#define LINK_ERROR_COUNT	GENMASK(15, 0)
+
+/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_POWER_MANAGEMENT		(SSUSB_USB2_CSR_BASE + 0x0004)
+#define U3D_DEVICE_CONTROL		(SSUSB_USB2_CSR_BASE + 0x000C)
+#define U3D_USB2_TEST_MODE		(SSUSB_USB2_CSR_BASE + 0x0014)
+#define U3D_COMMON_USB_INTR_ENABLE	(SSUSB_USB2_CSR_BASE + 0x0018)
+#define U3D_COMMON_USB_INTR		(SSUSB_USB2_CSR_BASE + 0x001C)
+#define U3D_LINK_RESET_INFO		(SSUSB_USB2_CSR_BASE + 0x0024)
+#define U3D_USB20_FRAME_NUM		(SSUSB_USB2_CSR_BASE + 0x003C)
+#define U3D_USB20_LPM_PARAMETER		(SSUSB_USB2_CSR_BASE + 0x0044)
+#define U3D_USB20_MISC_CONTROL		(SSUSB_USB2_CSR_BASE + 0x004C)
+
+/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_POWER_MANAGEMENT */
+#define LPM_BESL_STALL		BIT(14)
+#define LPM_BESLD_STALL		BIT(13)
+#define LPM_RWP			BIT(11)
+#define LPM_HRWE		BIT(10)
+#define LPM_MODE(x)		(((x) & 0x3) << 8)
+#define ISO_UPDATE		BIT(7)
+#define SOFT_CONN		BIT(6)
+#define HS_ENABLE		BIT(5)
+#define RESUME			BIT(2)
+#define SUSPENDM_ENABLE		BIT(0)
+
+/* U3D_DEVICE_CONTROL */
+#define DC_HOSTREQ		BIT(1)
+#define DC_SESSION		BIT(0)
+
+/* U3D_USB2_TEST_MODE */
+#define U2U3_AUTO_SWITCH	BIT(10)
+#define LPM_FORCE_STALL		BIT(8)
+#define FIFO_ACCESS		BIT(6)
+#define FORCE_FS		BIT(5)
+#define FORCE_HS		BIT(4)
+#define TEST_PACKET_MODE	BIT(3)
+#define TEST_K_MODE		BIT(2)
+#define TEST_J_MODE		BIT(1)
+#define TEST_SE0_NAK_MODE	BIT(0)
+
+/* U3D_COMMON_USB_INTR_ENABLE */
+/* U3D_COMMON_USB_INTR */
+#define LPM_RESUME_INTR		BIT(9)
+#define LPM_INTR		BIT(8)
+#define DISCONN_INTR		BIT(5)
+#define CONN_INTR		BIT(4)
+#define SOF_INTR		BIT(3)
+#define RESET_INTR		BIT(2)
+#define RESUME_INTR		BIT(1)
+#define SUSPEND_INTR		BIT(0)
+
+/* U3D_LINK_RESET_INFO */
+#define WTCHRP_MSK		GENMASK(19, 16)
+
+/* U3D_USB20_LPM_PARAMETER */
+#define LPM_BESLCK_U3(x)	(((x) & 0xf) << 12)
+#define LPM_BESLCK(x)		(((x) & 0xf) << 8)
+#define LPM_BESLDCK(x)		(((x) & 0xf) << 4)
+#define LPM_BESL		GENMASK(3, 0)
+
+/* U3D_USB20_MISC_CONTROL */
+#define LPM_U3_ACK_EN		BIT(0)
+
+/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
+
+#define U3D_SSUSB_IP_PW_CTRL0	(SSUSB_SIFSLV_IPPC_BASE + 0x0000)
+#define U3D_SSUSB_IP_PW_CTRL1	(SSUSB_SIFSLV_IPPC_BASE + 0x0004)
+#define U3D_SSUSB_IP_PW_CTRL2	(SSUSB_SIFSLV_IPPC_BASE + 0x0008)
+#define U3D_SSUSB_IP_PW_CTRL3	(SSUSB_SIFSLV_IPPC_BASE + 0x000C)
+#define U3D_SSUSB_IP_PW_STS1	(SSUSB_SIFSLV_IPPC_BASE + 0x0010)
+#define U3D_SSUSB_IP_PW_STS2	(SSUSB_SIFSLV_IPPC_BASE + 0x0014)
+#define U3D_SSUSB_OTG_STS	(SSUSB_SIFSLV_IPPC_BASE + 0x0018)
+#define U3D_SSUSB_OTG_STS_CLR	(SSUSB_SIFSLV_IPPC_BASE + 0x001C)
+#define U3D_SSUSB_IP_XHCI_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0024)
+#define U3D_SSUSB_IP_DEV_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0028)
+#define U3D_SSUSB_OTG_INT_EN	(SSUSB_SIFSLV_IPPC_BASE + 0x002C)
+#define U3D_SSUSB_U3_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0030)
+#define U3D_SSUSB_U2_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0050)
+#define U3D_SSUSB_REF_CK_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x008C)
+#define U3D_SSUSB_DEV_RST_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x0098)
+#define U3D_SSUSB_HW_ID		(SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
+#define U3D_SSUSB_HW_SUB_ID	(SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
+#define U3D_SSUSB_IP_SPARE0	(SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
+
+/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
+
+/* U3D_SSUSB_IP_PW_CTRL0 */
+#define SSUSB_IP_SW_RST			BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL1 */
+#define SSUSB_IP_HOST_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL2 */
+#define SSUSB_IP_DEV_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL3 */
+#define SSUSB_IP_PCIE_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS1 */
+#define SSUSB_IP_SLEEP_STS		BIT(30)
+#define SSUSB_U3_MAC_RST_B_STS		BIT(16)
+#define SSUSB_XHCI_RST_B_STS		BIT(11)
+#define SSUSB_SYS125_RST_B_STS		BIT(10)
+#define SSUSB_REF_RST_B_STS		BIT(8)
+#define SSUSB_SYSPLL_STABLE		BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS2 */
+#define SSUSB_U2_MAC_SYS_RST_B_STS	BIT(0)
+
+/* U3D_SSUSB_OTG_STS */
+#define SSUSB_VBUS_VALID		BIT(9)
+
+/* U3D_SSUSB_OTG_STS_CLR */
+#define SSUSB_VBUS_INTR_CLR		BIT(6)
+
+/* U3D_SSUSB_IP_XHCI_CAP */
+#define SSUSB_IP_XHCI_U2_PORT_NUM(x)	(((x) >> 8) & 0xff)
+#define SSUSB_IP_XHCI_U3_PORT_NUM(x)	((x) & 0xff)
+
+/* U3D_SSUSB_IP_DEV_CAP */
+#define SSUSB_IP_DEV_U3_PORT_NUM(x)	((x) & 0xff)
+
+/* U3D_SSUSB_OTG_INT_EN */
+#define SSUSB_VBUS_CHG_INT_A_EN		BIT(7)
+#define SSUSB_VBUS_CHG_INT_B_EN		BIT(6)
+
+/* U3D_SSUSB_U3_CTRL_0P */
+#define SSUSB_U3_PORT_HOST_SEL		BIT(2)
+#define SSUSB_U3_PORT_PDN		BIT(1)
+#define SSUSB_U3_PORT_DIS		BIT(0)
+
+/* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_OTG_SEL		BIT(7)
+#define SSUSB_U2_PORT_HOST_SEL		BIT(2)
+#define SSUSB_U2_PORT_PDN		BIT(1)
+#define SSUSB_U2_PORT_DIS		BIT(0)
+
+/* U3D_SSUSB_DEV_RST_CTRL */
+#define SSUSB_DEV_SW_RST		BIT(0)
+
+#endif	/* _SSUSB_HW_REGS_H_ */
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
new file mode 100644
index 0000000..d74bd9e
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+/* u2-port0 should be powered on and enabled; */
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	u32 value, check_val;
+	int ret;
+
+	check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
+			SSUSB_REF_RST_B_STS;
+
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+			(check_val == (value & check_val)), 100, 20000);
+	if (ret) {
+		dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
+		return ret;
+	}
+
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
+			(value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
+	if (ret) {
+		dev_err(ssusb->dev, "mac2 clock is not stable\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ssusb_phy_init(struct ssusb_mtk *ssusb)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ret = phy_init(ssusb->phys[i]);
+		if (ret)
+			goto exit_phy;
+	}
+	return 0;
+
+exit_phy:
+	for (; i > 0; i--)
+		phy_exit(ssusb->phys[i - 1]);
+
+	return ret;
+}
+
+static int ssusb_phy_exit(struct ssusb_mtk *ssusb)
+{
+	int i;
+
+	for (i = 0; i < ssusb->num_phys; i++)
+		phy_exit(ssusb->phys[i]);
+
+	return 0;
+}
+
+static int ssusb_phy_power_on(struct ssusb_mtk *ssusb)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ret = phy_power_on(ssusb->phys[i]);
+		if (ret)
+			goto power_off_phy;
+	}
+	return 0;
+
+power_off_phy:
+	for (; i > 0; i--)
+		phy_power_off(ssusb->phys[i - 1]);
+
+	return ret;
+}
+
+static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
+{
+	unsigned int i;
+
+	for (i = 0; i < ssusb->num_phys; i++)
+		phy_power_off(ssusb->phys[i]);
+}
+
+static int ssusb_common_init(struct ssusb_mtk *ssusb)
+{
+	int ret = 0;
+
+	ret = regulator_enable(ssusb->vusb33);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable vusb33\n");
+		goto vusb33_err;
+	}
+
+	ret = clk_prepare_enable(ssusb->sys_clk);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable sys_clk\n");
+		goto clk_err;
+	}
+
+	ret = ssusb_phy_init(ssusb);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to init phy\n");
+		goto phy_init_err;
+	}
+
+	ret = ssusb_phy_power_on(ssusb);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to power on phy\n");
+		goto phy_err;
+	}
+
+	return 0;
+
+phy_err:
+	ssusb_phy_exit(ssusb);
+
+phy_init_err:
+	clk_disable_unprepare(ssusb->sys_clk);
+
+clk_err:
+	regulator_disable(ssusb->vusb33);
+
+vusb33_err:
+
+	return ret;
+}
+
+static void ssusb_common_exit(struct ssusb_mtk *ssusb)
+{
+	clk_disable_unprepare(ssusb->sys_clk);
+	regulator_disable(ssusb->vusb33);
+	ssusb_phy_power_off(ssusb);
+	ssusb_phy_exit(ssusb);
+}
+
+static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
+{
+	/* reset whole ip (xhci & u3d) */
+	mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+	udelay(1);
+	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+}
+
+static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
+	if (IS_ERR(otg_sx->id_pinctrl)) {
+		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
+		return PTR_ERR(otg_sx->id_pinctrl);
+	}
+
+	otg_sx->id_float =
+		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
+	if (IS_ERR(otg_sx->id_float)) {
+		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
+		return PTR_ERR(otg_sx->id_float);
+	}
+
+	otg_sx->id_ground =
+		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
+	if (IS_ERR(otg_sx->id_ground)) {
+		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
+		return PTR_ERR(otg_sx->id_ground);
+	}
+
+	return 0;
+}
+
+static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+	struct device *dev = &pdev->dev;
+	struct regulator *vbus;
+	struct resource *res;
+	int i;
+	int ret;
+
+	ssusb->num_phys = of_count_phandle_with_args(node,
+			"phys", "#phy-cells");
+	if (ssusb->num_phys > 0) {
+		ssusb->phys = devm_kcalloc(dev, ssusb->num_phys,
+					sizeof(*ssusb->phys), GFP_KERNEL);
+		if (!ssusb->phys)
+			return -ENOMEM;
+	} else {
+		ssusb->num_phys = 0;
+	}
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ssusb->phys[i] = devm_of_phy_get_by_index(dev, node, i);
+		if (IS_ERR(ssusb->phys[i])) {
+			dev_err(dev, "failed to get phy-%d\n", i);
+			return PTR_ERR(ssusb->phys[i]);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
+	ssusb->ippc_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ssusb->ippc_base)) {
+		dev_err(dev, "failed to map memory for ippc\n");
+		return PTR_ERR(ssusb->ippc_base);
+	}
+
+	ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33");
+	if (IS_ERR(ssusb->vusb33)) {
+		dev_err(dev, "failed to get vusb33\n");
+		return PTR_ERR(ssusb->vusb33);
+	}
+
+	ssusb->sys_clk = devm_clk_get(dev, "sys_ck");
+	if (IS_ERR(ssusb->sys_clk)) {
+		dev_err(dev, "failed to get sys clock\n");
+		return PTR_ERR(ssusb->sys_clk);
+	}
+
+	ssusb->dr_mode = usb_get_dr_mode(dev);
+	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(dev, "dr_mode is error\n");
+		return -EINVAL;
+	}
+
+	if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		return 0;
+
+	/* if host role is supported */
+	ret = ssusb_wakeup_of_property_parse(ssusb, node);
+	if (ret)
+		return ret;
+
+	if (ssusb->dr_mode != USB_DR_MODE_OTG)
+		return 0;
+
+	/* if dual-role mode is supported */
+	vbus = devm_regulator_get(&pdev->dev, "vbus");
+	if (IS_ERR(vbus)) {
+		dev_err(dev, "failed to get vbus\n");
+		return PTR_ERR(vbus);
+	}
+	otg_sx->vbus = vbus;
+
+	otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
+	otg_sx->manual_drd_enabled =
+		of_property_read_bool(node, "enable-manual-drd");
+
+	if (of_property_read_bool(node, "extcon")) {
+		otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
+		if (IS_ERR(otg_sx->edev)) {
+			dev_err(ssusb->dev, "couldn't get extcon device\n");
+			return -EPROBE_DEFER;
+		}
+		if (otg_sx->manual_drd_enabled) {
+			ret = get_iddig_pinctrl(ssusb);
+			if (ret)
+				return ret;
+		}
+	}
+
+	dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n",
+		ssusb->dr_mode, otg_sx->is_u3_drd);
+
+	return 0;
+}
+
+static int mtu3_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct ssusb_mtk *ssusb;
+	int ret = -ENOMEM;
+
+	/* all elements are set to ZERO as default value */
+	ssusb = devm_kzalloc(dev, sizeof(*ssusb), GFP_KERNEL);
+	if (!ssusb)
+		return -ENOMEM;
+
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "No suitable DMA config available\n");
+		return -ENOTSUPP;
+	}
+
+	platform_set_drvdata(pdev, ssusb);
+	ssusb->dev = dev;
+
+	ret = get_ssusb_rscs(pdev, ssusb);
+	if (ret)
+		return ret;
+
+	/* enable power domain */
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+	device_enable_async_suspend(dev);
+
+	ret = ssusb_common_init(ssusb);
+	if (ret)
+		goto comm_init_err;
+
+	ssusb_ip_sw_reset(ssusb);
+
+	if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
+		ssusb->dr_mode = USB_DR_MODE_HOST;
+	else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
+		ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+	/* default as host */
+	ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL);
+
+	switch (ssusb->dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		ret = ssusb_gadget_init(ssusb);
+		if (ret) {
+			dev_err(dev, "failed to initialize gadget\n");
+			goto comm_exit;
+		}
+		break;
+	case USB_DR_MODE_HOST:
+		ret = ssusb_host_init(ssusb, node);
+		if (ret) {
+			dev_err(dev, "failed to initialize host\n");
+			goto comm_exit;
+		}
+		break;
+	case USB_DR_MODE_OTG:
+		ret = ssusb_gadget_init(ssusb);
+		if (ret) {
+			dev_err(dev, "failed to initialize gadget\n");
+			goto comm_exit;
+		}
+
+		ret = ssusb_host_init(ssusb, node);
+		if (ret) {
+			dev_err(dev, "failed to initialize host\n");
+			goto gadget_exit;
+		}
+
+		ssusb_otg_switch_init(ssusb);
+		break;
+	default:
+		dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode);
+		ret = -EINVAL;
+		goto comm_exit;
+	}
+
+	return 0;
+
+gadget_exit:
+	ssusb_gadget_exit(ssusb);
+
+comm_exit:
+	ssusb_common_exit(ssusb);
+
+comm_init_err:
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+static int mtu3_remove(struct platform_device *pdev)
+{
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	switch (ssusb->dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		ssusb_gadget_exit(ssusb);
+		break;
+	case USB_DR_MODE_HOST:
+		ssusb_host_exit(ssusb);
+		break;
+	case USB_DR_MODE_OTG:
+		ssusb_otg_switch_exit(ssusb);
+		ssusb_gadget_exit(ssusb);
+		ssusb_host_exit(ssusb);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ssusb_common_exit(ssusb);
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+/*
+ * when support dual-role mode, we reject suspend when
+ * it works as device mode;
+ */
+static int __maybe_unused mtu3_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	/* REVISIT: disconnect it for only device mode? */
+	if (!ssusb->is_host)
+		return 0;
+
+	ssusb_host_disable(ssusb, true);
+	ssusb_phy_power_off(ssusb);
+	clk_disable_unprepare(ssusb->sys_clk);
+	ssusb_wakeup_enable(ssusb);
+
+	return 0;
+}
+
+static int __maybe_unused mtu3_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (!ssusb->is_host)
+		return 0;
+
+	ssusb_wakeup_disable(ssusb);
+	clk_prepare_enable(ssusb->sys_clk);
+	ssusb_phy_power_on(ssusb);
+	ssusb_host_enable(ssusb);
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtu3_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
+};
+
+#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id mtu3_of_match[] = {
+	{.compatible = "mediatek,mt8173-mtu3",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mtu3_of_match);
+
+#endif
+
+static struct platform_driver mtu3_driver = {
+	.probe = mtu3_probe,
+	.remove = mtu3_remove,
+	.driver = {
+		.name = MTU3_DRIVER_NAME,
+		.pm = DEV_PM_OPS,
+		.of_match_table = of_match_ptr(mtu3_of_match),
+	},
+};
+module_platform_driver(mtu3_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
+
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
new file mode 100644
index 0000000..3fd687e
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -0,0 +1,599 @@
+/*
+ * mtu3_qmu.c - Queue Management Unit driver for device controller
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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.
+ *
+ */
+
+/*
+ * Queue Management Unit (QMU) is designed to unload SW effort
+ * to serve DMA interrupts.
+ * By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD),
+ * SW links data buffers and triggers QMU to send / receive data to
+ * host / from device at a time.
+ * And now only GPD is supported.
+ *
+ * For more detailed information, please refer to QMU Programming Guide
+ */
+
+#include <linux/dmapool.h>
+#include <linux/iopoll.h>
+
+#include "mtu3.h"
+
+#define QMU_CHECKSUM_LEN	16
+
+#define GPD_FLAGS_HWO	BIT(0)
+#define GPD_FLAGS_BDP	BIT(1)
+#define GPD_FLAGS_BPS	BIT(2)
+#define GPD_FLAGS_IOC	BIT(7)
+
+#define GPD_EXT_FLAG_ZLP	BIT(5)
+
+
+static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,
+		dma_addr_t dma_addr)
+{
+	dma_addr_t dma_base = ring->dma;
+	struct qmu_gpd *gpd_head = ring->start;
+	u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head);
+
+	if (offset >= MAX_GPD_NUM)
+		return NULL;
+
+	return gpd_head + offset;
+}
+
+static dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring,
+		struct qmu_gpd *gpd)
+{
+	dma_addr_t dma_base = ring->dma;
+	struct qmu_gpd *gpd_head = ring->start;
+	u32 offset;
+
+	offset = gpd - gpd_head;
+	if (offset >= MAX_GPD_NUM)
+		return 0;
+
+	return dma_base + (offset * sizeof(*gpd));
+}
+
+static void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd)
+{
+	ring->start = gpd;
+	ring->enqueue = gpd;
+	ring->dequeue = gpd;
+	ring->end = gpd + MAX_GPD_NUM - 1;
+}
+
+static void reset_gpd_list(struct mtu3_ep *mep)
+{
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->start;
+
+	if (gpd) {
+		gpd->flag &= ~GPD_FLAGS_HWO;
+		gpd_ring_init(ring, gpd);
+	}
+}
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep)
+{
+	struct qmu_gpd *gpd;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+	/* software own all gpds as default */
+	gpd = dma_pool_zalloc(mep->mtu->qmu_gpd_pool, GFP_ATOMIC, &ring->dma);
+	if (gpd == NULL)
+		return -ENOMEM;
+
+	gpd_ring_init(ring, gpd);
+
+	return 0;
+}
+
+void mtu3_gpd_ring_free(struct mtu3_ep *mep)
+{
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+	dma_pool_free(mep->mtu->qmu_gpd_pool,
+			ring->start, ring->dma);
+	memset(ring, 0, sizeof(*ring));
+}
+
+/*
+ * calculate check sum of a gpd or bd
+ * add "noinline" and "mb" to prevent wrong calculation
+ */
+static noinline u8 qmu_calc_checksum(u8 *data)
+{
+	u8 chksum = 0;
+	int i;
+
+	data[1] = 0x0;  /* set checksum to 0 */
+
+	mb();	/* ensure the gpd/bd is really up-to-date */
+	for (i = 0; i < QMU_CHECKSUM_LEN; i++)
+		chksum += data[i];
+
+	/* Default: HWO=1, @flag[bit0] */
+	chksum += 1;
+
+	return 0xFF - chksum;
+}
+
+void mtu3_qmu_resume(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 offset;
+
+	offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+	mtu3_writel(mbase, offset, QMU_Q_RESUME);
+	if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE))
+		mtu3_writel(mbase, offset, QMU_Q_RESUME);
+}
+
+static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring)
+{
+	if (ring->enqueue < ring->end)
+		ring->enqueue++;
+	else
+		ring->enqueue = ring->start;
+
+	return ring->enqueue;
+}
+
+static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
+{
+	if (ring->dequeue < ring->end)
+		ring->dequeue++;
+	else
+		ring->dequeue = ring->start;
+
+	return ring->dequeue;
+}
+
+/* check if a ring is emtpy */
+int gpd_ring_empty(struct mtu3_gpd_ring *ring)
+{
+	struct qmu_gpd *enq = ring->enqueue;
+	struct qmu_gpd *next;
+
+	if (ring->enqueue < ring->end)
+		next = enq + 1;
+	else
+		next = ring->start;
+
+	/* one gpd is reserved to simplify gpd preparation */
+	return next == ring->dequeue;
+}
+
+int mtu3_prepare_transfer(struct mtu3_ep *mep)
+{
+	return gpd_ring_empty(&mep->gpd_ring);
+}
+
+static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct qmu_gpd *enq;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->enqueue;
+	struct usb_request *req = &mreq->request;
+
+	/* set all fields to zero as default value */
+	memset(gpd, 0, sizeof(*gpd));
+
+	gpd->buffer = (u32)req->dma;
+	gpd->buf_len = req->length;
+	gpd->flag |= GPD_FLAGS_IOC;
+
+	/* get the next GPD */
+	enq = advance_enq_gpd(ring);
+	dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n",
+		mep->epnum, gpd, enq);
+
+	enq->flag &= ~GPD_FLAGS_HWO;
+	gpd->next_gpd = (u32)gpd_virt_to_dma(ring, enq);
+
+	if (req->zero)
+		gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
+
+	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+	gpd->flag |= GPD_FLAGS_HWO;
+
+	mreq->gpd = gpd;
+
+	return 0;
+}
+
+static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct qmu_gpd *enq;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->enqueue;
+	struct usb_request *req = &mreq->request;
+
+	/* set all fields to zero as default value */
+	memset(gpd, 0, sizeof(*gpd));
+
+	gpd->buffer = (u32)req->dma;
+	gpd->data_buf_len = req->length;
+	gpd->flag |= GPD_FLAGS_IOC;
+
+	/* get the next GPD */
+	enq = advance_enq_gpd(ring);
+	dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n",
+		mep->epnum, gpd, enq);
+
+	enq->flag &= ~GPD_FLAGS_HWO;
+	gpd->next_gpd = (u32)gpd_virt_to_dma(ring, enq);
+	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+	gpd->flag |= GPD_FLAGS_HWO;
+
+	mreq->gpd = gpd;
+
+	return 0;
+}
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+
+	if (mep->is_in)
+		mtu3_prepare_tx_gpd(mep, mreq);
+	else
+		mtu3_prepare_rx_gpd(mep, mreq);
+}
+
+int mtu3_qmu_start(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	u8 epnum = mep->epnum;
+
+	if (mep->is_in) {
+		/* set QMU start address */
+		mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma);
+		mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+		mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));
+
+		/* send zero length packet according to ZLP flag in GPD */
+		mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
+
+		mtu3_writel(mbase, U3D_TQERRIESR0,
+				QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum));
+
+		if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
+			dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
+			return 0;
+		}
+
+		mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);
+
+	} else {
+
+		mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma);
+		mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN);
+		mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));
+
+		/* don't expect ZLP */
+		mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
+		/* move to next GPD when receive ZLP */
+		mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
+
+		mtu3_writel(mbase, U3D_RQERRIESR0,
+				QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
+		mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));
+
+		if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
+			dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
+			return 0;
+		}
+
+		mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
+	}
+
+	return 0;
+}
+
+/* may called in atomic context */
+void mtu3_qmu_stop(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 csr = 0;
+	u32 reg_csr;
+	int ret;
+
+	reg_csr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+	if (!(mtu3_readl(mbase, reg_csr) & QMU_Q_ACTIVE)) {
+		dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
+		return;
+	}
+	mtu3_writel(mbase, reg_csr, QMU_Q_STOP);
+
+	ret = readl_poll_timeout_atomic(mbase + reg_csr, csr,
+			!(csr & QMU_Q_ACTIVE), 1, 1000);
+	if (ret) {
+		dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
+		return;
+	}
+
+	dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
+}
+
+void mtu3_qmu_flush(struct mtu3_ep *mep)
+{
+
+	dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
+		((mep->is_in) ? "TX" : "RX"));
+
+	/*Stop QMU */
+	mtu3_qmu_stop(mep);
+	reset_gpd_list(mep);
+}
+
+/*
+ * QMU can't transfer zero length packet directly (a hardware issue
+ * on old SoCs), so when needs to send ZLP, we intentionally trigger
+ * a length error interrupt, and in the ISR sends a ZLP by BMU.
+ */
+static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->in_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+	struct usb_request *req = NULL;
+	struct mtu3_request *mreq;
+	u32 txcsr = 0;
+	int ret;
+
+	mreq = next_request(mep);
+	if (mreq && mreq->request.length == 0)
+		req = &mreq->request;
+	else
+		return;
+
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	if (gpd_current->buf_len != 0) {
+		dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum);
+		return;
+	}
+
+	dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
+
+	mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+
+	ret = readl_poll_timeout_atomic(mbase + MU3D_EP_TXCR0(mep->epnum),
+			txcsr, !(txcsr & TX_FIFOFULL), 1, 1000);
+	if (ret) {
+		dev_err(mtu->dev, "%s wait for fifo empty fail\n", __func__);
+		return;
+	}
+	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY);
+
+	/* by pass the current GDP */
+	gpd_current->flag |= GPD_FLAGS_BPS;
+	gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current);
+	gpd_current->flag |= GPD_FLAGS_HWO;
+
+	/*enable DMAREQEN, switch back to QMU mode */
+	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+	mtu3_qmu_resume(mep);
+}
+
+/*
+ * NOTE: request list maybe is already empty as following case:
+ * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)-->
+ * queue_tx --> process_tasklet(meanwhile, the second one is transferred,
+ * tasklet process both of them)-->qmu_interrupt for second one.
+ * To avoid upper case, put qmu_done_tx in ISR directly to process it.
+ */
+static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->in_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd = ring->dequeue;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+	struct usb_request *request = NULL;
+	struct mtu3_request *mreq;
+
+	/*transfer phy address got from QMU register to virtual address */
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+		__func__, epnum, gpd, gpd_current, ring->enqueue);
+
+	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+		mreq = next_request(mep);
+
+		if (mreq == NULL || mreq->gpd != gpd) {
+			dev_err(mtu->dev, "no correct TX req is found\n");
+			break;
+		}
+
+		request = &mreq->request;
+		request->actual = gpd->buf_len;
+		mtu3_req_complete(mep, request, 0);
+
+		gpd = advance_deq_gpd(ring);
+	}
+
+	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+		__func__, epnum, ring->dequeue, ring->enqueue);
+
+}
+
+static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->out_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd = ring->dequeue;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));
+	struct usb_request *req = NULL;
+	struct mtu3_request *mreq;
+
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+		__func__, epnum, gpd, gpd_current, ring->enqueue);
+
+	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+		mreq = next_request(mep);
+
+		if (mreq == NULL || mreq->gpd != gpd) {
+			dev_err(mtu->dev, "no correct RX req is found\n");
+			break;
+		}
+		req = &mreq->request;
+
+		req->actual = gpd->buf_len;
+		mtu3_req_complete(mep, req, 0);
+
+		gpd = advance_deq_gpd(ring);
+	}
+
+	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+		__func__, epnum, ring->dequeue, ring->enqueue);
+}
+
+static void qmu_done_tasklet(unsigned long data)
+{
+	struct mtu3 *mtu = (struct mtu3 *)data;
+	unsigned long flags;
+	u32 qmu_val;
+	int i;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	qmu_val = mtu->qmu_done_intr;
+
+	mtu->qmu_done_intr = 0;
+
+	for (i = 1; i < mtu->num_eps; i++) {
+		if (qmu_val & QMU_RX_DONE_INT(i))
+			qmu_done_rx(mtu, i);
+		if (qmu_val & QMU_TX_DONE_INT(i))
+			qmu_done_tx(mtu, i);
+	}
+	spin_unlock_irqrestore(&mtu->lock, flags);
+}
+
+static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 errval;
+	int i;
+
+	if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) {
+		errval = mtu3_readl(mbase, U3D_RQERRIR0);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_RX_CS_ERR(i))
+				dev_err(mtu->dev, "Rx %d CS error!\n", i);
+
+			if (errval & QMU_RX_LEN_ERR(i))
+				dev_err(mtu->dev, "RX %d Length error\n", i);
+		}
+		mtu3_writel(mbase, U3D_RQERRIR0, errval);
+	}
+
+	if (qmu_status & RXQ_ZLPERR_INT) {
+		errval = mtu3_readl(mbase, U3D_RQERRIR1);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_RX_ZLP_ERR(i))
+				dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i);
+		}
+		mtu3_writel(mbase, U3D_RQERRIR1, errval);
+	}
+
+	if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) {
+		errval = mtu3_readl(mbase, U3D_TQERRIR0);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_TX_CS_ERR(i))
+				dev_err(mtu->dev, "Tx %d checksum error!\n", i);
+
+			if (errval & QMU_TX_LEN_ERR(i))
+				qmu_tx_zlp_error_handler(mtu, i);
+		}
+		mtu3_writel(mbase, U3D_TQERRIR0, errval);
+	}
+}
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 qmu_status;
+	u32 qmu_done_status;
+
+	/* U3D_QISAR1 is read update */
+	qmu_status = mtu3_readl(mbase, U3D_QISAR1);
+	qmu_status &= mtu3_readl(mbase, U3D_QIER1);
+
+	qmu_done_status = mtu3_readl(mbase, U3D_QISAR0);
+	qmu_done_status &= mtu3_readl(mbase, U3D_QIER0);
+	mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */
+	dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n",
+		(qmu_done_status & 0xFFFF), qmu_done_status >> 16,
+		qmu_status);
+
+	if (qmu_done_status) {
+		/* qmu maybe not handled yet */
+		mtu->qmu_done_intr |= qmu_done_status;
+		tasklet_schedule(&mtu->qmu_done);
+	}
+
+	if (qmu_status)
+		qmu_exception_isr(mtu, qmu_status);
+
+	return IRQ_HANDLED;
+}
+
+int mtu3_qmu_init(struct mtu3 *mtu)
+{
+
+	compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B");
+
+	mtu->qmu_gpd_pool = dma_pool_create("QMU_GPD", mtu->dev,
+			QMU_GPD_RING_SIZE, QMU_GPD_SIZE, 0);
+
+	if (!mtu->qmu_gpd_pool)
+		return -ENOMEM;
+
+	tasklet_init(&mtu->qmu_done, qmu_done_tasklet, (unsigned long)mtu);
+
+	return 0;
+}
+
+int mtu3_qmu_exit(struct mtu3 *mtu)
+{
+	tasklet_kill(&mtu->qmu_done);
+	dma_pool_destroy(mtu->qmu_gpd_pool);
+
+	return 0;
+}
+
diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h
new file mode 100644
index 0000000..2e07f5b
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.h
@@ -0,0 +1,43 @@
+/*
+ * mtu3_qmu.h - Queue Management Unit driver header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 __MTK_QMU_H__
+#define __MTK_QMU_H__
+
+#define MAX_GPD_NUM		64
+#define QMU_GPD_SIZE		(sizeof(struct qmu_gpd))
+#define QMU_GPD_RING_SIZE	(MAX_GPD_NUM * QMU_GPD_SIZE)
+
+#define GPD_BUF_SIZE		65532
+
+void mtu3_qmu_stop(struct mtu3_ep *mep);
+int mtu3_qmu_start(struct mtu3_ep *mep);
+void mtu3_qmu_resume(struct mtu3_ep *mep);
+void mtu3_qmu_flush(struct mtu3_ep *mep);
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
+int mtu3_prepare_transfer(struct mtu3_ep *mep);
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
+void mtu3_gpd_ring_free(struct mtu3_ep *mep);
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
+int mtu3_qmu_init(struct mtu3 *mtu);
+int mtu3_qmu_exit(struct mtu3 *mtu);
+
+#endif
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:40 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:40 +0800
Subject: [PATCH v3,3/5] usb: xhci-mtk: make IPPC register optional
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465543962-11678-4-git-send-email-chunfeng.yun@mediatek.com>

Make IPPC register optional to support host side of dual-role mode,
due to it is moved into common glue layer for simplification.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/host/xhci-mtk.c |   32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 79959f1..dc86832 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -94,6 +94,9 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
 	int ret;
 	int i;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* power on host ip */
 	value = readl(&ippc->ip_pw_ctr1);
 	value &= ~CTRL1_IP_HOST_PDN;
@@ -139,6 +142,9 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
 	int ret;
 	int i;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* power down all u3 ports */
 	for (i = 0; i < mtk->num_u3_ports; i++) {
 		value = readl(&ippc->u3_ctrl_p[i]);
@@ -173,6 +179,9 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
 	struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
 	u32 value;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* reset whole ip */
 	value = readl(&ippc->ip_pw_ctr0);
 	value |= CTRL0_IP_SW_RST;
@@ -475,6 +484,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
 /* called during probe() after chip reset completes */
 static int xhci_mtk_setup(struct usb_hcd *hcd)
 {
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
 	int ret;
 
@@ -482,12 +492,21 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
 		ret = xhci_mtk_ssusb_config(mtk);
 		if (ret)
 			return ret;
+	}
+
+	ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
+	if (ret)
+		return ret;
+
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		mtk->num_u3_ports = xhci->num_usb3_ports;
+		mtk->num_u2_ports = xhci->num_usb2_ports;
 		ret = xhci_mtk_sch_init(mtk);
 		if (ret)
 			return ret;
 	}
 
-	return xhci_gen_setup(hcd, xhci_mtk_quirks);
+	return ret;
 }
 
 static int xhci_mtk_probe(struct platform_device *pdev)
@@ -595,11 +614,14 @@ static int xhci_mtk_probe(struct platform_device *pdev)
 	hcd->rsrc_start = res->start;
 	hcd->rsrc_len = resource_size(res);
 
+	mtk->ippc_regs = NULL;
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	mtk->ippc_regs = devm_ioremap_resource(dev, res);
-	if (IS_ERR(mtk->ippc_regs)) {
-		ret = PTR_ERR(mtk->ippc_regs);
-		goto put_usb2_hcd;
+	if (res) {	/* ippc register is optional */
+		mtk->ippc_regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(mtk->ippc_regs)) {
+			ret = PTR_ERR(mtk->ippc_regs);
+			goto put_usb2_hcd;
+		}
 	}
 
 	for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:38 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:38 +0800
Subject: [PATCH v3,
 1/5] dt-bindings: mt8173-xhci: support host side of dual-role mode
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465543962-11678-2-git-send-email-chunfeng.yun@mediatek.com>

Some resources, such as IPPC register etc, shared with device
driver are moved into common glue layer when xHCI driver is the
host side of dual-role mode and they should be changed as optional
properties if they are required ones before. For clarity, add
a new part of binding to support host side of dual-role mode.

Additionally add optional properties of pinctrl for host only mode

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
index b3a7ffa..bec2dff 100644
--- a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
@@ -2,6 +2,14 @@ MT8173 xHCI
 
 The device node for Mediatek SOC USB3.0 host controller
 
+There are two scenarios: the first one only supports xHCI driver;
+the second one supports dual-role mode, and the host is based on xHCI
+driver. Take account of backward compatibility, we divide bindings
+into two parts.
+
+1st: only supports xHCI driver
+------------------------------------------------------------------------
+
 Required properties:
  - compatible : should contain "mediatek,mt8173-xhci"
  - reg : specifies physical base address and size of the registers,
@@ -27,6 +35,9 @@ Optional properties:
 	control register, it depends on "mediatek,wakeup-src".
  - vbus-supply : reference to the VBUS regulator;
  - usb3-lpm-capable : supports USB3.0 LPM
+ - pinctrl-names : a pinctrl state named "default" must be defined
+ - pinctrl-0 : pin control group
+	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
 
 Example:
 usb30: usb at 11270000 {
@@ -49,3 +60,40 @@ usb30: usb at 11270000 {
 	mediatek,syscon-wakeup = <&pericfg>;
 	mediatek,wakeup-src = <1>;
 };
+
+2nd: dual-role mode with xHCI driver
+------------------------------------------------------------------------
+
+In the case, xhci is added as subnode to mtu3. An example and the DT binding
+details of mtu3 can be found in:
+Documentation/devicetree/bindings/usb/mtu3.txt
+
+Required properties:
+ - compatible : should contain "mediatek,mt8173-xhci"
+ - reg : specifies physical base address and size of the registers,
+	should be only for xHCI IP registers
+ - interrupts : interrupt used by the host controller
+ - power-domains : a phandle to USB power domain node to control USB's
+	mtcmos
+ - vusb33-supply : regulator of USB avdd3.3v
+
+ - clocks : a list of phandle + clock-specifier pairs, one for each
+	entry in clock-names
+ - clock-names : must be
+	"sys_ck": for clock of xHCI MAC
+
+Optional properties:
+ - vbus-supply : reference to the VBUS regulator;
+ - usb3-lpm-capable : supports USB3.0 LPM
+
+Example:
+usb30: usb at 11270000 {
+	compatible = "mediatek,mt8173-xhci";
+	reg = <0 0x11270000 0 0x1000>;
+	interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+	clocks = <&topckgen CLK_TOP_USB30_SEL>;
+	clock-names = "sys_ck";
+	vusb33-supply = <&mt6397_vusb_reg>;
+	usb3-lpm-capable;
+};
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:42 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:42 +0800
Subject: [PATCH v3,5/5] arm64: dts: mediatek: add USB3 DRD driver
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465543962-11678-6-git-send-email-chunfeng.yun@mediatek.com>

USB3 DRD driver is added for MT8173-EVB, and xHCI driver
becomes its subnode

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts |   46 +++++++++++++++++++++++++--
 arch/arm64/boot/dts/mediatek/mt8173.dtsi    |   28 ++++++++++++----
 2 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
index 7453a47..682dfd7 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
@@ -34,6 +34,11 @@
 
 	chosen { };
 
+	extcon_usb: extcon_iddig {
+		compatible = "linux,extcon-usb-gpio";
+		id-gpio = <&pio 16 GPIO_ACTIVE_HIGH>;
+	};
+
 	usb_p1_vbus: regulator at 0 {
 		compatible = "regulator-fixed";
 		regulator-name = "usb_vbus";
@@ -42,6 +47,16 @@
 		gpio = <&pio 130 GPIO_ACTIVE_HIGH>;
 		enable-active-high;
 	};
+
+	usb_p0_vbus: regulator at 1 {
+		compatible = "regulator-fixed";
+		regulator-name = "vbus";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		gpio = <&pio 9 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
+
 };
 
 &i2c1 {
@@ -205,6 +220,20 @@
 			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
 		};
 	};
+
+	usb_id_pins_float: usb_iddig_pull_up {
+		pins_iddig {
+			pinmux = <MT8173_PIN_16_IDDIG__FUNC_IDDIG>;
+			bias-pull-up;
+		};
+	};
+
+	usb_id_pins_ground: usb_iddig_pull_down {
+		pins_iddig {
+			pinmux = <MT8173_PIN_16_IDDIG__FUNC_IDDIG>;
+			bias-pull-down;
+		};
+	};
 };
 
 &pwm0 {
@@ -431,12 +460,25 @@
 	status = "okay";
 };
 
+&ssusb {
+	vusb33-supply = <&mt6397_vusb_reg>;
+	vbus-supply = <&usb_p0_vbus>;
+	extcon = <&extcon_usb>;
+	dr_mode = "otg";
+	mediatek,enable-wakeup;
+	pinctrl-names = "default", "id_float", "id_ground";
+	pinctrl-0 = <&usb_id_pins_float>;
+	pinctrl-1 = <&usb_id_pins_float>;
+	pinctrl-2 = <&usb_id_pins_ground>;
+	status = "okay";
+};
+
 &uart0 {
 	status = "okay";
 };
 
-&usb30 {
+&usb_host {
 	vusb33-supply = <&mt6397_vusb_reg>;
 	vbus-supply = <&usb_p1_vbus>;
-	mediatek,wakeup-src = <1>;
+	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 05f89c4..723ab6e 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -608,11 +608,14 @@
 			status = "disabled";
 		};
 
-		usb30: usb at 11270000 {
-			compatible = "mediatek,mt8173-xhci";
-			reg = <0 0x11270000 0 0x1000>,
+		ssusb: usb at 11271000 {
+			compatible = "mediatek,mt8173-mtu3";
+			reg = <0 0x11271000 0 0x3000>,
 			      <0 0x11280700 0 0x0100>;
-			interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+			reg-names = "mac", "ippc";
+			interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
+			phys = <&phy_port0 PHY_TYPE_USB3>,
+			       <&phy_port1 PHY_TYPE_USB2>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
 			clocks = <&topckgen CLK_TOP_USB30_SEL>,
 				 <&pericfg CLK_PERI_USB0>,
@@ -620,10 +623,21 @@
 			clock-names = "sys_ck",
 				      "wakeup_deb_p0",
 				      "wakeup_deb_p1";
-			phys = <&phy_port0 PHY_TYPE_USB3>,
-			       <&phy_port1 PHY_TYPE_USB2>;
 			mediatek,syscon-wakeup = <&pericfg>;
-			status = "okay";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+			status = "disabled";
+
+			usb_host: xhci at 11270000 {
+				compatible = "mediatek,mt8173-xhci";
+				reg = <0 0x11270000 0 0x1000>;
+				interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+				power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+				clocks = <&topckgen CLK_TOP_USB30_SEL>;
+				clock-names = "sys_ck";
+				status = "disabled";
+			};
 		};
 
 		u3phy: usb-phy at 11290000 {
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:39 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:39 +0800
Subject: [PATCH v3,2/5] dt-bindings: mt8173-mtu3: add devicetree bindings
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465543962-11678-3-git-send-email-chunfeng.yun@mediatek.com>

add a DT binding doc for MediaTek USB3 DRD driver

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 .../devicetree/bindings/usb/mt8173-mtu3.txt        |   85 ++++++++++++++++++++
 1 file changed, 85 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt

diff --git a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
new file mode 100644
index 0000000..571ae8b
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
@@ -0,0 +1,85 @@
+The device node for Mediatek USB3.0 DRD controller
+
+Required properties:
+ - compatible : should be "mediatek,mt8173-mtu3"
+ - reg : specifies physical base address and size of the registers
+ - reg-names: should be "mac" for device IP and "ippc" for IP port control
+ - interrupts : interrupt used by the device IP
+ - power-domains : a phandle to USB power domain node to control USB's
+	mtcmos
+ - vusb33-supply : regulator of USB avdd3.3v
+ - clocks : a list of phandle + clock-specifier pairs, one for each
+	entry in clock-names
+ - clock-names : must contain "sys_ck" for clock of controller;
+	"wakeup_deb_p0" and "wakeup_deb_p1" are optional, they are
+	depends on "mediatek,enable-wakeup"
+ - phys : a list of phandle + phy specifier pairs
+ - dr_mode : should be one of "host", "peripheral" or "otg",
+	refer to usb/generic.txt
+
+Optional properties:
+ - #address-cells, #size-cells : should be '2' if the device has sub-nodes
+	with 'reg' property
+ - ranges : allows valid 1:1 translation between child's address space and
+	parent's address space
+ - extcon : external connector for vbus and idpin changes detection, needed
+	when supports dual-role mode.
+ - vbus-supply : reference to the VBUS regulator, needed when supports
+	dual-role mode.
+ - pinctl-names : a pinctrl state named "default" must be defined,
+	"id_float" and "id_ground" are optinal which depends on
+	"mediatek,enable-manual-drd"
+ - pinctrl-0 : pin control group
+	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+	"full-speed"; refer to usb/generic.txt
+ - enable-manual-drd : supports manual dual-role switch via sysfs; only used
+	when receptacle is TYPE-A and also wants to support dual-role mode.
+ - mediatek,enable-wakeup : supports ip sleep wakeup used by host mode
+ - mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
+	control register, it depends on "mediatek,enable-wakeup".
+
+Sub-nodes:
+The xhci should be added as subnode to mtu3 as shown in the following example
+if host mode is enabled. The DT binding details of xhci can be found in:
+Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+
+Example:
+ssusb: usb at 11271000 {
+	compatible = "mediatek,mt8173-mtu3";
+	reg = <0 0x11271000 0 0x3000>,
+	      <0 0x11280700 0 0x0100>;
+	reg-names = "mac", "ippc";
+	interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
+	phys = <&phy_port0 PHY_TYPE_USB3>,
+	       <&phy_port1 PHY_TYPE_USB2>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+	clocks = <&topckgen CLK_TOP_USB30_SEL>,
+		 <&pericfg CLK_PERI_USB0>,
+		 <&pericfg CLK_PERI_USB1>;
+	clock-names = "sys_ck",
+		      "wakeup_deb_p0",
+		      "wakeup_deb_p1";
+	vusb33-supply = <&mt6397_vusb_reg>;
+	vbus-supply = <&usb_p0_vbus>;
+	extcon = <&extcon_usb>;
+	dr_mode = "otg";
+	mediatek,enable-wakeup;
+	mediatek,syscon-wakeup = <&pericfg>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+	ranges;
+	status = "disabled";
+
+	usb_host: xhci at 11270000 {
+		compatible = "mediatek,mt8173-xhci";
+		reg = <0 0x11270000 0 0x1000>;
+		interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+		power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+		clocks = <&topckgen CLK_TOP_USB30_SEL>;
+		clock-names = "sys_ck";
+		vusb33-supply = <&mt6397_vusb_reg>;
+		status = "disabled";
+	};
+};
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Fri Jun 10 00:32:37 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Fri, 10 Jun 2016 15:32:37 +0800
Subject: Add MediaTek USB3 DRD Driver 
Message-ID: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>

>From 8fdc924cecf0ca14da6e8aba360501e53437e548 Mon Sep 17 00:00:00 2001
From: Chunfeng Yun <chunfeng.yun at mediatek.com>
Date: Fri, 10 Jun 2016 15:20:20 +0800
Subject: [PATCH 0/5] Add MediaTek USB3 DRD Driver

These patches introduce the MediaTek USB3 dual-role controller
driver.

The driver can be configured as Dual-Role Device (DRD),
Peripheral Only and Host Only (xHCI) modes. It works well
with Mass Storage, RNDIS and g_zero on FS/HS and SS. And it is
tested on MT8173 platform which only contains USB2.0 device IP,
and on MT6290 platform which contains USB3.0 device IP.

Change in v3:
1. fix some typo error
2. rename mtu3.txt to mt8173-mtu3.txt

Change in v2:
1. modify binding docs according to suggestions
2. modify some comments and remove some dummy blank lines
3. fix memory leakage

Chunfeng Yun (5):
  dt-bindings: mt8173-xhci: support host side of dual-role mode
  dt-bindings: mt8173-mtu3: add devicetree bindings
  usb: xhci-mtk: make IPPC register optional
  usb: Add MediaTek USB3 DRD Driver
  arm64: dts: mediatek: add USB3 DRD driver

 .../devicetree/bindings/usb/mt8173-mtu3.txt        |   85 ++
 .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts        |   46 +-
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   28 +-
 drivers/usb/Kconfig                                |    2 +
 drivers/usb/Makefile                               |    1 +
 drivers/usb/host/xhci-mtk.c                        |   32 +-
 drivers/usb/mtu3/Kconfig                           |   47 ++
 drivers/usb/mtu3/Makefile                          |   20 +
 drivers/usb/mtu3/mtu3.h                            |  422 ++++++++++
 drivers/usb/mtu3/mtu3_core.c                       |  879 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_dr.c                         |  348 ++++++++
 drivers/usb/mtu3/mtu3_dr.h                         |  108 +++
 drivers/usb/mtu3/mtu3_gadget.c                     |  731 ++++++++++++++++
 drivers/usb/mtu3/mtu3_gadget_ep0.c                 |  879 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_host.c                       |  294 +++++++
 drivers/usb/mtu3/mtu3_hw_regs.h                    |  474 +++++++++++
 drivers/usb/mtu3/mtu3_plat.c                       |  490 +++++++++++
 drivers/usb/mtu3/mtu3_qmu.c                        |  599 +++++++++++++
 drivers/usb/mtu3/mtu3_qmu.h                        |   43 +
 20 files changed, 5562 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
 create mode 100644 drivers/usb/mtu3/Kconfig
 create mode 100644 drivers/usb/mtu3/Makefile
 create mode 100644 drivers/usb/mtu3/mtu3.h
 create mode 100644 drivers/usb/mtu3/mtu3_core.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.h
 create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
 create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
 create mode 100644 drivers/usb/mtu3/mtu3_host.c
 create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
 create mode 100644 drivers/usb/mtu3/mtu3_plat.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.h

--
1.7.9.5




From chunfeng.yun at mediatek.com  Fri Jun 10 02:15:09 2016
From: chunfeng.yun at mediatek.com (chunfeng yun)
Date: Fri, 10 Jun 2016 17:15:09 +0800
Subject: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465550109.11651.147.camel@mhfsdcap03>

Hi,
On Fri, 2016-06-10 at 15:32 +0800, Chunfeng Yun wrote:
> From 8fdc924cecf0ca14da6e8aba360501e53437e548 Mon Sep 17 00:00:00 2001
> From: Chunfeng Yun <chunfeng.yun at mediatek.com>
> Date: Fri, 10 Jun 2016 15:20:20 +0800
> Subject: [PATCH 0/5] Add MediaTek USB3 DRD Driver
> 
> These patches introduce the MediaTek USB3 dual-role controller
> driver.
> 
> The driver can be configured as Dual-Role Device (DRD),
> Peripheral Only and Host Only (xHCI) modes. It works well
> with Mass Storage, RNDIS and g_zero on FS/HS and SS. And it is
> tested on MT8173 platform which only contains USB2.0 device IP,
> and on MT6290 platform which contains USB3.0 device IP.
> 
> Change in v3:
> 1. fix some typo error
> 2. rename mtu3.txt to mt8173-mtu3.txt
> 
> Change in v2:
> 1. modify binding docs according to suggestions
> 2. modify some comments and remove some dummy blank lines
> 3. fix memory leakage
> 
> Chunfeng Yun (5):
>   dt-bindings: mt8173-xhci: support host side of dual-role mode
>   dt-bindings: mt8173-mtu3: add devicetree bindings

Could you please help me to review the following two patches based on
v4.7-rc1:
  1- usb: xhci-mtk: make IPPC register optional
  2- usb: Add MediaTek USB3 DRD Driver

Thank you very much

>   usb: xhci-mtk: make IPPC register optional
>   usb: Add MediaTek USB3 DRD Driver
>   arm64: dts: mediatek: add USB3 DRD driver
> 
>  .../devicetree/bindings/usb/mt8173-mtu3.txt        |   85 ++
>  .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++
>  arch/arm64/boot/dts/mediatek/mt8173-evb.dts        |   46 +-
>  arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   28 +-
>  drivers/usb/Kconfig                                |    2 +
>  drivers/usb/Makefile                               |    1 +
>  drivers/usb/host/xhci-mtk.c                        |   32 +-
>  drivers/usb/mtu3/Kconfig                           |   47 ++
>  drivers/usb/mtu3/Makefile                          |   20 +
>  drivers/usb/mtu3/mtu3.h                            |  422 ++++++++++
>  drivers/usb/mtu3/mtu3_core.c                       |  879 ++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_dr.c                         |  348 ++++++++
>  drivers/usb/mtu3/mtu3_dr.h                         |  108 +++
>  drivers/usb/mtu3/mtu3_gadget.c                     |  731 ++++++++++++++++
>  drivers/usb/mtu3/mtu3_gadget_ep0.c                 |  879 ++++++++++++++++++++
>  drivers/usb/mtu3/mtu3_host.c                       |  294 +++++++
>  drivers/usb/mtu3/mtu3_hw_regs.h                    |  474 +++++++++++
>  drivers/usb/mtu3/mtu3_plat.c                       |  490 +++++++++++
>  drivers/usb/mtu3/mtu3_qmu.c                        |  599 +++++++++++++
>  drivers/usb/mtu3/mtu3_qmu.h                        |   43 +
>  20 files changed, 5562 insertions(+), 14 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
>  create mode 100644 drivers/usb/mtu3/Kconfig
>  create mode 100644 drivers/usb/mtu3/Makefile
>  create mode 100644 drivers/usb/mtu3/mtu3.h
>  create mode 100644 drivers/usb/mtu3/mtu3_core.c
>  create mode 100644 drivers/usb/mtu3/mtu3_dr.c
>  create mode 100644 drivers/usb/mtu3/mtu3_dr.h
>  create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
>  create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
>  create mode 100644 drivers/usb/mtu3/mtu3_host.c
>  create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
>  create mode 100644 drivers/usb/mtu3/mtu3_plat.c
>  create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
>  create mode 100644 drivers/usb/mtu3/mtu3_qmu.h
> 
> --
> 1.7.9.5
> 
> 




From felipe.balbi at linux.intel.com  Fri Jun 10 02:21:33 2016
From: felipe.balbi at linux.intel.com (Felipe Balbi)
Date: Fri, 10 Jun 2016 12:21:33 +0300
Subject: [PATCH v3,2/5] dt-bindings: mt8173-mtu3: add devicetree bindings
In-Reply-To: <1465543962-11678-3-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
 <1465543962-11678-3-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <878tyd761u.fsf@linux.intel.com>


Hi,

Chunfeng Yun <chunfeng.yun at mediatek.com> writes:
> + - enable-manual-drd : supports manual dual-role switch via sysfs; only used
> +	when receptacle is TYPE-A and also wants to support dual-role mode.

sorry, but this manual-drd can't be accepted. You should rely on a DT
property for this and you shouldn't expose it to sysfs. Use DebugFS for
that.


-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160610/ae48bff0/attachment.sig>

From felipe.balbi at linux.intel.com  Fri Jun 10 02:27:11 2016
From: felipe.balbi at linux.intel.com (Felipe Balbi)
Date: Fri, 10 Jun 2016 12:27:11 +0300
Subject: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <8760th75sg.fsf@linux.intel.com>


Hi,

Chunfeng Yun <chunfeng.yun at mediatek.com> writes:
> From 8fdc924cecf0ca14da6e8aba360501e53437e548 Mon Sep 17 00:00:00 2001
> From: Chunfeng Yun <chunfeng.yun at mediatek.com>
> Date: Fri, 10 Jun 2016 15:20:20 +0800
> Subject: [PATCH 0/5] Add MediaTek USB3 DRD Driver
>
> These patches introduce the MediaTek USB3 dual-role controller
> driver.
>
> The driver can be configured as Dual-Role Device (DRD),
> Peripheral Only and Host Only (xHCI) modes. It works well
> with Mass Storage, RNDIS and g_zero on FS/HS and SS. And it is
> tested on MT8173 platform which only contains USB2.0 device IP,
> and on MT6290 platform which contains USB3.0 device IP.
>
> Change in v3:
> 1. fix some typo error
> 2. rename mtu3.txt to mt8173-mtu3.txt
>
> Change in v2:
> 1. modify binding docs according to suggestions
> 2. modify some comments and remove some dummy blank lines
> 3. fix memory leakage

I don't see anything inherently wrong with this series. But keep in mind
I don't have HW to test. I can take the peripheral driver in a few days.

-- 
balbi
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160610/f4fc2408/attachment.sig>

From k.kozlowski at samsung.com  Fri Jun 10 03:11:17 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Fri, 10 Jun 2016 12:11:17 +0200
Subject: [PATCH v4 00/44] dma-mapping: Use unsigned long for dma_attrs
Message-ID: <1465553521-27303-1-git-send-email-k.kozlowski@samsung.com>

Hi,


This is fourth approach for replacing struct dma_attrs with unsigned
long.

The main patch (1/44) doing the change is split into many subpatches
for easier review (2-42).  They should be squashed together when
applying.


*Important:* Patchset is tested on my ARM platforms and *only* build
tested on allyesconfigs: ARM, ARM64, i386, x86_64 and powerpc.
Please kindly provide reviewes and tests for other platforms.

Rebased on next-20160607.

For easier testing the patchset is available here:
repo:   https://github.com/krzk/linux
branch: for-next/dma-attrs-const-v4


Changes since v3
================
1. Collect some acks.
2. Drop wrong patch 1/45 ("powerpc: dma-mapping: Don't hard-code
   the value of DMA_ATTR_WEAK_ORDERING").
3. Minor fix pointed out by Michael Ellerman.


Changes since v2
================
1. Follow Christoph Hellwig's comments (don't use BIT add
   documentation, remove dma_get_attr).


Rationale
=========
The dma-mapping core and the implementations do not change the
DMA attributes passed by pointer.  Thus the pointer can point to const
data.  However the attributes do not have to be a bitfield. Instead
unsigned long will do fine:

1. This is just simpler.  Both in terms of reading the code and setting
   attributes.  Instead of initializing local attributes on the stack
   and passing pointer to it to dma_set_attr(), just set the bits.

2. It brings safeness and checking for const correctness because the
   attributes are passed by value.


Best regards,
Krzysztof


Krzysztof Kozlowski (44):
  dma-mapping: Use unsigned long for dma_attrs
  alpha: dma-mapping: Use unsigned long for dma_attrs
  arc: dma-mapping: Use unsigned long for dma_attrs
  ARM: dma-mapping: Use unsigned long for dma_attrs
  arm64: dma-mapping: Use unsigned long for dma_attrs
  avr32: dma-mapping: Use unsigned long for dma_attrs
  blackfin: dma-mapping: Use unsigned long for dma_attrs
  c6x: dma-mapping: Use unsigned long for dma_attrs
  cris: dma-mapping: Use unsigned long for dma_attrs
  frv: dma-mapping: Use unsigned long for dma_attrs
  drm/exynos: dma-mapping: Use unsigned long for dma_attrs
  drm/mediatek: dma-mapping: Use unsigned long for dma_attrs
  drm/msm: dma-mapping: Use unsigned long for dma_attrs
  drm/nouveau: dma-mapping: Use unsigned long for dma_attrs
  drm/rockship: dma-mapping: Use unsigned long for dma_attrs
  infiniband: dma-mapping: Use unsigned long for dma_attrs
  iommu: dma-mapping: Use unsigned long for dma_attrs
  [media] dma-mapping: Use unsigned long for dma_attrs
  xen: dma-mapping: Use unsigned long for dma_attrs
  swiotlb: dma-mapping: Use unsigned long for dma_attrs
  powerpc: dma-mapping: Use unsigned long for dma_attrs
  video: dma-mapping: Use unsigned long for dma_attrs
  x86: dma-mapping: Use unsigned long for dma_attrs
  iommu: intel: dma-mapping: Use unsigned long for dma_attrs
  h8300: dma-mapping: Use unsigned long for dma_attrs
  hexagon: dma-mapping: Use unsigned long for dma_attrs
  ia64: dma-mapping: Use unsigned long for dma_attrs
  m68k: dma-mapping: Use unsigned long for dma_attrs
  metag: dma-mapping: Use unsigned long for dma_attrs
  microblaze: dma-mapping: Use unsigned long for dma_attrs
  mips: dma-mapping: Use unsigned long for dma_attrs
  mn10300: dma-mapping: Use unsigned long for dma_attrs
  nios2: dma-mapping: Use unsigned long for dma_attrs
  openrisc: dma-mapping: Use unsigned long for dma_attrs
  parisc: dma-mapping: Use unsigned long for dma_attrs
  misc: mic: dma-mapping: Use unsigned long for dma_attrs
  s390: dma-mapping: Use unsigned long for dma_attrs
  sh: dma-mapping: Use unsigned long for dma_attrs
  sparc: dma-mapping: Use unsigned long for dma_attrs
  tile: dma-mapping: Use unsigned long for dma_attrs
  unicore32: dma-mapping: Use unsigned long for dma_attrs
  xtensa: dma-mapping: Use unsigned long for dma_attrs
  dma-mapping: Remove dma_get_attr
  dma-mapping: Document the DMA attributes next to the declaration

 Documentation/DMA-API.txt                          |  33 +++---
 Documentation/DMA-attributes.txt                   |   2 +-
 arch/alpha/include/asm/dma-mapping.h               |   2 -
 arch/alpha/kernel/pci-noop.c                       |   2 +-
 arch/alpha/kernel/pci_iommu.c                      |  12 +-
 arch/arc/mm/dma.c                                  |  12 +-
 arch/arm/common/dmabounce.c                        |   4 +-
 arch/arm/include/asm/dma-mapping.h                 |  13 +--
 arch/arm/include/asm/xen/page-coherent.h           |  16 +--
 arch/arm/mm/dma-mapping.c                          | 121 ++++++++++---------
 arch/arm/xen/mm.c                                  |   8 +-
 arch/arm64/mm/dma-mapping.c                        |  67 +++++------
 arch/avr32/mm/dma-coherent.c                       |  12 +-
 arch/blackfin/kernel/dma-mapping.c                 |   8 +-
 arch/c6x/include/asm/dma-mapping.h                 |   4 +-
 arch/c6x/kernel/dma.c                              |   9 +-
 arch/c6x/mm/dma-coherent.c                         |   4 +-
 arch/cris/arch-v32/drivers/pci/dma.c               |   9 +-
 arch/frv/mb93090-mb00/pci-dma-nommu.c              |   8 +-
 arch/frv/mb93090-mb00/pci-dma.c                    |   9 +-
 arch/h8300/kernel/dma.c                            |   8 +-
 arch/hexagon/include/asm/dma-mapping.h             |   1 -
 arch/hexagon/kernel/dma.c                          |   8 +-
 arch/ia64/hp/common/sba_iommu.c                    |  22 ++--
 arch/ia64/include/asm/machvec.h                    |   1 -
 arch/ia64/kernel/pci-swiotlb.c                     |   4 +-
 arch/ia64/sn/pci/pci_dma.c                         |  22 ++--
 arch/m68k/kernel/dma.c                             |  12 +-
 arch/metag/kernel/dma.c                            |  16 +--
 arch/microblaze/include/asm/dma-mapping.h          |   1 -
 arch/microblaze/kernel/dma.c                       |  12 +-
 arch/mips/cavium-octeon/dma-octeon.c               |   8 +-
 arch/mips/loongson64/common/dma-swiotlb.c          |  10 +-
 arch/mips/mm/dma-default.c                         |  20 ++--
 arch/mips/netlogic/common/nlm-dma.c                |   4 +-
 arch/mn10300/mm/dma-alloc.c                        |   8 +-
 arch/nios2/mm/dma-mapping.c                        |  12 +-
 arch/openrisc/kernel/dma.c                         |  21 ++--
 arch/parisc/kernel/pci-dma.c                       |  18 +--
 arch/powerpc/include/asm/dma-mapping.h             |   7 +-
 arch/powerpc/include/asm/iommu.h                   |  10 +-
 arch/powerpc/kernel/dma-iommu.c                    |  12 +-
 arch/powerpc/kernel/dma.c                          |  18 +--
 arch/powerpc/kernel/ibmebus.c                      |  12 +-
 arch/powerpc/kernel/iommu.c                        |  12 +-
 arch/powerpc/kernel/vio.c                          |  12 +-
 arch/powerpc/platforms/cell/iommu.c                |  28 ++---
 arch/powerpc/platforms/pasemi/iommu.c              |   2 +-
 arch/powerpc/platforms/powernv/npu-dma.c           |   8 +-
 arch/powerpc/platforms/powernv/pci-ioda.c          |   4 +-
 arch/powerpc/platforms/powernv/pci.c               |   2 +-
 arch/powerpc/platforms/powernv/pci.h               |   2 +-
 arch/powerpc/platforms/ps3/system-bus.c            |  18 +--
 arch/powerpc/platforms/pseries/iommu.c             |   6 +-
 arch/powerpc/sysdev/dart_iommu.c                   |   2 +-
 arch/s390/include/asm/dma-mapping.h                |   1 -
 arch/s390/pci/pci_dma.c                            |  23 ++--
 arch/sh/include/asm/dma-mapping.h                  |   4 +-
 arch/sh/kernel/dma-nommu.c                         |   4 +-
 arch/sh/mm/consistent.c                            |   4 +-
 arch/sparc/kernel/iommu.c                          |  12 +-
 arch/sparc/kernel/ioport.c                         |  24 ++--
 arch/sparc/kernel/pci_sun4v.c                      |  12 +-
 arch/tile/kernel/pci-dma.c                         |  28 ++---
 arch/unicore32/mm/dma-swiotlb.c                    |   4 +-
 arch/x86/include/asm/dma-mapping.h                 |   5 +-
 arch/x86/include/asm/swiotlb.h                     |   4 +-
 arch/x86/include/asm/xen/page-coherent.h           |   9 +-
 arch/x86/kernel/amd_gart_64.c                      |  20 ++--
 arch/x86/kernel/pci-calgary_64.c                   |  14 +--
 arch/x86/kernel/pci-dma.c                          |   4 +-
 arch/x86/kernel/pci-nommu.c                        |   4 +-
 arch/x86/kernel/pci-swiotlb.c                      |   4 +-
 arch/x86/pci/sta2x11-fixup.c                       |   2 +-
 arch/x86/pci/vmd.c                                 |  16 +--
 arch/xtensa/kernel/pci-dma.c                       |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c          |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_gem.c            |  20 ++--
 drivers/gpu/drm/exynos/exynos_drm_gem.h            |   2 +-
 drivers/gpu/drm/mediatek/mtk_drm_gem.c             |  13 +--
 drivers/gpu/drm/mediatek/mtk_drm_gem.h             |   2 +-
 drivers/gpu/drm/msm/msm_drv.c                      |  13 +--
 .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c    |  13 +--
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c        |  17 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h        |   2 +-
 drivers/infiniband/core/umem.c                     |   7 +-
 drivers/iommu/amd_iommu.c                          |  12 +-
 drivers/iommu/dma-iommu.c                          |   8 +-
 drivers/iommu/intel-iommu.c                        |  12 +-
 drivers/media/platform/sti/bdisp/bdisp-hw.c        |  26 ++---
 drivers/media/v4l2-core/videobuf2-dma-contig.c     |  30 ++---
 drivers/media/v4l2-core/videobuf2-dma-sg.c         |  19 +--
 drivers/misc/mic/host/mic_boot.c                   |  20 ++--
 drivers/parisc/ccio-dma.c                          |  16 +--
 drivers/parisc/sba_iommu.c                         |  16 +--
 drivers/video/fbdev/omap2/omapfb/omapfb-main.c     |  12 +-
 drivers/video/fbdev/omap2/omapfb/omapfb.h          |   3 +-
 drivers/xen/swiotlb-xen.c                          |  14 +--
 include/linux/dma-attrs.h                          |  72 ------------
 include/linux/dma-iommu.h                          |   6 +-
 include/linux/dma-mapping.h                        | 128 ++++++++++++++-------
 include/linux/swiotlb.h                            |  10 +-
 include/media/videobuf2-dma-contig.h               |   7 +-
 include/rdma/ib_verbs.h                            |   8 +-
 include/xen/swiotlb-xen.h                          |  12 +-
 lib/dma-noop.c                                     |   9 +-
 lib/swiotlb.c                                      |  13 ++-
 108 files changed, 691 insertions(+), 793 deletions(-)
 delete mode 100644 include/linux/dma-attrs.h

-- 
1.9.1



From k.kozlowski at samsung.com  Fri Jun 10 03:11:29 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Fri, 10 Jun 2016 12:11:29 +0200
Subject: [PATCH v4 12/44] drm/mediatek: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1465553521-27303-1-git-send-email-k.kozlowski@samsung.com>
References: <1465553521-27303-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1465553521-27303-13-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_gem.c | 13 ++++++-------
 drivers/gpu/drm/mediatek/mtk_drm_gem.h |  2 +-
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
index fa2ec0cd00e8..7abc550ebc00 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -54,15 +54,14 @@ struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
 
 	obj = &mtk_gem->base;
 
-	init_dma_attrs(&mtk_gem->dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
+	mtk_gem->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (!alloc_kmap)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
+		mtk_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
 					  &mtk_gem->dma_addr, GFP_KERNEL,
-					  &mtk_gem->dma_attrs);
+					  mtk_gem->dma_attrs);
 	if (!mtk_gem->cookie) {
 		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
 		ret = -ENOMEM;
@@ -93,7 +92,7 @@ void mtk_drm_gem_free_object(struct drm_gem_object *obj)
 		drm_prime_gem_destroy(obj, mtk_gem->sg);
 	else
 		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
-			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
+			       mtk_gem->dma_addr, mtk_gem->dma_attrs);
 
 	/* release file pointer to gem object. */
 	drm_gem_object_release(obj);
@@ -173,7 +172,7 @@ static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
-			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
+			     mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs);
 	if (ret)
 		drm_gem_vm_close(vma);
 
@@ -224,7 +223,7 @@ struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
 
 	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
 				    mtk_gem->dma_addr, obj->size,
-				    &mtk_gem->dma_attrs);
+				    mtk_gem->dma_attrs);
 	if (ret) {
 		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 		kfree(sgt);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
index 3a2a5624a1cb..2752718fa5b2 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -35,7 +35,7 @@ struct mtk_drm_gem_obj {
 	void			*cookie;
 	void			*kvaddr;
 	dma_addr_t		dma_addr;
-	struct dma_attrs	dma_attrs;
+	unsigned long		dma_attrs;
 	struct sg_table		*sg;
 };
 
-- 
1.9.1



From john at phrozen.org  Fri Jun 10 04:28:06 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:06 +0200
Subject: [PATCH V2 09/11] net: mediatek: fix off by one in the TX ring
 allocation
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-10-git-send-email-john@phrozen.org>

The TX ring setup has an off by one error causing it to not utilise all
descriptors. This has the side effect that we need to reset the next
pointer at runtime to make it work. Fix the off by one and remove the
code fixing the ring at runtime.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 24714ab..6daf48b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -903,7 +903,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 		}
 		mtk_tx_unmap(eth->dev, tx_buf);
 
-		ring->last_free->txd2 = next_cpu;
 		ring->last_free = desc;
 		atomic_inc(&ring->free_count);
 
@@ -1011,7 +1010,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
 
 	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
 	ring->next_free = &ring->dma[0];
-	ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
+	ring->last_free = &ring->dma[MTK_DMA_SIZE - 1];
 	ring->thresh = MAX_SKB_FRAGS;
 
 	/* make sure that all changes to the dma ring are flushed before we
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:01 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:01 +0200
Subject: [PATCH V2 04/11] net: mediatek: dropped rx packets are not being
 counted properly
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-5-git-send-email-john@phrozen.org>

There are two places inside mtk_poll_rx where rx_dropped is not being
incremented properly. Fix this by adding the missing code to increment
the counter.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 4f22ee9..0610262 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -810,6 +810,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
 					  DMA_FROM_DEVICE);
 		if (unlikely(dma_mapping_error(&netdev->dev, dma_addr))) {
 			skb_free_frag(new_data);
+			netdev->stats.rx_dropped++;
 			goto release_desc;
 		}
 
@@ -817,6 +818,7 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget,
 		skb = build_skb(data, ring->frag_size);
 		if (unlikely(!skb)) {
 			put_page(virt_to_head_page(new_data));
+			netdev->stats.rx_dropped++;
 			goto release_desc;
 		}
 		skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:05 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:05 +0200
Subject: [PATCH V2 08/11] net: mediatek: increase watchdog_timeo
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-9-git-send-email-john@phrozen.org>

During stress testing, after reducing the threshold value, we have seen
TX timeouts that were caused by the watchdog_timeo value being too low.
Increase the value to 5 * HZ which is a value commonly used by many other
drivers.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8b289e1..24714ab 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1688,7 +1688,7 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 	mac->hw_stats->reg_offset = id * MTK_STAT_OFFSET;
 
 	SET_NETDEV_DEV(eth->netdev[id], eth->dev);
-	eth->netdev[id]->watchdog_timeo = HZ;
+	eth->netdev[id]->watchdog_timeo = 5 * HZ;
 	eth->netdev[id]->netdev_ops = &mtk_netdev_ops;
 	eth->netdev[id]->base_addr = (unsigned long)eth->base;
 	eth->netdev[id]->vlan_features = MTK_HW_FEATURES &
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:27:57 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:27:57 +0200
Subject: [PATCH V2 00/11] net: mediatek: various small fixes
Message-ID: <1465558088-15265-1-git-send-email-john@phrozen.org>

This series contains various small fixes that we stumbled across while
doing thorough testing and code level reviewing of the driver. The only
patch that sticks out is the first one, which addresses a DQL related
issue. The rest are just minor fixes.

Changes in V2:
* drop the DQL patch from the list until a better solution is found

John Crispin (11):
  net: mediatek: add missing return code check
  net: mediatek: fix missing free of scratch memory
  net: mediatek: invalid buffer lookup in mtk_tx_map()
  net: mediatek: dropped rx packets are not being counted properly
  net: mediatek: add next data pointer coherency protection
  net: mediatek: disable all interrupts during probe
  net: mediatek: fix threshold value
  net: mediatek: increase watchdog_timeo
  net: mediatek: fix off by one in the TX ring allocation
  net: mediatek: only wake the queue if it is stopped
  net: mediatek: remove superfluous queue wake up call

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   61 ++++++++++++++++++---------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    3 ++
 2 files changed, 45 insertions(+), 19 deletions(-)

-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:00 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:00 +0200
Subject: [PATCH V2 03/11] net: mediatek: invalid buffer lookup in mtk_tx_map()
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-4-git-send-email-john@phrozen.org>

The lookup of the tx_buffer in the error path inside mtk_tx_map() uses the
wrong descriptor pointer. This looks like a copy & paste error. Change the
code to use the correct pointer.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 8d79bbf..4f22ee9 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -655,7 +655,7 @@ static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev,
 
 err_dma:
 	do {
-		tx_buf = mtk_desc_to_tx_buf(ring, txd);
+		tx_buf = mtk_desc_to_tx_buf(ring, itxd);
 
 		/* unmap dma */
 		mtk_tx_unmap(&dev->dev, tx_buf);
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:04 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:04 +0200
Subject: [PATCH V2 07/11] net: mediatek: fix threshold value
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-8-git-send-email-john@phrozen.org>

The logic to calculate the threshold value for stopping the TX queue is
bad. Currently it will always use 1/2 of the rings size, which is way too
much. Set the threshold to MAX_SKB_FRAGS. This makes sure that the queue
is stopped when there is not enough room to accept an additional segment.?

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 93af4e3..8b289e1 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1012,8 +1012,7 @@ static int mtk_tx_alloc(struct mtk_eth *eth)
 	atomic_set(&ring->free_count, MTK_DMA_SIZE - 2);
 	ring->next_free = &ring->dma[0];
 	ring->last_free = &ring->dma[MTK_DMA_SIZE - 2];
-	ring->thresh = max((unsigned long)MTK_DMA_SIZE >> 2,
-			      MAX_SKB_FRAGS);
+	ring->thresh = MAX_SKB_FRAGS;
 
 	/* make sure that all changes to the dma ring are flushed before we
 	 * continue
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:27:58 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:27:58 +0200
Subject: [PATCH V2 01/11] net: mediatek: add missing return code check
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-2-git-send-email-john@phrozen.org>

The code fails to check if the scratch memory was properly allocated. Add
this check and return with an error if the allocation failed.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 45f8dbf..6780886 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -467,6 +467,9 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
 
 	eth->scratch_head = kcalloc(cnt, MTK_QDMA_PAGE_SIZE,
 				    GFP_KERNEL);
+	if (unlikely(!eth->scratch_head))
+		return -ENOMEM;
+
 	dma_addr = dma_map_single(eth->dev,
 				  eth->scratch_head, cnt * MTK_QDMA_PAGE_SIZE,
 				  DMA_FROM_DEVICE);
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:02 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:02 +0200
Subject: [PATCH V2 05/11] net: mediatek: add next data pointer coherency
 protection
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-6-git-send-email-john@phrozen.org>

The QDMA engine can fail to update the register pointing to the next TX
descriptor if this bit does not get set in the QDMA configuration register.
Not setting this bit can result in invalid values inside the TX rings
registers which will causes TX stalls.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 0610262..bfac376 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1261,7 +1261,7 @@ static int mtk_start_dma(struct mtk_eth *eth)
 	mtk_w32(eth,
 		MTK_TX_WB_DDONE | MTK_RX_DMA_EN | MTK_TX_DMA_EN |
 		MTK_RX_2B_OFFSET | MTK_DMA_SIZE_16DWORDS |
-		MTK_RX_BT_32DWORDS,
+		MTK_RX_BT_32DWORDS | MTK_NDP_CO_PRO,
 		MTK_QDMA_GLO_CFG);
 
 	return 0;
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 57f7e8a..a5eb7c6 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -91,6 +91,7 @@
 #define MTK_QDMA_GLO_CFG	0x1A04
 #define MTK_RX_2B_OFFSET	BIT(31)
 #define MTK_RX_BT_32DWORDS	(3 << 11)
+#define MTK_NDP_CO_PRO		BIT(10)
 #define MTK_TX_WB_DDONE		BIT(6)
 #define MTK_DMA_SIZE_16DWORDS	(2 << 4)
 #define MTK_RX_DMA_BUSY		BIT(3)
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:03 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:03 +0200
Subject: [PATCH V2 06/11] net: mediatek: disable all interrupts during probe
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-7-git-send-email-john@phrozen.org>

The current code only disables those IRQs that we will later use. To
ensure that we have a predefined state, we really want to disable all IRQs.
Change the code to disable all IRQs to achieve this.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index bfac376..93af4e3 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1375,7 +1375,7 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 
 	/* disable delay and normal interrupt */
 	mtk_w32(eth, 0, MTK_QDMA_DELAY_INT);
-	mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+	mtk_irq_disable(eth, ~0);
 	mtk_w32(eth, RST_GL_PSE, MTK_RST_GL);
 	mtk_w32(eth, 0, MTK_RST_GL);
 
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:27:59 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:27:59 +0200
Subject: [PATCH V2 02/11] net: mediatek: fix missing free of scratch memory
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-3-git-send-email-john@phrozen.org>

Scratch memory gets allocated in mtk_init_fq_dma() but the corresponding
code to free it is missing inside mtk_dma_free() causing a memory leak.
With this patch applied, we can run ifconfig up/down several thousand
times without any problems.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   18 +++++++++++++-----
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    2 ++
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 6780886..8d79bbf 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -453,14 +453,14 @@ static inline void mtk_rx_get_desc(struct mtk_rx_dma *rxd,
 /* the qdma core needs scratch memory to be setup */
 static int mtk_init_fq_dma(struct mtk_eth *eth)
 {
-	dma_addr_t phy_ring_head, phy_ring_tail;
+	dma_addr_t phy_ring_tail;
 	int cnt = MTK_DMA_SIZE;
 	dma_addr_t dma_addr;
 	int i;
 
 	eth->scratch_ring = dma_alloc_coherent(eth->dev,
 					       cnt * sizeof(struct mtk_tx_dma),
-					       &phy_ring_head,
+					       &eth->phy_scratch_ring,
 					       GFP_ATOMIC | __GFP_ZERO);
 	if (unlikely(!eth->scratch_ring))
 		return -ENOMEM;
@@ -477,19 +477,19 @@ static int mtk_init_fq_dma(struct mtk_eth *eth)
 		return -ENOMEM;
 
 	memset(eth->scratch_ring, 0x0, sizeof(struct mtk_tx_dma) * cnt);
-	phy_ring_tail = phy_ring_head +
+	phy_ring_tail = eth->phy_scratch_ring +
 			(sizeof(struct mtk_tx_dma) * (cnt - 1));
 
 	for (i = 0; i < cnt; i++) {
 		eth->scratch_ring[i].txd1 =
 					(dma_addr + (i * MTK_QDMA_PAGE_SIZE));
 		if (i < cnt - 1)
-			eth->scratch_ring[i].txd2 = (phy_ring_head +
+			eth->scratch_ring[i].txd2 = (eth->phy_scratch_ring +
 				((i + 1) * sizeof(struct mtk_tx_dma)));
 		eth->scratch_ring[i].txd3 = TX_DMA_SDL(MTK_QDMA_PAGE_SIZE);
 	}
 
-	mtk_w32(eth, phy_ring_head, MTK_QDMA_FQ_HEAD);
+	mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD);
 	mtk_w32(eth, phy_ring_tail, MTK_QDMA_FQ_TAIL);
 	mtk_w32(eth, (cnt << 16) | cnt, MTK_QDMA_FQ_CNT);
 	mtk_w32(eth, MTK_QDMA_PAGE_SIZE << 16, MTK_QDMA_FQ_BLEN);
@@ -1189,6 +1189,14 @@ static void mtk_dma_free(struct mtk_eth *eth)
 	for (i = 0; i < MTK_MAC_COUNT; i++)
 		if (eth->netdev[i])
 			netdev_reset_queue(eth->netdev[i]);
+	if (eth->scratch_ring) {
+		dma_free_coherent(eth->dev,
+				  MTK_DMA_SIZE * sizeof(struct mtk_tx_dma),
+				  eth->scratch_ring,
+				  eth->phy_scratch_ring);
+		eth->scratch_ring = NULL;
+		eth->phy_scratch_ring = 0;
+	}
 	mtk_tx_clean(eth);
 	mtk_rx_clean(eth);
 	kfree(eth->scratch_head);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index eed626d..57f7e8a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -357,6 +357,7 @@ struct mtk_rx_ring {
  * @rx_ring:		Pointer to the memore holding info about the RX ring
  * @rx_napi:		The NAPI struct
  * @scratch_ring:	Newer SoCs need memory for a second HW managed TX ring
+ * @phy_scratch_ring:	physical address of scratch_ring
  * @scratch_head:	The scratch memory that scratch_ring points to.
  * @clk_ethif:		The ethif clock
  * @clk_esw:		The switch clock
@@ -384,6 +385,7 @@ struct mtk_eth {
 	struct mtk_rx_ring		rx_ring;
 	struct napi_struct		rx_napi;
 	struct mtk_tx_dma		*scratch_ring;
+	dma_addr_t			phy_scratch_ring;
 	void				*scratch_head;
 	struct clk			*clk_ethif;
 	struct clk			*clk_esw;
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:28:08 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:08 +0200
Subject: [PATCH V2 11/11] net: mediatek: remove superfluous queue wake up call
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-12-git-send-email-john@phrozen.org>

The code checks if the queue should be stopped because we are below the
threshold of free descriptors only to check if it should be started again.
If we do end up in a state where we are at the threshold limit, it makes
more sense to just stop the queue and wait for the next IRQ to trigger the
TX housekeeping again. There is no rush in enqueuing the next packet, it
needs to wait for all the others in the queue to be dispatched first
anyway.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 40d3cfd..ebfca7d 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -764,12 +764,9 @@ static int mtk_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (mtk_tx_map(skb, dev, tx_num, ring, gso) < 0)
 		goto drop;
 
-	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) {
+	if (unlikely(atomic_read(&ring->free_count) <= ring->thresh))
 		mtk_stop_queue(eth);
-		if (unlikely(atomic_read(&ring->free_count) >
-			     ring->thresh))
-			mtk_wake_queue(eth);
-	}
+
 	spin_unlock_irqrestore(&eth->page_lock, flags);
 
 	return NETDEV_TX_OK;
-- 
1.7.10.4



From john at phrozen.org  Fri Jun 10 04:30:15 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:30:15 +0200
Subject: [PATCH V2 00/11] net: mediatek: various small fixes
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <ac8eae20-f8a5-6e06-43fd-c2826599b709@phrozen.org>



On 10/06/2016 13:27, John Crispin wrote:
> This series contains various small fixes that we stumbled across while
> doing thorough testing and code level reviewing of the driver. The only
> patch that sticks out is the first one, which addresses a DQL related
> issue. The rest are just minor fixes.
> 

Hi David,

i forgot to remove the last sentence here. can you live with that as it
wont end up in the git history or do you want me to send a V3 with this
line removed.

	John


> Changes in V2:
> * drop the DQL patch from the list until a better solution is found
> 
> John Crispin (11):
>   net: mediatek: add missing return code check
>   net: mediatek: fix missing free of scratch memory
>   net: mediatek: invalid buffer lookup in mtk_tx_map()
>   net: mediatek: dropped rx packets are not being counted properly
>   net: mediatek: add next data pointer coherency protection
>   net: mediatek: disable all interrupts during probe
>   net: mediatek: fix threshold value
>   net: mediatek: increase watchdog_timeo
>   net: mediatek: fix off by one in the TX ring allocation
>   net: mediatek: only wake the queue if it is stopped
>   net: mediatek: remove superfluous queue wake up call
> 
>  drivers/net/ethernet/mediatek/mtk_eth_soc.c |   61 ++++++++++++++++++---------
>  drivers/net/ethernet/mediatek/mtk_eth_soc.h |    3 ++
>  2 files changed, 45 insertions(+), 19 deletions(-)
> 


From john at phrozen.org  Fri Jun 10 04:28:07 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 13:28:07 +0200
Subject: [PATCH V2 10/11] net: mediatek: only wake the queue if it is stopped
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <1465558088-15265-11-git-send-email-john@phrozen.org>

The current code unconditionally wakes up the queue at the end of each
tx_poll action. Change the code to only wake up the queues if any of
them have actually been stopped before.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 6daf48b..40d3cfd 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -685,6 +685,20 @@ static inline int mtk_cal_txd_req(struct sk_buff *skb)
 	return nfrags;
 }
 
+static int mtk_queue_stopped(struct mtk_eth *eth)
+{
+	int i;
+
+	for (i = 0; i < MTK_MAC_COUNT; i++) {
+		if (!eth->netdev[i])
+			continue;
+		if (netif_queue_stopped(eth->netdev[i]))
+			return 1;
+	}
+
+	return 0;
+}
+
 static void mtk_wake_queue(struct mtk_eth *eth)
 {
 	int i;
@@ -929,7 +943,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 	if (!total)
 		return 0;
 
-	if (atomic_read(&ring->free_count) > ring->thresh)
+	if (mtk_queue_stopped(eth) &&
+	    (atomic_read(&ring->free_count) > ring->thresh))
 		mtk_wake_queue(eth);
 
 	return total;
-- 
1.7.10.4



From daniel at ffwll.ch  Fri Jun 10 08:24:12 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Fri, 10 Jun 2016 17:24:12 +0200
Subject: [PATCH v2 06/20] drm: i915: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <1465300095-16971-7-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-7-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160610152412.GL3363@phenom.ffwll.local>

On Tue, Jun 07, 2016 at 01:48:01PM +0200, Boris Brezillon wrote:
> For all outputs except dp_mst, we have a 1:1 relationship between
> connectors and encoders and the driver is relying on the atomic helpers:
> we can drop the custom ->best_encoder() implementation and let the core
> call drm_atomic_helper_best_encoder() for us.
> 
> Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>

You can also drop the best_encoder from intel_dp_mst, we only need the
atomic_best_encoder. The best_encoder there was needed to help out the
fbdev emulation. Care to respin?
-Daniel

> ---
>  drivers/gpu/drm/i915/intel_crt.c     | 1 -
>  drivers/gpu/drm/i915/intel_display.c | 8 --------
>  drivers/gpu/drm/i915/intel_dp.c      | 1 -
>  drivers/gpu/drm/i915/intel_drv.h     | 1 -
>  drivers/gpu/drm/i915/intel_dsi.c     | 1 -
>  drivers/gpu/drm/i915/intel_dvo.c     | 1 -
>  drivers/gpu/drm/i915/intel_hdmi.c    | 1 -
>  drivers/gpu/drm/i915/intel_lvds.c    | 1 -
>  drivers/gpu/drm/i915/intel_sdvo.c    | 1 -
>  drivers/gpu/drm/i915/intel_tv.c      | 1 -
>  10 files changed, 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
> index 3fbb6fc..bd0cd68 100644
> --- a/drivers/gpu/drm/i915/intel_crt.c
> +++ b/drivers/gpu/drm/i915/intel_crt.c
> @@ -753,7 +753,6 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
>  	.mode_valid = intel_crt_mode_valid,
>  	.get_modes = intel_crt_get_modes,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_encoder_funcs intel_crt_enc_funcs = {
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 2113f40..77026ce 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -16113,14 +16113,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
>  	intel_teardown_gmbus(dev);
>  }
>  
> -/*
> - * Return which encoder is currently attached for connector.
> - */
> -struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
> -{
> -	return &intel_attached_encoder(connector)->base;
> -}
> -
>  void intel_connector_attach_encoder(struct intel_connector *connector,
>  				    struct intel_encoder *encoder)
>  {
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index f192f58..21b2833 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -4935,7 +4935,6 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
>  	.get_modes = intel_dp_get_modes,
>  	.mode_valid = intel_dp_mode_valid,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_encoder_funcs intel_dp_enc_funcs = {
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index a28b4aa..79a4d6b 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1128,7 +1128,6 @@ struct intel_connector *intel_connector_alloc(void);
>  bool intel_connector_get_hw_state(struct intel_connector *connector);
>  void intel_connector_attach_encoder(struct intel_connector *connector,
>  				    struct intel_encoder *encoder);
> -struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
>  struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
>  					     struct drm_crtc *crtc);
>  enum pipe intel_get_pipe_from_connector(struct intel_connector *connector);
> diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
> index 366ad6c..ec51952 100644
> --- a/drivers/gpu/drm/i915/intel_dsi.c
> +++ b/drivers/gpu/drm/i915/intel_dsi.c
> @@ -1378,7 +1378,6 @@ static const struct drm_encoder_funcs intel_dsi_funcs = {
>  static const struct drm_connector_helper_funcs intel_dsi_connector_helper_funcs = {
>  	.get_modes = intel_dsi_get_modes,
>  	.mode_valid = intel_dsi_mode_valid,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_connector_funcs intel_dsi_connector_funcs = {
> diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
> index 286baec..34b7e3f 100644
> --- a/drivers/gpu/drm/i915/intel_dvo.c
> +++ b/drivers/gpu/drm/i915/intel_dvo.c
> @@ -351,7 +351,6 @@ static const struct drm_connector_funcs intel_dvo_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
>  	.mode_valid = intel_dvo_mode_valid,
>  	.get_modes = intel_dvo_get_modes,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
> diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
> index 2c3bd9c..aef4bc8 100644
> --- a/drivers/gpu/drm/i915/intel_hdmi.c
> +++ b/drivers/gpu/drm/i915/intel_hdmi.c
> @@ -2114,7 +2114,6 @@ static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
>  	.get_modes = intel_hdmi_get_modes,
>  	.mode_valid = intel_hdmi_mode_valid,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
> diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
> index bc53c0d..d4faa5a 100644
> --- a/drivers/gpu/drm/i915/intel_lvds.c
> +++ b/drivers/gpu/drm/i915/intel_lvds.c
> @@ -547,7 +547,6 @@ static int intel_lvds_set_property(struct drm_connector *connector,
>  static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
>  	.get_modes = intel_lvds_get_modes,
>  	.mode_valid = intel_lvds_mode_valid,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_connector_funcs intel_lvds_connector_funcs = {
> diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
> index 2128fae..5cd48ff 100644
> --- a/drivers/gpu/drm/i915/intel_sdvo.c
> +++ b/drivers/gpu/drm/i915/intel_sdvo.c
> @@ -2191,7 +2191,6 @@ static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
>  	.get_modes = intel_sdvo_get_modes,
>  	.mode_valid = intel_sdvo_mode_valid,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
> diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
> index 223129d..47fe241 100644
> --- a/drivers/gpu/drm/i915/intel_tv.c
> +++ b/drivers/gpu/drm/i915/intel_tv.c
> @@ -1512,7 +1512,6 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = {
>  static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = {
>  	.mode_valid = intel_tv_mode_valid,
>  	.get_modes = intel_tv_get_modes,
> -	.best_encoder = intel_best_encoder,
>  };
>  
>  static const struct drm_encoder_funcs intel_tv_enc_funcs = {
> -- 
> 2.7.4
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From daniel at ffwll.ch  Fri Jun 10 08:25:58 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Fri, 10 Jun 2016 17:25:58 +0200
Subject: [PATCH v2 00/20] drm/atomic: Provide default ->best_encoder()
 behavior
In-Reply-To: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
Message-ID: <20160610152558.GM3363@phenom.ffwll.local>

On Tue, Jun 07, 2016 at 01:47:55PM +0200, Boris Brezillon wrote:
> Hello,
> 
> This patch series aims at replacing all dummy ->best_encoder()
> implementations where we have a 1:1 relationship between encoders
> and connectors.
> The core already provides the drm_atomic_helper_best_encoder()
> function which is taking the first encoder attached to the
> connector (after making sure only one encoder was attached to the
> connector), but it's not automatically used, and drivers wanting
> to rely on this default behavior have to explicitly assign their
> ->best_encoder() hook to drm_atomic_helper_best_encoder().
> 
> The first patch fixes remaining places where
> drm_atomic_helper_best_encoder() should be called when ->best_encoder()
> is NULL, so that drivers using the atomic helpers can get rid of the
> explicit ->best_encoder assignment if they need to rely on the default
> drm_atomic_helper_best_encoder() implementation.
> 
> The following patches are killing all open coded ->best_encoder()
> implementations that could be replaced by
> drm_atomic_helper_best_encoder().
> 
> All modifications have been compile tested except for the changed on
> the intel driver.
> I've also tested on an atmel board, but I recommend waiting for DRM
> driver maintainers feedback before applying the associated changes.
> 
> Note that once patch 1 is applied, the other patches can be applied
> independently.

One comment on the i915 patch, all others should now be in drm-misc.
Thanks a lot for doing this.
-Daniel

> 
> Best Regards,
> 
> Boris
> 
> Changes since v1:
> - remove useless ->encoder backpointers in some implementations
> - documented the default behavior in the vtable doc
> - added R-b/A-b tags
> 
> Boris Brezillon (20):
>   drm/atomic: Fix remaining places where !funcs->best_encoder is valid
>   drm: arc: Rely on the default ->best_encoder() behavior
>   drm: atmel-hlcdc: Rely on the default ->best_encoder() behavior
>   drm: exynos: Rely on the default ->best_encoder() behavior
>   drm: fsl-dcu: Rely on the default ->best_encoder() behavior
>   drm: i915: Rely on the default ->best_encoder() behavior where
>     appropriate
>   drm: mediatek: Rely on the default ->best_encoder() behavior
>   drm: msm: Rely on the default ->best_encoder() behavior where
>     appropriate
>   drm: rcar-du: Rely on the default ->best_encoder() behavior
>   drm: rockchip: Rely on the default ->best_encoder() behavior
>   drm: sti: Rely on the default ->best_encoder() behavior
>   drm: sun4i: Rely on the default ->best_encoder() behavior
>   drm: tegra: Rely on the default ->best_encoder() behavior
>   drm: vc4: Rely on the default ->best_encoder() behavior
>   drm: virtgpu: Rely on the default ->best_encoder() behavior
>   drm: omap: Rely on the default ->best_encoder() behavior
>   drm/bridge: anx78xx: Rely on the default ->best_encoder() behavior
>   drm/bridge: ptn3460: Rely on the default ->best_encoder() behavior
>   drm/bridge: ps8622: Rely on the default ->best_encoder() behavior
>   drm/bridge: dw-hdmi: Use drm_atomic_helper_best_encoder()
> 
>  drivers/gpu/drm/arc/arcpgu_hdmi.c                  | 18 ------------------
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c   | 12 ------------
>  drivers/gpu/drm/bridge/analogix-anx78xx.c          |  8 --------
>  drivers/gpu/drm/bridge/dw-hdmi.c                   | 11 +----------
>  drivers/gpu/drm/bridge/nxp-ptn3460.c               |  8 --------
>  drivers/gpu/drm/bridge/parade-ps8622.c             | 10 ----------
>  drivers/gpu/drm/drm_atomic_helper.c                |  4 +++-
>  drivers/gpu/drm/drm_fb_helper.c                    | 13 ++++++++++++-
>  drivers/gpu/drm/exynos/exynos_drm_dpi.c            |  9 ---------
>  drivers/gpu/drm/exynos/exynos_drm_dsi.c            |  9 ---------
>  drivers/gpu/drm/exynos/exynos_drm_vidi.c           |  8 --------
>  drivers/gpu/drm/exynos/exynos_hdmi.c               |  8 --------
>  drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c          |  9 ---------
>  drivers/gpu/drm/i915/intel_crt.c                   |  1 -
>  drivers/gpu/drm/i915/intel_display.c               |  8 --------
>  drivers/gpu/drm/i915/intel_dp.c                    |  1 -
>  drivers/gpu/drm/i915/intel_drv.h                   |  1 -
>  drivers/gpu/drm/i915/intel_dsi.c                   |  1 -
>  drivers/gpu/drm/i915/intel_dvo.c                   |  1 -
>  drivers/gpu/drm/i915/intel_hdmi.c                  |  1 -
>  drivers/gpu/drm/i915/intel_lvds.c                  |  1 -
>  drivers/gpu/drm/i915/intel_sdvo.c                  |  1 -
>  drivers/gpu/drm/i915/intel_tv.c                    |  1 -
>  drivers/gpu/drm/mediatek/mtk_dsi.c                 |  9 ---------
>  drivers/gpu/drm/msm/edp/edp_connector.c            | 10 ----------
>  drivers/gpu/drm/msm/hdmi/hdmi_connector.c          |  8 --------
>  drivers/gpu/drm/msm/mdp/mdp4/mdp4_lvds_connector.c |  9 ---------
>  drivers/gpu/drm/omapdrm/omap_connector.c           | 10 ----------
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.c          | 12 ------------
>  drivers/gpu/drm/rcar-du/rcar_du_encoder.h          |  3 ---
>  drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c          |  1 -
>  drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c          |  1 -
>  drivers/gpu/drm/rcar-du/rcar_du_vgacon.c           |  3 ---
>  drivers/gpu/drm/rockchip/dw-mipi-dsi.c             |  9 ---------
>  drivers/gpu/drm/rockchip/inno_hdmi.c               |  9 ---------
>  drivers/gpu/drm/sti/sti_dvo.c                      | 10 ----------
>  drivers/gpu/drm/sti/sti_hda.c                      | 10 ----------
>  drivers/gpu/drm/sti/sti_hdmi.c                     | 10 ----------
>  drivers/gpu/drm/sun4i/sun4i_rgb.c                  | 10 ----------
>  drivers/gpu/drm/sun4i/sun4i_tv.c                   |  9 ---------
>  drivers/gpu/drm/tegra/drm.h                        |  2 --
>  drivers/gpu/drm/tegra/dsi.c                        |  1 -
>  drivers/gpu/drm/tegra/hdmi.c                       |  1 -
>  drivers/gpu/drm/tegra/output.c                     |  8 --------
>  drivers/gpu/drm/tegra/rgb.c                        |  1 -
>  drivers/gpu/drm/tegra/sor.c                        |  1 -
>  drivers/gpu/drm/vc4/vc4_dpi.c                      |  9 ---------
>  drivers/gpu/drm/vc4/vc4_hdmi.c                     |  9 ---------
>  drivers/gpu/drm/virtio/virtgpu_display.c           | 10 ----------
>  include/drm/drm_modeset_helper_vtables.h           | 10 ++++++++--
>  50 files changed, 24 insertions(+), 305 deletions(-)
> 
> -- 
> 2.7.4
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From daniel at ffwll.ch  Fri Jun 10 09:41:52 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Fri, 10 Jun 2016 18:41:52 +0200
Subject: [PATCH v2 06/20] drm: i915: Rely on the default ->best_encoder()
 behavior where appropriate
In-Reply-To: <20160610152412.GL3363@phenom.ffwll.local>
References: <1465300095-16971-1-git-send-email-boris.brezillon@free-electrons.com>
 <1465300095-16971-7-git-send-email-boris.brezillon@free-electrons.com>
 <20160610152412.GL3363@phenom.ffwll.local>
Message-ID: <20160610164152.GO3363@phenom.ffwll.local>

On Fri, Jun 10, 2016 at 05:24:12PM +0200, Daniel Vetter wrote:
> On Tue, Jun 07, 2016 at 01:48:01PM +0200, Boris Brezillon wrote:
> > For all outputs except dp_mst, we have a 1:1 relationship between
> > connectors and encoders and the driver is relying on the atomic helpers:
> > we can drop the custom ->best_encoder() implementation and let the core
> > call drm_atomic_helper_best_encoder() for us.
> > 
> > Signed-off-by: Boris Brezillon <boris.brezillon at free-electrons.com>
> 
> You can also drop the best_encoder from intel_dp_mst, we only need the
> atomic_best_encoder. The best_encoder there was needed to help out the
> fbdev emulation. Care to respin?

Boris pointed out on irc that this won't work for the fbdev stuff since
that has a WARN_ON if theres more than 1 possible encoder. Applied this
one here instead.

Thanks, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From davem at davemloft.net  Fri Jun 10 10:46:04 2016
From: davem at davemloft.net (David Miller)
Date: Fri, 10 Jun 2016 10:46:04 -0700 (PDT)
Subject: [PATCH V2 00/11] net: mediatek: various small fixes
In-Reply-To: <ac8eae20-f8a5-6e06-43fd-c2826599b709@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
 <ac8eae20-f8a5-6e06-43fd-c2826599b709@phrozen.org>
Message-ID: <20160610.104604.309213239246401664.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Fri, 10 Jun 2016 13:30:15 +0200

> 
> 
> On 10/06/2016 13:27, John Crispin wrote:
>> This series contains various small fixes that we stumbled across while
>> doing thorough testing and code level reviewing of the driver. The only
>> patch that sticks out is the first one, which addresses a DQL related
>> issue. The rest are just minor fixes.
>> 
> 
> Hi David,
> 
> i forgot to remove the last sentence here. can you live with that as it
> wont end up in the git history or do you want me to send a V3 with this
> line removed.

What do you mean it won't end up in the GIT history?  I always put this
introductory text into the merge commit for the patch series.

Now, I can remove it for you, which I will do.



From john at phrozen.org  Fri Jun 10 10:50:33 2016
From: john at phrozen.org (John Crispin)
Date: Fri, 10 Jun 2016 19:50:33 +0200
Subject: [PATCH V2 00/11] net: mediatek: various small fixes
In-Reply-To: <20160610.104604.309213239246401664.davem@davemloft.net>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
 <ac8eae20-f8a5-6e06-43fd-c2826599b709@phrozen.org>
 <20160610.104604.309213239246401664.davem@davemloft.net>
Message-ID: <a333787c-ce3a-049e-8144-d07d38b55cfc@phrozen.org>



On 10/06/2016 19:46, David Miller wrote:
> From: John Crispin <john at phrozen.org>
> Date: Fri, 10 Jun 2016 13:30:15 +0200
> 
>>
>>
>> On 10/06/2016 13:27, John Crispin wrote:
>>> This series contains various small fixes that we stumbled across while
>>> doing thorough testing and code level reviewing of the driver. The only
>>> patch that sticks out is the first one, which addresses a DQL related
>>> issue. The rest are just minor fixes.
>>>
>>
>> Hi David,
>>
>> i forgot to remove the last sentence here. can you live with that as it
>> wont end up in the git history or do you want me to send a V3 with this
>> line removed.
> 
> What do you mean it won't end up in the GIT history?  I always put this
> introductory text into the merge commit for the patch series.
> 
> Now, I can remove it for you, which I will do.
> 

cool, thanks a lot !

	John


From davem at davemloft.net  Fri Jun 10 23:30:16 2016
From: davem at davemloft.net (David Miller)
Date: Fri, 10 Jun 2016 23:30:16 -0700 (PDT)
Subject: [PATCH V2 00/11] net: mediatek: various small fixes
In-Reply-To: <1465558088-15265-1-git-send-email-john@phrozen.org>
References: <1465558088-15265-1-git-send-email-john@phrozen.org>
Message-ID: <20160610.233016.1590591569188856238.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Fri, 10 Jun 2016 13:27:57 +0200

> This series contains various small fixes that we stumbled across while
> doing thorough testing and code level reviewing of the driver. The only
> patch that sticks out is the first one, which addresses a DQL related
> issue. The rest are just minor fixes.
> 
> Changes in V2:
> * drop the DQL patch from the list until a better solution is found

Series applied, thanks.


From lkp at intel.com  Sat Jun 11 14:08:13 2016
From: lkp at intel.com (kbuild test robot)
Date: Sun, 12 Jun 2016 05:08:13 +0800
Subject: [PATCH v3,4/5] usb: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465543962-11678-5-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <201606120512.cHS58YAk%fengguang.wu@intel.com>

Hi,

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on v4.7-rc2 next-20160609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Chunfeng-Yun/usb-Add-MediaTek-USB3-DRD-Driver/20160610-153559
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: blackfin-allmodconfig (attached as .config)
compiler: bfin-uclinux-gcc (GCC) 4.6.3
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=blackfin 

All errors (new ones prefixed by >>):

   drivers/usb/mtu3/mtu3_dr.c: In function 'ssusb_mode_sysfs_switch':
>> drivers/usb/mtu3/mtu3_dr.c:271:3: error: implicit declaration of function 'pinctrl_select_state' [-Werror=implicit-function-declaration]
   cc1: some warnings being treated as errors

vim +/pinctrl_select_state +271 drivers/usb/mtu3/mtu3_dr.c

   265		struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
   266	
   267		if (!otg_sx->manual_drd_enabled)
   268			return;
   269	
   270		if (to_host)
 > 271			pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
   272		else
   273			pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
   274	}

---
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: 40649 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160612/bb680ca8/attachment-0001.obj>

From lkp at intel.com  Sat Jun 11 21:14:13 2016
From: lkp at intel.com (kbuild test robot)
Date: Sun, 12 Jun 2016 12:14:13 +0800
Subject: [PATCH v3,4/5] usb: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465543962-11678-5-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <201606121211.aP7WlrOd%fengguang.wu@intel.com>

Hi,

[auto build test ERROR on usb/usb-testing]
[also build test ERROR on v4.7-rc2 next-20160609]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Chunfeng-Yun/usb-Add-MediaTek-USB3-DRD-Driver/20160610-153559
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: i386-randconfig-x011-06120748 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All error/warnings (new ones prefixed by >>):

warning: (USB_MTU3) selects USB_XHCI_MTK which has unmet direct dependencies (USB_SUPPORT && USB && USB_XHCI_HCD && (ARCH_MEDIATEK || COMPILE_TEST))
   drivers/built-in.o: In function `mtu3_probe':
>> mtu3_plat.c:(.text+0x21471d): undefined reference to `extcon_get_edev_by_phandle'

---
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: 27652 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160612/d55e8007/attachment-0001.obj>

From ck.hu at mediatek.com  Sun Jun 12 22:56:06 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Mon, 13 Jun 2016 13:56:06 +0800
Subject: [RFC v3 4/5] drm/mediatek: add support for Mediatek SoC MT2701
In-Reply-To: <1465401825-43642-5-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
 <1465401825-43642-5-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465797366.27253.2.camel@mtksdaap41>

Hi, YT:

Some comments inline.

On Thu, 2016-06-09 at 00:03 +0800, YT Shen wrote:
> This patch add support for the Mediatek MT2701 DISP subsystem.
> There is only one OVL engine in MT2701.
> 
> Signed-off-by: YT Shen <yt.shen at mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    6 ++++
>  drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    6 ++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.c      |   42 +++++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |    8 +++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    2 ++
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   31 ++++++++++++++++++++
>  6 files changed, 95 insertions(+)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> index eb5c05e..1da0a71 100644
> --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> @@ -286,11 +286,17 @@ static int mtk_disp_ovl_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +static const struct mtk_ddp_comp_driver_data mt2701_ovl_driver_data = {
> +	.ovl = {0x0040, 1 << 12, 0}
> +};
> +
>  static const struct mtk_ddp_comp_driver_data mt8173_ovl_driver_data = {
>  	.ovl = {0x0f40, 0, 1 << 12}
>  };
>  
>  static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt2701-disp-ovl",
> +	  .data = &mt2701_ovl_driver_data},
>  	{ .compatible = "mediatek,mt8173-disp-ovl",
>  	  .data = &mt8173_ovl_driver_data},
>  	{},
> diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> index fb0db50..506a353 100644
> --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> @@ -225,11 +225,17 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +static const struct mtk_ddp_comp_driver_data mt2701_rdma_driver_data = {
> +	.rdma_fifo_pseudo_size = SZ_4K,
> +};
> +
>  static const struct mtk_ddp_comp_driver_data mt8173_rdma_driver_data = {
>  	.rdma_fifo_pseudo_size = SZ_8K,
>  };
>  
>  static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt2701-disp-rdma",
> +	  .data = &mt2701_rdma_driver_data},
>  	{ .compatible = "mediatek,mt8173-disp-rdma",
>  	  .data = &mt8173_rdma_driver_data},
>  	{},
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> index fa53806..7ab6986 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -32,6 +32,10 @@
>  #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN	0x0c8
>  #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
>  
> +#define DISP_REG_CONFIG_DISP_OVL_MOUT_EN	0x030
> +#define DISP_REG_CONFIG_OUT_SEL			0x04c
> +#define DISP_REG_CONFIG_DSI_SEL			0x050

Align the digital value.

> +
>  #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
>  #define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
>  #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
> @@ -54,6 +58,13 @@
>  #define MT8173_MUTEX_MOD_DISP_PWM1		BIT(24)
>  #define MT8173_MUTEX_MOD_DISP_OD		BIT(25)
>  
> +#define MT2701_MUTEX_MOD_DISP_OVL		BIT(3)
> +#define MT2701_MUTEX_MOD_DISP_WDMA		BIT(6)
> +#define MT2701_MUTEX_MOD_DISP_COLOR		BIT(7)
> +#define MT2701_MUTEX_MOD_DISP_BLS		BIT(9)
> +#define MT2701_MUTEX_MOD_DISP_RDMA0		BIT(10)
> +#define MT2701_MUTEX_MOD_DISP_RDMA1		BIT(12)
> +
>  #define MUTEX_SOF_SINGLE_MODE		0
>  #define MUTEX_SOF_DSI0			1
>  #define MUTEX_SOF_DSI1			2
> @@ -69,6 +80,10 @@
>  #define DPI0_SEL_IN_RDMA1		0x1
>  #define COLOR1_SEL_IN_OVL1		0x1
>  
> +#define OVL_MOUT_EN_RDMA		0x1
> +#define BLS_TO_DSI_RDMA1_TO_DPI1	0x8
> +#define DSI_SEL_IN_BLS			0x0
> +
>  struct mtk_disp_mutex {
>  	int id;
>  	bool claimed;
> @@ -82,6 +97,15 @@ struct mtk_ddp {
>  	const unsigned int		*mutex_mod;
>  };
>  
> +static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> +	[DDP_COMPONENT_BLS] = MT2701_MUTEX_MOD_DISP_BLS,
> +	[DDP_COMPONENT_COLOR0] = MT2701_MUTEX_MOD_DISP_COLOR,
> +	[DDP_COMPONENT_OVL0] = MT2701_MUTEX_MOD_DISP_OVL,
> +	[DDP_COMPONENT_RDMA0] = MT2701_MUTEX_MOD_DISP_RDMA0,
> +	[DDP_COMPONENT_RDMA1] = MT2701_MUTEX_MOD_DISP_RDMA1,
> +	[DDP_COMPONENT_WDMA0] = MT2701_MUTEX_MOD_DISP_WDMA,
> +};
> +
>  static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
>  	[DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
>  	[DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
> @@ -109,6 +133,9 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
>  	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
>  		*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
>  		value = OVL0_MOUT_EN_COLOR0;
> +	} else if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_RDMA0) {
> +		*addr = DISP_REG_CONFIG_DISP_OVL_MOUT_EN;
> +		value = OVL_MOUT_EN_RDMA;
>  	} else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
>  		*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
>  		value = OD_MOUT_EN_RDMA0;
> @@ -146,6 +173,9 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
>  	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
>  		*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
>  		value = COLOR1_SEL_IN_OVL1;
> +	} else if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
> +		*addr = DISP_REG_CONFIG_DSI_SEL;
> +		value = DSI_SEL_IN_BLS;
>  	} else {
>  		value = 0;
>  	}
> @@ -153,6 +183,15 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
>  	return value;
>  }
>  
> +static void mtk_ddp_sout_sel(void __iomem *config_regs,
> +			    enum mtk_ddp_comp_id cur, enum mtk_ddp_comp_id next)
> +{
> +	if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
> +		writel_relaxed(BLS_TO_DSI_RDMA1_TO_DPI1,
> +			       config_regs + DISP_REG_CONFIG_OUT_SEL);
> +	}

Redundant parentheses.

> +}
> +
>  void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
>  			      enum mtk_ddp_comp_id cur,
>  			      enum mtk_ddp_comp_id next)
> @@ -165,6 +204,8 @@ void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
>  		writel_relaxed(reg, config_regs + addr);
>  	}
>  
> +	mtk_ddp_sout_sel(config_regs, cur, next);
> +
>  	value = mtk_ddp_sel_in(cur, next, &addr);
>  	if (value) {
>  		reg = readl_relaxed(config_regs + addr) | value;
> @@ -362,6 +403,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
>  }
>  
>  static const struct of_device_id ddp_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt2701-disp-mutex", .data = mt2701_mutex_mod},
>  	{ .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
>  	{},
>  };
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> index 4b4e449..87e0436 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -102,6 +102,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
>  	[MTK_DISP_PWM] = "pwm",
>  	[MTK_DISP_MUTEX] = "mutex",
>  	[MTK_DISP_OD] = "od",
> +	[MTK_DISP_BLS] = "bls",
>  };
>  
>  struct mtk_ddp_comp_match {
> @@ -112,6 +113,7 @@ struct mtk_ddp_comp_match {
>  
>  static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
>  	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
> +	[DDP_COMPONENT_BLS]	= { MTK_DISP_BLS,	0, NULL },
>  	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
>  	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
>  	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
> @@ -130,11 +132,17 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
>  	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
>  };
>  
> +static const struct mtk_ddp_comp_driver_data mt2701_color_driver_data = {
> +	.color_offset = 0x0f00,
> +};
> +
>  static const struct mtk_ddp_comp_driver_data mt8173_color_driver_data = {
>  	.color_offset = 0x0c00,
>  };
>  
>  static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
> +	{ .compatible = "mediatek,mt2701-disp-color",
> +	  .data = &mt2701_color_driver_data},
>  	{ .compatible = "mediatek,mt8173-disp-color",
>  	  .data = &mt8173_color_driver_data},
>  	{},
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> index 53065c7..6e2d918 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -35,6 +35,7 @@ enum mtk_ddp_comp_type {
>  	MTK_DISP_PWM,
>  	MTK_DISP_MUTEX,
>  	MTK_DISP_OD,
> +	MTK_DISP_BLS,
>  	MTK_DDP_COMP_TYPE_MAX,
>  };
>  
> @@ -57,6 +58,7 @@ enum mtk_ddp_comp_id {
>  	DDP_COMPONENT_UFOE,
>  	DDP_COMPONENT_WDMA0,
>  	DDP_COMPONENT_WDMA1,
> +	DDP_COMPONENT_BLS,
>  	DDP_COMPONENT_ID_MAX,
>  };
>  
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index e9eb535..b4b66a7 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -109,6 +109,19 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
>  	.atomic_commit = mtk_atomic_commit,
>  };
>  
> +static const enum mtk_ddp_comp_id mt2701_mtk_ddp_main[] = {
> +	DDP_COMPONENT_OVL0,
> +	DDP_COMPONENT_RDMA0,
> +	DDP_COMPONENT_COLOR0,
> +	DDP_COMPONENT_BLS,
> +	DDP_COMPONENT_DSI0,
> +};
> +
> +static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
> +	DDP_COMPONENT_RDMA1,
> +	DDP_COMPONENT_DPI0,
> +};
> +
>  static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
>  	DDP_COMPONENT_OVL0,
>  	DDP_COMPONENT_COLOR0,
> @@ -128,6 +141,14 @@ static const enum mtk_ddp_comp_id mt8173_mtk_ddp_ext[] = {
>  	DDP_COMPONENT_DPI0,
>  };
>  
> +static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
> +	.main_path = mt2701_mtk_ddp_main,
> +	.main_len = ARRAY_SIZE(mt2701_mtk_ddp_main),
> +	.ext_path = mt2701_mtk_ddp_ext,
> +	.ext_len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
> +	.shadow_register = true,
> +};
> +
>  static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
>  	.main_path = mt8173_mtk_ddp_main,
>  	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
> @@ -332,18 +353,26 @@ static const struct component_master_ops mtk_drm_ops = {
>  };
>  
>  static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
> +	{ .compatible = "mediatek,mt2701-disp-ovl",   .data = (void *)MTK_DISP_OVL },
>  	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
> +	{ .compatible = "mediatek,mt2701-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
>  	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
> +	{ .compatible = "mediatek,mt2701-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
>  	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
> +	{ .compatible = "mediatek,mt2701-disp-color", .data = (void *)MTK_DISP_COLOR },
>  	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
>  	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
>  	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
>  	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
> +	{ .compatible = "mediatek,mt2701-dsi",	      .data = (void *)MTK_DSI },
>  	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
> +	{ .compatible = "mediatek,mt2701-dpi",	      .data = (void *)MTK_DPI },
>  	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
> +	{ .compatible = "mediatek,mt2701-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
>  	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
>  	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
>  	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
> +	{ .compatible = "mediatek,mt2701-disp-bls",   .data = (void *)MTK_DISP_BLS },
>  	{ }
>  };
>  
> @@ -515,6 +544,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
>  			 mtk_drm_sys_resume);
>  
>  static const struct of_device_id mtk_drm_of_ids[] = {
> +	{ .compatible = "mediatek,mt2701-mmsys",
> +	  .data = &mt2701_mmsys_driver_data},
>  	{ .compatible = "mediatek,mt8173-mmsys",
>  	  .data = &mt8173_mmsys_driver_data},
>  	{ }

Regards,
CK



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:10 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:10 +0800
Subject: [alsa-devel] [PATCH v3 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-8-git-send-email-garlic.tseng@mediatek.com>

Add supports for 16k (wideband BT) and add a general compatible
string "linux,bt-sco"

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 Documentation/devicetree/bindings/sound/bt-sco.txt | 2 +-
 sound/soc/codecs/bt-sco.c                          | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt
index 29b8e5d..641edf7 100644
--- a/Documentation/devicetree/bindings/sound/bt-sco.txt
+++ b/Documentation/devicetree/bindings/sound/bt-sco.txt
@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad1..101b384 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -31,14 +31,14 @@ static struct snd_soc_dai_driver bt_sco_dai = {
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	.capture = {
 		 .stream_name = "Capture",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 };
@@ -77,6 +77,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
 	{ .compatible = "delta,dfbmcs320", },
+	{ .compatible = "linux,bt-sco", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:07 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:07 +0800
Subject: [alsa-devel] [PATCH v3 4/9] ASoC: mediatek: add documents for mt2701
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-5-git-send-email-garlic.tseng@mediatek.com>

add mt2701-afe-pcm.txt and mt2701-cs42448.txt for mt2701

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   | 150 +++++++++++++++++++++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |  43 ++++++
 2 files changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 0000000..3e623a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm at 11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 0000000..0557444
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:03 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:03 +0800
Subject: [alsa-devel] [PATCH v3 0/9] ASoC: Mediatek: Add support for MT2701 SOC
Message-ID: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>

This patch adds basic support for Mediatek AFE for MT2701 SoC.
The patch is based on broonie tree "for-next" branch.

Change since v2:

* fix comments style.
* replace EXPORT_SYMBOL with EXPORT_SYMBOL_GPL
* reduce debug message
* move mutex lock for irq-allocated to AFE struct
* remove func mtk_simple_isr and call snd_pcm_period_elapsed directly
* mt2701 driver probe will fail if clock initial error
* fix dapm on/off switch control naming

Change since v1:

implement common private structure, fe dai and platform driver.
let mt8173 and mt2701 use the common structure.


Garlic Tseng (9):
  ASoC: mediatek: Refine mt8173 driver and change config option
  ASoC: mediatek: implement mediatek common structure
  ASoC: mediatek: let mt8173 use mediatek common structure
  ASoC: mediatek: add documents for mt2701
  ASoC: mediatek: add structure define and clock control for 2701
  ASoC: mediatek: add mt2701 platform driver implementation.
  ASoC: bt-sco: extend rate and add a general compatible string
  ASoC: mediatek: add BT implementation
  ASoC: mediatek: Add mt2701-cs42448 driver and config option.

 Documentation/devicetree/bindings/sound/bt-sco.txt |    2 +-
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   |  150 ++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |   43 +
 sound/soc/codecs/bt-sco.c                          |    5 +-
 sound/soc/mediatek/Kconfig                         |   44 +-
 sound/soc/mediatek/Makefile                        |   11 +-
 sound/soc/mediatek/common/Makefile                 |   17 +
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         |  379 +++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |   45 +
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |   90 ++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |   23 +
 sound/soc/mediatek/common/mtk-base-afe.h           |  104 ++
 sound/soc/mediatek/mt2701/Makefile                 |   19 +
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c  |  464 ++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h  |   38 +
 sound/soc/mediatek/mt2701/mt2701-afe-common.h      |  181 +++
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c         | 1698 ++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c         |  422 +++++
 sound/soc/mediatek/mt2701/mt2701-reg.h             |  186 +++
 sound/soc/mediatek/mt8173/Makefile                 |    7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      |   73 +
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     |  921 +++++------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |    4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |    2 +-
 sound/soc/mediatek/mtk-afe-common.h                |  101 --
 27 files changed, 4399 insertions(+), 634 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
 create mode 100644 sound/soc/mediatek/common/Makefile
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (51%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:11 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:11 +0800
Subject: [alsa-devel] [PATCH v3 8/9] ASoC: mediatek: add BT implementation
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-9-git-send-email-garlic.tseng@mediatek.com>

Add BT implementation for mt2701 platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 139 +++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index deb5f82..5fd2caa 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -377,6 +377,86 @@ static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+	afe_priv->mrg_enable[substream->stream] = 1;
+	return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_fs;
+	u32 val, msk;
+
+	stream_fs = params_rate(params);
+
+	if ((stream_fs != 8000) && (stream_fs != 16000)) {
+		dev_err(afe->dev, "%s() btmgr not supprt this stream_fs %d\n",
+			__func__, stream_fs);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_I2S_MODE_MASK,
+			   AFE_MRGIF_CON_I2S_MODE_32K);
+
+	val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+	      | AFE_DAIBT_CON0_MRG_USE;
+	msk = val;
+
+	if (stream_fs == 16000)
+		val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+			   AFE_DAIBT_CON0_DAIBT_EN,
+			   AFE_DAIBT_CON0_DAIBT_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_I2S_EN,
+			   AFE_MRGIF_CON_MRG_I2S_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_EN,
+			   AFE_MRGIF_CON_MRG_EN);
+	return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	/* if the other direction stream is not occupied */
+	if (!afe_priv->mrg_enable[!substream->stream]) {
+		regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+				   AFE_DAIBT_CON0_DAIBT_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_I2S_EN, 0);
+		regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+				   AUDIO_TOP_CON4_PDN_MRGIF,
+				   AUDIO_TOP_CON4_PDN_MRGIF);
+	}
+	afe_priv->mrg_enable[substream->stream] = 0;
+}
+
 static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
@@ -560,6 +640,13 @@ static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
 	.set_fmt	= mt2701_afe_i2s_set_fmt,
 };
 
+/* MRG BE DAIs */
+static struct snd_soc_dai_ops mt2701_btmrg_ops = {
+	.startup = mt2701_btmrg_startup,
+	.shutdown = mt2701_btmrg_shutdown,
+	.hw_params = mt2701_btmrg_hw_params,
+};
+
 static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
@@ -610,6 +697,36 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		},
 		.ops = &mt2701_single_memif_dai_ops,
 	},
+	{
+		.name = "PCM_BT_DL",
+		.id = MT2701_MEMIF_DLBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM_BT_UL",
+		.id = MT2701_MEMIF_ULBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "ULBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
 	/* BE DAIs */
 	{
 		.name = "I2S0",
@@ -709,6 +826,28 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		.ops = &mt2701_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
+	{
+		.name = "MRG BT",
+		.id = MT2701_IO_MRG,
+		.playback = {
+			.stream_name = "BT Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.stream_name = "BT Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_btmrg_ops,
+		.symmetric_rates = 1,
+	}
 };
 
 static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:05 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:05 +0800
Subject: [alsa-devel] [PATCH v3 2/9] ASoC: mediatek: implement mediatek common
 structure
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-3-git-send-email-garlic.tseng@mediatek.com>

implement mediatek basic structure, include common private data,
afe fe dai operator and afe platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         | 379 +++++++++++++++++++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |  45 +++
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |  90 +++++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |  23 ++
 sound/soc/mediatek/common/mtk-base-afe.h           | 104 ++++++
 5 files changed, 641 insertions(+)
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h

diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 0000000..d6b99d2
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,379 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+			   unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	/*enable agent*/
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       0 << memif->data->agent_disable_shift);
+
+	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+	/*
+	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+	 * smaller than period_size due to AFE's internal buffer.
+	 * This easily leads to overrun when avail_min is period_size.
+	 * One more period can hold the possible unread buffer.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		int periods_max = mtk_afe_hardware->periods_max;
+
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+						   SNDRV_PCM_HW_PARAM_PERIODS,
+						   3, periods_max);
+		if (ret < 0) {
+			dev_err(afe->dev, "hw_constraint_minmax failed\n");
+			return ret;
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	/*dynamic allocate irq to memif */
+	if (memif->irq_usage < 0) {
+		int irq_id = mtk_dynamic_irq_acquire(afe);
+
+		if (irq_id != afe->irqs_size) {
+			/* link */
+			memif->irq_usage = irq_id;
+		} else {
+			dev_err(afe->dev, "%s() error: no more asys irq\n",
+				__func__);
+			ret = -EBUSY;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int irq_id;
+
+	irq_id = memif->irq_usage;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       1 << memif->data->agent_disable_shift);
+
+	if (!memif->const_irq) {
+		mtk_dynamic_irq_release(afe, irq_id);
+		memif->irq_usage = -1;
+		memif->substream = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int msb_at_bit33 = 0;
+	int ret, fs = 0;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+			 memif->phys_buf_addr);
+	/* end */
+	mtk_regmap_write(afe->regmap,
+			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+			 memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set MSB to 33-bit */
+	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+			       1 << memif->data->msb_shift,
+			       msb_at_bit33 << memif->data->msb_shift);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+				       1 << memif->data->mono_shift,
+				       mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+			       memif->data->fs_maskbit << memif->data->fs_shift,
+			       fs << memif->data->fs_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+	unsigned int counter = runtime->period_size;
+	int fs;
+
+	dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (memif->data->enable_shift >= 0)
+			mtk_regmap_update_bits(afe->regmap,
+					       memif->data->enable_reg,
+					       1 << memif->data->enable_shift,
+					       1 << memif->data->enable_shift);
+
+		/* set irq counter */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+				       irq_data->irq_cnt_maskbit
+				       << irq_data->irq_cnt_shift,
+				       counter << irq_data->irq_cnt_shift);
+
+		/* set irq fs */
+		fs = afe->irq_fs(substream, runtime->rate);
+
+		if (fs < 0)
+			return -EINVAL;
+
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+				       irq_data->irq_fs_maskbit
+				       << irq_data->irq_fs_shift,
+				       fs << irq_data->irq_fs_shift);
+
+		/* enable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       1 << irq_data->irq_en_shift);
+
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+				       1 << memif->data->enable_shift, 0);
+		/* disable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       0 << irq_data->irq_en_shift);
+		/* and clear pending IRQ */
+		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+				 1 << irq_data->irq_clr_shift);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int hd_audio = 0;
+
+	/*set hd mode*/
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hd_audio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hd_audio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		hd_audio = 1;
+		break;
+	default:
+		dev_err(afe->dev, "%s() error: unsupported format %d\n",
+			__func__, substream->runtime->format);
+		break;
+	}
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+			       1 << memif->data->hd_shift,
+			       hd_audio << memif->data->hd_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+	.startup	= mtk_afe_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mtk_afe_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+	int i;
+
+	mutex_lock(&afe->irq_alloc_lock);
+	for (i = 0; i < afe->irqs_size; ++i) {
+		if (afe->irqs[i].irq_occupyed == 0) {
+			afe->irqs[i].irq_occupyed = 1;
+			mutex_unlock(&afe->irq_alloc_lock);
+			return i;
+		}
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+	mutex_lock(&afe->irq_alloc_lock);
+	if (irq_id >= 0 && irq_id < afe->irqs_size) {
+		afe->irqs[irq_id].irq_occupyed = 0;
+		mutex_unlock(&afe->irq_alloc_lock);
+		return 0;
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	afe->suspended = true;
+	afe->runtime_suspend(dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	afe->runtime_resume(dev);
+
+	if (!afe->reg_back_up)
+		dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+				 afe->reg_back_up[i]);
+
+	afe->suspended = false;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 0000000..28cb178
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 0000000..82d439c
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+			 (struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	const struct mtk_base_memif_data *memif_data = memif->data;
+	struct regmap *regmap = afe->regmap;
+	struct device *dev = afe->dev;
+	int reg_ofs_base = memif_data->reg_ofs_base;
+	int reg_ofs_cur = memif_data->reg_ofs_cur;
+	unsigned int hw_ptr = 0, hw_base = 0;
+	int ret, pcm_ptr_bytes;
+
+	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+	if (ret || hw_ptr == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+	if (ret || hw_base == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+	.ioctl = snd_pcm_lib_ioctl,
+	.pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	size_t size;
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+	size = afe->mtk_afe_hardware->buffer_bytes_max;
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+	.ops = &mtk_afe_pcm_ops,
+	.pcm_new = mtk_afe_pcm_new,
+	.pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 0000000..a973fc9
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 0000000..3a78f6f
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_reg;
+	int fs_shift;
+	int fs_maskbit;
+	int mono_reg;
+	int mono_shift;
+	int enable_reg;
+	int enable_shift;
+	int hd_reg;
+	int hd_shift;
+	int msb_reg;
+	int msb_shift;
+	int agent_disable_reg;
+	int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+	int id;
+	int irq_cnt_reg;
+	int irq_cnt_shift;
+	int irq_cnt_maskbit;
+	int irq_fs_reg;
+	int irq_fs_shift;
+	int irq_fs_maskbit;
+	int irq_en_reg;
+	int irq_en_shift;
+	int irq_clr_reg;
+	int irq_clr_shift;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+	void __iomem *base_addr;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+	unsigned int const *reg_back_up_list;
+	unsigned int *reg_back_up;
+	unsigned int reg_back_up_list_num;
+
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	bool suspended;
+
+	struct mtk_base_afe_memif *memif;
+	int memif_size;
+	struct mtk_base_afe_irq *irqs;
+	int irqs_size;
+
+	const struct snd_pcm_hardware *mtk_afe_hardware;
+	int (*memif_fs)(struct snd_pcm_substream *substream,
+			unsigned int rate);
+	int (*irq_fs)(struct snd_pcm_substream *substream,
+		      unsigned int rate);
+
+	void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mtk_base_memif_data *data;
+	int irq_usage;
+	int const_irq;
+};
+
+struct mtk_base_afe_irq {
+	const struct mtk_base_irq_data *irq_data;
+	int irq_occupyed;
+};
+
+#endif
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:12 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:12 +0800
Subject: [alsa-devel] [PATCH v3 9/9] ASoC: mediatek: Add mt2701-cs42448 driver
 and config option.
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-10-git-send-email-garlic.tseng@mediatek.com>

Add machine driver and config option for MT2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                 |  21 ++
 sound/soc/mediatek/Makefile                |   1 +
 sound/soc/mediatek/mt2701/Makefile         |  19 ++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c | 422 +++++++++++++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ff1a419..0ecb785 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -7,6 +7,27 @@ config SND_SOC_MEDIATEK
 	  Select Y if you have such device.
 	  If unsure select "N".
 
+config SND_SOC_MT2701
+	tristate "ASoC support for Mediatek MT2701 chip"
+	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+	tristate "SND_SOC_MT2701_CS42448"
+	depends on SND_SOC_MT2701
+	select SND_SOC_CS42XX8_I2C
+	select SND_SOC_BT_SCO
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  with the CS42448 codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 3d893be..5133215 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
 
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 0000000..31c3d04
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644
index 0000000..0e48cb9
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,422 @@
+/*
+ * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian at mediatek.com>
+ *              Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+	int i2s1_in_mux;
+	int i2s1_in_mux_gpio_sel_1;
+	int i2s1_in_mux_gpio_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+	"ADC_SDOUT2",
+	"ADC_SDOUT3",
+	"I2S_IN_1",
+	"I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+	SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+	return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+		return 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 1:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 2:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	case 3:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	default:
+		dev_warn(card->dev, "%s invalid setting\n", __func__);
+	}
+
+	priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget
+			mt2701_cs42448_asoc_card_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+	SND_SOC_DAPM_MIC("AMIC", NULL),
+	SND_SOC_DAPM_LINE("Tuner In", NULL),
+	SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+	SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+	SOC_DAPM_PIN_SWITCH("AMIC"),
+	SOC_DAPM_PIN_SWITCH("Tuner In"),
+	SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+	SOC_DAPM_PIN_SWITCH("AUX In"),
+	SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+		     mt2701_cs42448_i2sin1_mux_get,
+		     mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+		.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+		.list = mt2701_cs42448_sampling_rates,
+		.mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+	int err;
+
+	err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 &mt2701_cs42448_constraints_rates);
+	if (err < 0) {
+		dev_err(substream->pcm->card->dev,
+			"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+			__func__, err);
+		return err;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+	.startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	/* codec slave, mt2701 master */
+	unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			    | SND_SOC_DAIFMT_CONT;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+	unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+	unsigned int div_bck_to_lrck = 64;
+
+	mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+
+	/* mt2701 mclk */
+	snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+	/* mt2701 bck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+	/* mt2701 lrck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+	/* mt2701 master */
+	snd_soc_dai_set_fmt(cpu_dai, fmt);
+
+	/* codec mclk */
+	snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+	/* codec slave */
+	snd_soc_dai_set_fmt(codec_dai, fmt);
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_be_ops = {
+	.hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+	DAI_LINK_FE_MULTI_CH_OUT,
+	DAI_LINK_FE_PCM0_IN,
+	DAI_LINK_FE_PCM1_IN,
+	DAI_LINK_FE_BT_OUT,
+	DAI_LINK_FE_BT_IN,
+	DAI_LINK_BE_I2S0,
+	DAI_LINK_BE_I2S1,
+	DAI_LINK_BE_I2S2,
+	DAI_LINK_BE_I2S3,
+	DAI_LINK_BE_MRG_BT,
+};
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+	/* FE */
+	[DAI_LINK_FE_MULTI_CH_OUT] = {
+		.name = "mt2701-cs42448-multi-ch-out",
+		.stream_name = "mt2701-cs42448-multi-ch-out",
+		.cpu_dai_name = "PCM_multi",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_PCM0_IN] = {
+		.name = "mt2701-cs42448-pcm0",
+		.stream_name = "mt2701-cs42448-pcm0-data-UL",
+		.cpu_dai_name = "PCM0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_PCM1_IN] = {
+		.name = "mt2701-cs42448-pcm1-data-UL",
+		.stream_name = "mt2701-cs42448-pcm1-data-UL",
+		.cpu_dai_name = "PCM1",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_BT_OUT] = {
+		.name = "mt2701-cs42448-pcm-BT-out",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_DL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_BT_IN] = {
+		.name = "mt2701-cs42448-pcm-BT-in",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_UL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	/* BE */
+	[DAI_LINK_BE_I2S0] = {
+		.name = "mt2701-cs42448-I2S0",
+		.cpu_dai_name = "I2S0",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S1] = {
+		.name = "mt2701-cs42448-I2S1",
+		.cpu_dai_name = "I2S1",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S2] = {
+		.name = "mt2701-cs42448-I2S2",
+		.cpu_dai_name = "I2S2",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S3] = {
+		.name = "mt2701-cs42448-I2S3",
+		.cpu_dai_name = "I2S3",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_MRG_BT] = {
+		.name = "mt2701-cs42448-MRG-BT",
+		.cpu_dai_name = "MRG BT",
+		.no_pcm = 1,
+		.codec_dai_name = "bt-sco-pcm",
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+	.name = "mt2701-cs42448",
+	.owner = THIS_MODULE,
+	.dai_link = mt2701_cs42448_dai_links,
+	.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+	.controls = mt2701_cs42448_controls,
+	.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+	.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+	int ret;
+	int i;
+	struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+	struct mt2701_cs42448_private *priv =
+		devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+			     GFP_KERNEL);
+	struct device *dev = &pdev->dev;
+
+	if (!priv)
+		return -ENOMEM;
+
+	platform_node = of_parse_phandle(pdev->dev.of_node,
+					 "mediatek,platform", 0);
+	if (!platform_node) {
+		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].platform_name)
+			continue;
+		mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
+	}
+
+	card->dev = dev;
+
+	codec_node = of_parse_phandle(pdev->dev.of_node,
+				      "mediatek,audio-codec", 0);
+	if (!codec_node) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].codec_name)
+			continue;
+		mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
+	}
+
+	codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+					     "mediatek,audio-codec-bt-mrg", 0);
+	if (!codec_node_bt_mrg) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec-bt-mrg' missing or invalid\n");
+		return -EINVAL;
+	}
+	mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+							= codec_node_bt_mrg;
+
+	ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+		return ret;
+	}
+
+	priv->i2s1_in_mux_gpio_sel_1 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+					"i2s1_in_mux_gpio_sel_1");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+	}
+
+	priv->i2s1_in_mux_gpio_sel_2 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+					"i2s1_in_mux_gpio_sel_2");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+	}
+	snd_soc_card_set_drvdata(card, priv);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret)
+		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+			__func__, ret);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+	{.compatible = "mediatek,mt2701-cs42448-machine",},
+	{}
+};
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+	.driver = {
+		.name = "mt2701-cs42448",
+		   #ifdef CONFIG_OF
+		   .of_match_table = mt2701_cs42448_machine_dt_match,
+		   #endif
+	},
+	.probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:08 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:08 +0800
Subject: [alsa-devel] [PATCH v3 5/9] ASoC: mediatek: add structure define and
 clock control for 2701
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-6-git-send-email-garlic.tseng@mediatek.com>

add structure define and clock control function for 2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c | 464 ++++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h |  38 ++
 sound/soc/mediatek/mt2701/mt2701-afe-common.h     | 181 +++++++++
 sound/soc/mediatek/mt2701/mt2701-reg.h            | 186 +++++++++
 4 files changed, 869 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 0000000..b815ecc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+	[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+	[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+	[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+	[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+	[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+	[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+	[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+	[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+	[MT2701_AUD_APLL_SEL] = "top_apll_sel",
+	[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+	[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+	[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+	[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+	[MT2701_AUD_AUDPLL] = "top_audpll",
+	[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+	[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+	[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+	[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+	[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+	[MT2701_AUD_CLK_26M] = "clk_26m",
+	[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+	[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+	[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+	[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+	[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+	[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+	[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+	[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+	[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+	[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+	[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+	[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+	[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+	[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+	[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+	[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+	[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+	[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+	[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+	[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+	[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+	[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+	[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+	[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i = 0;
+
+	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(aud_clks[i])) {
+			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+				 __func__, aud_clks[i]);
+			return PTR_ERR(aud_clks[i]);
+		}
+	}
+
+	return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+	int ret = 0;
+
+	ret = mt2701_turn_on_a1sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_a2sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_afe_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		mt2701_turn_off_a2sys_clock(afe);
+		return ret;
+	}
+
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON,
+			   AFE_DAC_CON0_AFE_ON);
+	regmap_write(afe->regmap, PWR2_TOP_CON,
+		     PWR2_TOP_CON_INIT_VAL);
+	regmap_write(afe->regmap, PWR1_ASM_CON1,
+		     PWR1_ASM_CON1_INIT_VAL);
+	regmap_write(afe->regmap, PWR2_ASM_CON1,
+		     PWR2_ASM_CON1_INIT_VAL);
+
+	return 0;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+	mt2701_turn_off_afe_clock(afe);
+	mt2701_turn_off_a1sys_clock(afe);
+	mt2701_turn_off_a2sys_clock(afe);
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+			aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+			   MT2701_AUD_AUD_MUX1_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+		goto A1SYS_CLK_AUD_48K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A1SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A1SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+			   MT2701_AUD_AUD_MUX2_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+		goto A2SYS_CLK_AUD_44K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A2SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A2SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	/* enable INFRA_SYS */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto AFE_AUD_INFRA_ERR;
+	}
+
+	/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+			     afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUDINTBUS],
+			aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_M_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+	return 0;
+
+AFE_AUD_ASM_M_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK,
+			   AUDIO_TOP_CON0_PDN_APLL_CK);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS,
+			   AUDIO_TOP_CON4_PDN_A1SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS,
+			   AUDIO_TOP_CON4_PDN_A2SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+	int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+	int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+	/* Set MCLK Kx_SRC_SEL(domain) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_clk_id], ret);
+
+	if (domain == 0) {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+	} else {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+	}
+	clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+	/* Set MCLK Kx_SRC_DIV(divider) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_div_id], ret);
+
+	ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+			aud_clks[aud_src_div_id], mclk, ret);
+	clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 0000000..6497d57
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 0000000..c77166e
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,181 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE	98304000
+#define MT2701_PLL_DOMAIN_1_RATE	90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+	MT2701_I2S_1,
+	MT2701_I2S_2,
+	MT2701_I2S_3,
+	MT2701_I2S_4,
+	MT2701_I2S_NUM,
+};
+
+enum {
+	MT2701_MEMIF_DL1,
+	MT2701_MEMIF_DL2,
+	MT2701_MEMIF_DL3,
+	MT2701_MEMIF_DL4,
+	MT2701_MEMIF_DL5,
+	MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_UL1,
+	MT2701_MEMIF_UL2,
+	MT2701_MEMIF_UL3,
+	MT2701_MEMIF_UL4,
+	MT2701_MEMIF_UL5,
+	MT2701_MEMIF_DLBT,
+	MT2701_MEMIF_ULBT,
+	MT2701_MEMIF_NUM,
+	MT2701_IO_I2S = MT2701_MEMIF_NUM,
+	MT2701_IO_2ND_I2S,
+	MT2701_IO_3RD_I2S,
+	MT2701_IO_4TH_I2S,
+	MT2701_IO_5TH_I2S,
+	MT2701_IO_6TH_I2S,
+	MT2701_IO_MRG,
+};
+
+enum {
+	MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ2,
+	MT2701_IRQ_ASYS_IRQ3,
+	MT2701_IRQ_ASYS_END,
+};
+
+enum {
+	DIV_ID_MCLK_TO_BCK,
+	DIV_ID_BCK_TO_LRCK,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+	MT2701_AUD_INFRA_SYS_AUDIO,
+	MT2701_AUD_AUD_MUX1_SEL,
+	MT2701_AUD_AUD_MUX2_SEL,
+	MT2701_AUD_AUD_MUX1_DIV,
+	MT2701_AUD_AUD_MUX2_DIV,
+	MT2701_AUD_AUD_48K_TIMING,
+	MT2701_AUD_AUD_44K_TIMING,
+	MT2701_AUD_AUDPLL_MUX_SEL,
+	MT2701_AUD_APLL_SEL,
+	MT2701_AUD_AUD1PLL_98M,
+	MT2701_AUD_AUD2PLL_90M,
+	MT2701_AUD_HADDS2PLL_98M,
+	MT2701_AUD_HADDS2PLL_294M,
+	MT2701_AUD_AUDPLL,
+	MT2701_AUD_AUDPLL_D4,
+	MT2701_AUD_AUDPLL_D8,
+	MT2701_AUD_AUDPLL_D16,
+	MT2701_AUD_AUDPLL_D24,
+	MT2701_AUD_AUDINTBUS,
+	MT2701_AUD_CLK_26M,
+	MT2701_AUD_SYSPLL1_D4,
+	MT2701_AUD_AUD_K1_SRC_SEL,
+	MT2701_AUD_AUD_K2_SRC_SEL,
+	MT2701_AUD_AUD_K3_SRC_SEL,
+	MT2701_AUD_AUD_K4_SRC_SEL,
+	MT2701_AUD_AUD_K5_SRC_SEL,
+	MT2701_AUD_AUD_K6_SRC_SEL,
+	MT2701_AUD_AUD_K1_SRC_DIV,
+	MT2701_AUD_AUD_K2_SRC_DIV,
+	MT2701_AUD_AUD_K3_SRC_DIV,
+	MT2701_AUD_AUD_K4_SRC_DIV,
+	MT2701_AUD_AUD_K5_SRC_DIV,
+	MT2701_AUD_AUD_K6_SRC_DIV,
+	MT2701_AUD_AUD_I2S1_MCLK,
+	MT2701_AUD_AUD_I2S2_MCLK,
+	MT2701_AUD_AUD_I2S3_MCLK,
+	MT2701_AUD_AUD_I2S4_MCLK,
+	MT2701_AUD_AUD_I2S5_MCLK,
+	MT2701_AUD_AUD_I2S6_MCLK,
+	MT2701_AUD_ASM_M_SEL,
+	MT2701_AUD_ASM_H_SEL,
+	MT2701_AUD_UNIVPLL2_D4,
+	MT2701_AUD_UNIVPLL2_D2,
+	MT2701_AUD_SYSPLL_D5,
+	MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AUDIO_TOP_CON4,
+	AUDIO_TOP_CON5,
+	ASYS_TOP_CON,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN2,
+	AFE_CONN3,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN22,
+	AFE_DAC_CON0,
+	AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+	int i2s_ctrl_reg;
+	int i2s_pwn_shift;
+	int i2s_asrc_fs_shift;
+	int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+	I2S_OUT,
+	I2S_IN,
+	I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+	int dai_id;
+	int mclk_rate;
+	int div_mclk_to_bck;
+	int div_bck_to_lrck;
+	int format;
+	snd_pcm_format_t stream_fmt;
+	int on[I2S_DIR_NUM];
+	int occupied[I2S_DIR_NUM];
+	const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+	struct clk *clocks[MT2701_CLOCK_NUM];
+	struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+	bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 0000000..bb62b1c
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON	(0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE		(0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK	(0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN	(0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS	(0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS	(0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN	(0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF	(0x1 << 25)
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN		(0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN	(0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY	(0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN	(0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE		(0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL		(0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN		(0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN	(0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK	(0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K	(0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS		(0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)	((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH		(0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN		(0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN		(0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE	(0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)	((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL		(0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL		(0xffffffff)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL		(0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON		(0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK		(0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE	(0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE	(0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK			(0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK		(0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)		((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK	(0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES		(0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS			(0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)		((x) << 8)
+#define ASYS_I2S_CON_RESET		(0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN		(0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE	(0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE		(0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)	((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX		(0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:06 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:06 +0800
Subject: [alsa-devel] [PATCH v3 3/9] ASoC: mediatek: let mt8173 use mediatek
 common structure
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-4-git-send-email-garlic.tseng@mediatek.com>

Modify mt8173 driver implementation to use common structure.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                    |  11 +
 sound/soc/mediatek/Makefile                   |   3 +-
 sound/soc/mediatek/common/Makefile            |  17 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
 sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 679 +++++++++++---------------
 5 files changed, 327 insertions(+), 425 deletions(-)
 create mode 100644 sound/soc/mediatek/common/Makefile

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ae9f664..ff1a419 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,16 @@
+config SND_SOC_MEDIATEK
+	tristate "SND_SOC_MEDIATEK"
+	depends on ARCH_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
 	help
 	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
@@ -49,3 +59,4 @@ config SND_SOC_MT8173_RT5650_RT5676
 	  with the RT5650 and RT5676 codecs.
 	  Select Y if you have such device.
 	  If unsure select "N".
+
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 240dfc70..3d893be 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,3 @@
-# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
+
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 0000000..0fcd921
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
+
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
index 8f2936d..9a4837c 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -46,14 +46,13 @@ enum {
 };
 
 enum {
-	MT8173_AFE_IRQ_1,
-	MT8173_AFE_IRQ_2,
-	MT8173_AFE_IRQ_3,
-	MT8173_AFE_IRQ_4,
-	MT8173_AFE_IRQ_5,
-	MT8173_AFE_IRQ_6,
-	MT8173_AFE_IRQ_7,
-	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_DL1,
+	MT8173_AFE_IRQ_DL2,
+	MT8173_AFE_IRQ_VUL,
+	MT8173_AFE_IRQ_DAI,
+	MT8173_AFE_IRQ_AWB,
+	MT8173_AFE_IRQ_MOD_DAI,
+	MT8173_AFE_IRQ_HDMI,
 	MT8173_AFE_IRQ_NUM,
 };
 
@@ -71,31 +70,4 @@ enum {
 	MT8173_CLK_NUM
 };
 
-struct mt8173_afe;
-struct snd_pcm_substream;
-
-struct mt8173_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mt8173_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mt8173_afe_memif_data *data;
-	const struct mt8173_afe_irq_data *irqdata;
-};
-
 #endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 4fc52bc..8a643a3 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -25,6 +25,9 @@
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1		0x0548
 #define AFE_TDM_CON2		0x054c
 
-#define AFE_BASE_END_OFFSET	8
 #define AFE_IRQ_STATUS_BITS	0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -152,15 +154,8 @@ static const unsigned int mt8173_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mt8173_afe {
-	/* address for ioremap audio hardware register */
-	void __iomem *base_addr;
-	struct device *dev;
-	struct regmap *regmap;
-	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+struct mt8173_afe_private {
 	struct clk *clocks[MT8173_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
-	bool suspended;
 };
 
 static const struct snd_pcm_hardware mt8173_afe_hardware = {
@@ -174,53 +169,6 @@ static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mt8173_afe_pcm_pointer
-			 (struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int hw_ptr;
-	int ret;
-
-	ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-	if (ret || hw_ptr == 0) {
-		dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-		hw_ptr = memif->phys_buf_addr;
-	}
-
-	return bytes_to_frames(substream->runtime,
-			       hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
-	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mt8173_afe_pcm_pointer,
-};
-
-static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-	size_t size;
-	struct snd_card *card = rtd->card->snd_card;
-	struct snd_pcm *pcm = rtd->pcm;
-
-	size = mt8173_afe_hardware.buffer_bytes_max;
-
-	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-						     card->dev, size, size);
-}
-
-static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
-{
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
-	.ops = &mt8173_afe_pcm_ops,
-	.pcm_new = mt8173_afe_pcm_new,
-	.pcm_free = mt8173_afe_pcm_free,
-};
-
 struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
@@ -253,7 +201,7 @@ static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 	return -EINVAL;
 }
 
-static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
 	unsigned int val;
 	int fs = mt8173_afe_i2s_fs(rate);
@@ -281,7 +229,7 @@ static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,7 +244,7 @@ static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
 				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
@@ -319,7 +267,7 @@ static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
 				    struct clk *m_ck, unsigned int mck_rate,
 				    struct clk *b_ck, unsigned int bck_rate)
 {
@@ -343,7 +291,7 @@ static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
 					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
@@ -356,7 +304,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
 				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -370,7 +318,7 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
@@ -386,12 +334,13 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
 				 runtime->rate * 256, NULL, 0);
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
 				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
 	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
@@ -407,13 +356,14 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return 0;
 
-	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				    afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
@@ -421,13 +371,14 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
 				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return;
 
-	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				     afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				     afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
 static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
@@ -435,12 +386,14 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
 	unsigned int val;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
 				 runtime->rate * 128,
-				 afe->clocks[MT8173_CLK_I2S3_B],
+				 afe_priv->clocks[MT8173_CLK_I2S3_B],
 				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
@@ -496,7 +449,7 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -508,10 +461,14 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
 		regmap_write(afe->regmap, AFE_HDMI_CONN0,
-			     AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-			     AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-			     AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-			     AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+				 AFE_HDMI_CONN0_O30_I30 |
+				 AFE_HDMI_CONN0_O31_I31 |
+				 AFE_HDMI_CONN0_O32_I34 |
+				 AFE_HDMI_CONN0_O33_I35 |
+				 AFE_HDMI_CONN0_O34_I32 |
+				 AFE_HDMI_CONN0_O35_I33 |
+				 AFE_HDMI_CONN0_O36_I36 |
+				 AFE_HDMI_CONN0_O37_I37);
 
 		/* enable Out control */
 		regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -531,224 +488,46 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int ret;
-
-	memif->substream = substream;
-
-	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
-
-	/*
-	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-	 * smaller than period_size due to AFE's internal buffer.
-	 * This easily leads to overrun when avail_min is period_size.
-	 * One more period can hold the possible unread buffer.
-	 */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		ret = snd_pcm_hw_constraint_minmax(runtime,
-						   SNDRV_PCM_HW_PARAM_PERIODS,
-						   3,
-						   mt8173_afe_hardware.periods_max);
-		if (ret < 0) {
-			dev_err(afe->dev, "hw_constraint_minmax failed\n");
-			return ret;
-		}
-	}
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-	return ret;
-}
-
-static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				     struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-	memif->substream = NULL;
-}
-
-static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				     struct snd_pcm_hw_params *params,
-				     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int msb_at_bit33 = 0;
-	int ret;
-
-	dev_dbg(afe->dev,
-		"%s period = %u, rate= %u, channels=%u\n",
-		__func__, params_period_size(params), params_rate(params),
-		params_channels(params));
-
-	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-	if (ret < 0)
-		return ret;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int fs;
 
-	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-	memif->buffer_size = substream->runtime->dma_bytes;
-
-	/* start */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base, memif->phys_buf_addr);
-	/* end */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-		     memif->phys_buf_addr + memif->buffer_size - 1);
-
-	/* set MSB to 33-bit */
-	regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-			   1 << memif->data->msb_shift,
-			   msb_at_bit33 << memif->data->msb_shift);
-
-	/* set channel */
-	if (memif->data->mono_shift >= 0) {
-		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   1 << memif->data->mono_shift,
-				   mono << memif->data->mono_shift);
-	}
-
-	/* set rate */
-	if (memif->data->fs_shift < 0)
-		return 0;
 	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
 	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
-		unsigned int val;
-
-		switch (params_rate(params)) {
+		switch (rate) {
 		case 8000:
-			val = 0;
+			fs = 0;
 			break;
 		case 16000:
-			val = 1;
+			fs = 1;
 			break;
 		case 32000:
-			val = 2;
+			fs = 2;
 			break;
 		default:
 			return -EINVAL;
 		}
-
-		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-		else
-			regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-
 	} else {
-		int fs = mt8173_afe_i2s_fs(params_rate(params));
-
-		if (fs < 0)
-			return -EINVAL;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   0xf << memif->data->fs_shift,
-				   fs << memif->data->fs_shift);
+		fs = mt8173_afe_i2s_fs(rate);
 	}
-
-	return 0;
+	return fs;
 }
 
-static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-	return snd_pcm_lib_free_pages(substream);
+	return mt8173_afe_i2s_fs(rate);
 }
 
-static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int counter = runtime->period_size;
-
-	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift,
-					   1 << memif->data->enable_shift);
-
-		/* set irq counter */
-		regmap_update_bits(afe->regmap,
-				   memif->data->irq_reg_cnt,
-				   0x3ffff << memif->data->irq_cnt_shift,
-				   counter << memif->data->irq_cnt_shift);
-
-		/* set irq fs */
-		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mt8173_afe_i2s_fs(runtime->rate);
-
-			if (fs < 0)
-				return -EINVAL;
-
-			regmap_update_bits(afe->regmap,
-					   AFE_IRQ_MCU_CON,
-					   0xf << memif->data->irq_fs_shift,
-					   fs << memif->data->irq_fs_shift);
-		}
-		/* enable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   1 << memif->data->irq_en_shift);
-
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift, 0);
-		/* disable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   0 << memif->data->irq_en_shift);
-		/* and clear pending IRQ */
-		regmap_write(afe->regmap, AFE_IRQ_CLR,
-			     1 << memif->data->irq_clr_shift);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
-	.startup	= mt8173_afe_dais_startup,
-	.shutdown	= mt8173_afe_dais_shutdown,
-	.hw_params	= mt8173_afe_dais_hw_params,
-	.hw_free	= mt8173_afe_dais_hw_free,
-	.trigger	= mt8173_afe_dais_trigger,
-};
-
 /* BE DAIs */
 static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
 	.startup	= mt8173_afe_i2s_startup,
@@ -761,56 +540,15 @@ static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
 	.shutdown	= mt8173_afe_hdmi_shutdown,
 	.prepare	= mt8173_afe_hdmi_prepare,
 	.trigger	= mt8173_afe_hdmi_trigger,
-
 };
 
-static int mt8173_afe_runtime_suspend(struct device *dev);
-static int mt8173_afe_runtime_resume(struct device *dev);
-
-static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
-			    &afe->backup_regs[i]);
-
-	afe->suspended = true;
-	mt8173_afe_runtime_suspend(afe->dev);
-	return 0;
-}
-
-static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i = 0;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-		return 0;
-
-	mt8173_afe_runtime_resume(afe->dev);
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
-			     afe->backup_regs[i]);
-
-	afe->suspended = false;
-	return 0;
-}
-
 static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
 		.id = MT8173_AFE_MEMIF_DL1,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -818,12 +556,12 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
 		.id = MT8173_AFE_MEMIF_VUL,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -831,7 +569,7 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
@@ -860,8 +598,8 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	{
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -872,7 +610,7 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
@@ -978,105 +716,222 @@ static const char *aud_clks[MT8173_CLK_NUM] = {
 	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
 		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 21,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 1,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 0,
-		.irq_fs_shift = 4,
-		.irq_clr_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 0,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DL2",
 		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 22,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 2,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 2,
-		.irq_fs_shift = 16,
-		.irq_clr_shift = 2,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "VUL",
 		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 27,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 3,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 1,
-		.irq_fs_shift = 8,
-		.irq_clr_shift = 1,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 6,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DAI",
 		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON0,
 		.fs_shift = 24,
+		.fs_maskbit = 0x3,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 4,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 5,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "AWB",
 		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 24,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 6,
-		.irq_reg_cnt = AFE_IRQ_CNT7,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 14,
-		.irq_fs_shift = 24,
-		.irq_clr_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 3,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "MOD_DAI",
 		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 30,
+		.fs_maskbit = 0x3,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 30,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 7,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 4,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
 		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = -1,
 		.enable_shift = -1,
-		.irq_reg_cnt = AFE_IRQ_CNT5,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 8,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+	{
+		.id = MT8173_AFE_IRQ_DL1,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8173_AFE_IRQ_DL2,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 2,
+
+	}, {
+		.id = MT8173_AFE_IRQ_VUL,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_AWB,
+		.irq_cnt_reg = AFE_IRQ_CNT7,
 		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_HDMI,
+		.irq_cnt_reg = AFE_IRQ_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
 		.irq_en_shift = 12,
+		.irq_fs_reg = -1,
 		.irq_fs_shift = -1,
+		.irq_fs_maskbit = -1,
+		.irq_clr_reg = AFE_IRQ_CLR,
 		.irq_clr_shift = 4,
-		.msb_shift = 8,
 	},
 };
 
@@ -1090,7 +945,7 @@ static const struct regmap_config mt8173_afe_regmap_config = {
 
 static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mt8173_afe *afe = dev_id;
+	struct mtk_base_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1102,9 +957,15 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 	}
 
 	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
-		struct mt8173_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *irq;
 
-		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+		if (memif->irq_usage < 0)
+			continue;
+
+		irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
 			continue;
 
 		snd_pcm_period_elapsed(memif->substream);
@@ -1112,14 +973,16 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 
 err_irq:
 	/* clear irq */
-	regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+	regmap_write(afe->regmap, AFE_IRQ_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
 
 	return IRQ_HANDLED;
 }
 
 static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1127,44 +990,46 @@ static int mt8173_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
 static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1181,35 +1046,37 @@ static int mt8173_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
+
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
 	size_t i;
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-		afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-		if (IS_ERR(afe->clocks[i])) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(afe_priv->clocks[i])) {
 			dev_err(afe->dev, "%s devm_clk_get %s fail\n",
 				__func__, aud_clks[i]);
-			return PTR_ERR(afe->clocks[i]);
+			return PTR_ERR(afe_priv->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
@@ -1217,7 +1084,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mt8173_afe *afe;
+	struct mtk_base_afe *afe;
+	struct mt8173_afe_private *afe_priv;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1228,6 +1096,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 	if (!afe)
 		return -ENOMEM;
 
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe_priv)
+		return -ENOMEM;
+
 	afe->dev = &pdev->dev;
 
 	irq_id = platform_get_irq(pdev, 0);
@@ -1259,8 +1133,30 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
+	/* memif % irq initialize*/
+	afe->memif_size = MT8173_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8173_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++) {
 		afe->memif[i].data = &memif_data[i];
+		afe->irqs[i].irq_data = &irq_data[i];
+		afe->irqs[i].irq_occupyed = true;
+		afe->memif[i].irq_usage = i;
+		afe->memif[i].const_irq = 1;
+	}
+
+	afe->mtk_afe_hardware = &mt8173_afe_hardware;
+	afe->memif_fs = mt8173_memif_fs;
+	afe->irq_fs = mt8173_irq_fs;
 
 	platform_set_drvdata(pdev, afe);
 
@@ -1271,7 +1167,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
+	afe->reg_back_up_list = mt8173_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+	afe->runtime_resume = mt8173_afe_runtime_resume;
+	afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:09 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:09 +0800
Subject: [alsa-devel] [PATCH v3 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-7-git-send-email-garlic.tseng@mediatek.com>

Add mt2701 platform driver implementation for playback and capture.
The implement follow DAPM structure (memory interface as FE and I2S
as BE).
Because of the hardware design, i2s out required to be enabled when
we need to enable i2s in. This patch includes the implementation.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 1559 ++++++++++++++++++++++++++++
 1 file changed, 1559 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644
index 0000000..deb5f82
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1559 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS	0xff
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+		| SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+		   | SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 1024 * 256,
+	.periods_min = 4,
+	.periods_max = 1024,
+	.buffer_bytes_max = 1024 * 1024 * 16,
+	.fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+	unsigned int rate;
+	unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+	{ .rate = 8000, .regvalue = 0 },
+	{ .rate = 12000, .regvalue = 1 },
+	{ .rate = 16000, .regvalue = 2 },
+	{ .rate = 24000, .regvalue = 3 },
+	{ .rate = 32000, .regvalue = 4 },
+	{ .rate = 48000, .regvalue = 5 },
+	{ .rate = 96000, .regvalue = 6 },
+	{ .rate = 192000, .regvalue = 7 },
+	{ .rate = 384000, .regvalue = 8 },
+	{ .rate = 7350, .regvalue = 16 },
+	{ .rate = 11025, .regvalue = 17 },
+	{ .rate = 14700, .regvalue = 18 },
+	{ .rate = 22050, .regvalue = 19 },
+	{ .rate = 29400, .regvalue = 20 },
+	{ .rate = 44100, .regvalue = 21 },
+	{ .rate = 88200, .regvalue = 22 },
+	{ .rate = 176400, .regvalue = 23 },
+	{ .rate = 352800, .regvalue = 24 },
+};
+
+int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+	int val = num - MT2701_IO_I2S;
+
+	if (val < 0 || val >= MT2701_I2S_NUM) {
+		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+			__func__, num, val);
+		return -EINVAL;
+	}
+	return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+		if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+			return mt2701_afe_i2s_rates[i].regvalue;
+
+	return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+	int ret = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* enable mclk */
+	ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+	if (ret)
+		dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+			i2s_num);
+
+	return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai,
+					int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	int stream_dir = substream->stream;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert)	{
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	i2s_path->on[stream_dir]--;
+	if (i2s_path->on[stream_dir] < 0) {
+		dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+			 i2s_path->on[stream_dir], stream_dir);
+		i2s_path->on[stream_dir] = 0;
+	}
+	if (i2s_path->on[stream_dir])
+		return 0;
+
+	/* disable i2s */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   1 << i2s_data->i2s_pwn_shift);
+	return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+	if (i2s_num < 0)
+		return;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (i2s_path->occupied[substream->stream])
+		i2s_path->occupied[substream->stream] = 0;
+	else
+		goto I2S_UNSTART;
+
+	mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+	/* need to disable i2s-out path when disable i2s-in */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+	/* disable mclk */
+	clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+					  struct snd_soc_dai *dai,
+					  int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	int reg, fs, w_len = 1;
+	int stream_dir = substream->stream;
+	unsigned int mask = 0, val = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert) {
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	/* no need to enable if already done */
+	i2s_path->on[stream_dir]++;
+
+	if (i2s_path->on[stream_dir] != 1)
+		return 0;
+
+	fs = mt2701_afe_i2s_fs(runtime->rate);
+
+	if (i2s_path->div_bck_to_lrck == 32)
+		w_len = 0;
+	else if (i2s_path->div_bck_to_lrck == 64)
+		w_len = 1;
+	else
+		dev_warn(dai->dev, "%s() bad bit count %d\n", __func__,
+			 afe_priv->i2s_path[i2s_num].div_bck_to_lrck);
+
+	mask = ASYS_I2S_CON_FS |
+	       ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
+	       ASYS_I2S_CON_I2S_MODE |
+	       ASYS_I2S_CON_WIDE_MODE;
+
+	val = ASYS_I2S_CON_FS_SET(fs) |
+	      ASYS_I2S_CON_I2S_MODE |
+	      ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+	if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+		mask |= ASYS_I2S_IN_PHASE_FIX;
+		val |= ASYS_I2S_IN_PHASE_FIX;
+	}
+
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = ASMO_TIMING_CON1;
+	else
+		reg = ASMI_TIMING_CON1;
+
+	regmap_update_bits(afe->regmap, reg,
+			   i2s_data->i2s_asrc_fs_mask
+			   << i2s_data->i2s_asrc_fs_shift,
+			   fs << i2s_data->i2s_asrc_fs_shift);
+
+	/* enable i2s */
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   0 << i2s_data->i2s_pwn_shift);
+
+	/* reset i2s hw status before enable */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, 0);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+	return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	int clk_domain;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int mclk_rate;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+	mclk_rate = i2s_path->mclk_rate;
+
+	if (i2s_path->occupied[substream->stream])
+		return -EBUSY;
+	i2s_path->occupied[substream->stream] = 1;
+
+	if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+		clk_domain = 0;
+	} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+		clk_domain = 1;
+	} else {
+		dev_err(dai->dev, "%s() bad mclk rate %d\n",
+			__func__, mclk_rate);
+		return -EINVAL;
+	}
+	mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	} else {
+		/* need to enable i2s-out path when enable i2s-in */
+		/* prepare for another direction "out" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 1);
+		/* prepare for "in" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	}
+
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				     unsigned int freq, int dir)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* mclk */
+	if (dir == SND_SOC_CLOCK_IN) {
+		dev_warn(dai->dev,
+			 "%s() warning: mt2701 doesn't support mclk input\n",
+			__func__);
+		return -EINVAL;
+	}
+	afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+				     int div)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	switch (div_id) {
+	case DIV_ID_MCLK_TO_BCK:
+		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
+		break;
+	case DIV_ID_BCK_TO_LRCK:
+		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	afe_priv->i2s_path[i2s_num].format = fmt;
+	return 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif_tmp;
+
+	/* can't run single DL & DLM at the same time */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+		if (memif_tmp->substream) {
+			dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+				 __func__, stream_dir, memif_num);
+			return -EBUSY;
+		}
+	}
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+
+	/* single DL use PAIR_INTERLEAVE */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_update_bits(afe->regmap,
+				   AFE_MEMIF_PBUF_SIZE,
+				   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+				   AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+	}
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp;
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_tmp = &afe->memif[i];
+		if (memif_tmp->substream)
+			return -EBUSY;
+	}
+
+	/* enable agent for all signal DL (due to hw design) */
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   0 << memif_data->agent_disable_shift);
+	}
+
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   1 << memif_data->agent_disable_shift);
+	}
+	return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int channels = params_channels(params);
+
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+			   AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+				 int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift,
+				   1 << memif_tmp->data->enable_shift);
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift, 0);
+
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int fs;
+
+	if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT)
+		fs = mt2701_afe_i2s_fs(rate);
+	else
+		fs = (rate == 16000 ? 1 : 0);
+	return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+	return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+	.startup	= mt2701_simple_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mt2701_simple_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+	.startup	= mt2701_dlm_fe_startup,
+	.shutdown	= mt2701_dlm_fe_shutdown,
+	.hw_params	= mt2701_dlm_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+	.startup	= mt2701_afe_i2s_startup,
+	.shutdown	= mt2701_afe_i2s_shutdown,
+	.prepare	= mt2701_afe_i2s_prepare,
+	.set_sysclk	= mt2701_afe_i2s_set_sysclk,
+	.set_clkdiv	= mt2701_afe_i2s_set_clkdiv,
+	.set_fmt	= mt2701_afe_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+	/* FE DAIs: memory intefaces to CPU */
+	{
+		.name = "PCM_multi",
+		.id = MT2701_MEMIF_DLM,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLM",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_dlm_memif_dai_ops,
+	},
+	{
+		.name = "PCM0",
+		.id = MT2701_MEMIF_UL1,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM1",
+		.id = MT2701_MEMIF_UL2,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL2",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	/* BE DAIs */
+	{
+		.name = "I2S0",
+		.id = MT2701_IO_I2S,
+		.playback = {
+			.stream_name = "I2S0 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.capture = {
+			.stream_name = "I2S0 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S1",
+		.id = MT2701_IO_2ND_I2S,
+		.playback = {
+			.stream_name = "I2S1 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S1 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S2",
+		.id = MT2701_IO_3RD_I2S,
+		.playback = {
+			.stream_name = "I2S2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S3",
+		.id = MT2701_IO_4TH_I2S,
+		.playback = {
+			.stream_name = "I2S3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S3 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o23_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+	SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S0 Out Switch",
+				    ASYS_I2SO1_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S1 Out Switch",
+				    ASYS_I2SO2_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S2 Out Switch",
+				    PWR2_TOP_CON, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S3 Out Switch",
+				    PWR2_TOP_CON, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S4 Out Switch",
+				    PWR2_TOP_CON, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1,
+				    1),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+			   ARRAY_SIZE(mt2701_afe_i02_mix)),
+	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+			   ARRAY_SIZE(mt2701_afe_o00_mix)),
+	SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+			   ARRAY_SIZE(mt2701_afe_o01_mix)),
+	SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+			   ARRAY_SIZE(mt2701_afe_o02_mix)),
+	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+			   ARRAY_SIZE(mt2701_afe_o03_mix)),
+	SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+			   ARRAY_SIZE(mt2701_afe_o14_mix)),
+	SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+			   ARRAY_SIZE(mt2701_afe_o15_mix)),
+	SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+			   ARRAY_SIZE(mt2701_afe_o16_mix)),
+	SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+			   ARRAY_SIZE(mt2701_afe_o17_mix)),
+	SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+			   ARRAY_SIZE(mt2701_afe_o18_mix)),
+	SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+			   ARRAY_SIZE(mt2701_afe_o19_mix)),
+	SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+			   ARRAY_SIZE(mt2701_afe_o20_mix)),
+	SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+			   ARRAY_SIZE(mt2701_afe_o21_mix)),
+	SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+			   ARRAY_SIZE(mt2701_afe_o22_mix)),
+	SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+			   ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+	SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+	SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+	SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+	SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+
+	SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+	SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+	SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+	SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+	{"I12", NULL, "DL1"},
+	{"I13", NULL, "DL1"},
+	{"I35", NULL, "DLBT"},
+
+	{"I2S0 Playback", NULL, "O15"},
+	{"I2S0 Playback", NULL, "O16"},
+
+	{"I2S1 Playback", NULL, "O17"},
+	{"I2S1 Playback", NULL, "O18"},
+	{"I2S2 Playback", NULL, "O19"},
+	{"I2S2 Playback", NULL, "O20"},
+	{"I2S3 Playback", NULL, "O21"},
+	{"I2S3 Playback", NULL, "O22"},
+	{"BT Playback", NULL, "O31"},
+
+	{"UL1", NULL, "O00"},
+	{"UL1", NULL, "O01"},
+	{"UL2", NULL, "O02"},
+	{"UL2", NULL, "O03"},
+	{"ULBT", NULL, "O14"},
+
+	{"I00", NULL, "I2S0 Capture"},
+	{"I01", NULL, "I2S0 Capture"},
+
+	{"I02", NULL, "I2S1 Capture"},
+	{"I03", NULL, "I2S1 Capture"},
+	/* I02,03 link to UL2, also need to open I2S0 */
+	{"I02", "I2S0 Switch", "I2S0 Capture"},
+
+	{"I26", NULL, "BT Capture"},
+
+	{"ASRC_O0", "Asrc0 out Switch", "DLM"},
+	{"ASRC_O1", "Asrc1 out Switch", "DLM"},
+	{"ASRC_O2", "Asrc2 out Switch", "DLM"},
+	{"ASRC_O3", "Asrc3 out Switch", "DLM"},
+
+	{"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"},
+	{"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"},
+	{"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"},
+	{"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"},
+
+	{ "I12", NULL, "I12I13" },
+	{ "I13", NULL, "I12I13" },
+	{ "I14", NULL, "I14I15" },
+	{ "I15", NULL, "I14I15" },
+	{ "I16", NULL, "I16I17" },
+	{ "I17", NULL, "I16I17" },
+	{ "I18", NULL, "I18I19" },
+	{ "I19", NULL, "I18I19" },
+
+	{ "O00", "I00 Switch", "I00" },
+	{ "O01", "I01 Switch", "I01" },
+	{ "O02", "I02 Switch", "I02" },
+	{ "O03", "I03 Switch", "I03" },
+	{ "O14", "I26 Switch", "I26" },
+	{ "O15", "I12 Switch", "I12" },
+	{ "O16", "I13 Switch", "I13" },
+	{ "O17", "I14 Switch", "I14" },
+	{ "O18", "I15 Switch", "I15" },
+	{ "O19", "I16 Switch", "I16" },
+	{ "O20", "I17 Switch", "I17" },
+	{ "O21", "I18 Switch", "I18" },
+	{ "O22", "I19 Switch", "I19" },
+	{ "O31", "I35 Switch", "I35" },
+
+};
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+	.name = "mt2701-afe-pcm-dai",
+	.dapm_widgets = mt2701_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+	.dapm_routes = mt2701_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+};
+
+static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
+	{
+		.name = "DL1",
+		.id = MT2701_MEMIF_DL1,
+		.reg_ofs_base = AFE_DL1_BASE,
+		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 16,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 1,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 6,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL2",
+		.id = MT2701_MEMIF_DL2,
+		.reg_ofs_base = AFE_DL2_BASE,
+		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 17,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 2,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 7,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL3",
+		.id = MT2701_MEMIF_DL3,
+		.reg_ofs_base = AFE_DL3_BASE,
+		.reg_ofs_cur = AFE_DL3_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 18,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 3,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 4,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 8,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL4",
+		.id = MT2701_MEMIF_DL4,
+		.reg_ofs_base = AFE_DL4_BASE,
+		.reg_ofs_cur = AFE_DL4_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 19,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 4,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 9,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL5",
+		.id = MT2701_MEMIF_DL5,
+		.reg_ofs_base = AFE_DL5_BASE,
+		.reg_ofs_cur = AFE_DL5_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 20,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 20,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 5,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 10,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLM",
+		.id = MT2701_MEMIF_DLM,
+		.reg_ofs_base = AFE_DLMCH_BASE,
+		.reg_ofs_cur = AFE_DLMCH_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 7,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 28,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 12,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL1",
+		.id = MT2701_MEMIF_UL1,
+		.reg_ofs_base = AFE_VUL_BASE,
+		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 0,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 10,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 0,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL2",
+		.id = MT2701_MEMIF_UL2,
+		.reg_ofs_base = AFE_UL2_BASE,
+		.reg_ofs_cur = AFE_UL2_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 2,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 11,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 1,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL3",
+		.id = MT2701_MEMIF_UL3,
+		.reg_ofs_base = AFE_UL3_BASE,
+		.reg_ofs_cur = AFE_UL3_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 4,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 12,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 2,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL4",
+		.id = MT2701_MEMIF_UL4,
+		.reg_ofs_base = AFE_UL4_BASE,
+		.reg_ofs_cur = AFE_UL4_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 6,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 13,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 3,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL5",
+		.id = MT2701_MEMIF_UL5,
+		.reg_ofs_base = AFE_UL5_BASE,
+		.reg_ofs_cur = AFE_UL5_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 20,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 8,
+		.fs_maskbit = 0x1f,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 14,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 4,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLBT",
+		.id = MT2701_MEMIF_DLBT,
+		.reg_ofs_base = AFE_ARB1_BASE,
+		.reg_ofs_cur = AFE_ARB1_CUR,
+		.fs_reg = AFE_DAC_CON3,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 22,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 8,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 14,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 13,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "ULBT",
+		.id = MT2701_MEMIF_ULBT,
+		.reg_ofs_base = AFE_DAI_BASE,
+		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 30,
+		.fs_maskbit = 0x1,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 17,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 20,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 16,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+	{
+		.id = MT2701_IRQ_ASYS_IRQ1,
+		.irq_cnt_reg = ASYS_IRQ1_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ1_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ1_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 0,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ2,
+		.irq_cnt_reg = ASYS_IRQ2_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ2_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ2_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 1,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ3,
+		.irq_cnt_reg = ASYS_IRQ3_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ3_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ3_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 2,
+	}
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = {
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO1_CON,
+			.i2s_pwn_shift = 6,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN1_CON,
+			.i2s_pwn_shift = 0,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO2_CON,
+			.i2s_pwn_shift = 7,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN2_CON,
+			.i2s_pwn_shift = 1,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO3_CON,
+			.i2s_pwn_shift = 8,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN3_CON,
+			.i2s_pwn_shift = 2,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO4_CON,
+			.i2s_pwn_shift = 9,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN4_CON,
+			.i2s_pwn_shift = 3,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AFE_END_ADDR,
+	.cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+	int id;
+	struct mtk_base_afe *afe = dev;
+	struct mtk_base_afe_memif *memif;
+	struct mtk_base_afe_irq *irq;
+	u32 status;
+
+	regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+	regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+	for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+		memif = &afe->memif[id];
+		if (memif->irq_usage < 0)
+			continue;
+		irq = &afe->irqs[memif->irq_usage];
+		if (status & 1 << (irq->irq_data->irq_clr_shift))
+			snd_pcm_period_elapsed(memif->substream);
+	}
+	return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	return mt2701_afe_enable_clock(afe);
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+	int ret, i;
+	unsigned int irq_id;
+	struct mtk_base_afe *afe;
+	struct mt2701_afe_private *afe_priv;
+	struct resource *res;
+	struct device *dev;
+
+	ret = 0;
+	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe)
+		return -ENOMEM;
+
+	afe->dev = &pdev->dev;
+	dev = afe->dev;
+
+	irq_id = platform_get_irq(pdev, 0);
+	if (!irq_id) {
+		dev_err(dev, "%s no irq found\n", dev->of_node->name);
+		return -ENXIO;
+	}
+	ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+			       IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+	if (ret) {
+		dev_err(dev, "could not request_irq for asys-isr\n");
+		return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+	if (IS_ERR(afe->base_addr))
+		return PTR_ERR(afe->base_addr);
+
+	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+		&mt2701_afe_regmap_config);
+	if (IS_ERR(afe->regmap))
+		return PTR_ERR(afe->regmap);
+
+	mutex_init(&afe->irq_alloc_lock);
+
+	/* memif initialize */
+	afe->memif_size = MT2701_MEMIF_NUM;
+	afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+				  GFP_KERNEL);
+
+	if (!afe->memif)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->memif_size; i++) {
+		afe->memif[i].data = &memif_data[i];
+		afe->memif[i].irq_usage = -1;
+	}
+
+	/* irq initialize */
+	afe->irqs_size = MT2701_IRQ_ASYS_END;
+	afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+				 GFP_KERNEL);
+
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++)
+		afe->irqs[i].irq_data = &irq_data[i];
+
+	/* I2S initialize */
+	for (i = 0; i < MT2701_I2S_NUM; i++) {
+		afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+			= &mt2701_i2s_data[i][I2S_OUT];
+		afe_priv->i2s_path[i].i2s_data[I2S_IN]
+			= &mt2701_i2s_data[i][I2S_IN];
+	}
+
+	afe->mtk_afe_hardware = &mt2701_afe_hardware;
+	afe->memif_fs = mt2701_memif_fs;
+	afe->irq_fs = mt2701_irq_fs;
+
+	afe->reg_back_up_list = mt2701_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+	afe->runtime_resume = mt2701_afe_runtime_resume;
+	afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+	/* initial audio related clock */
+	ret = mt2701_init_clock(afe);
+	if (ret) {
+		dev_err(dev, "init clock error\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, afe);
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev))
+		goto err_pm_disable;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	if (ret) {
+		dev_warn(dev, "err_platform\n");
+		goto err_platform;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev,
+					 &mt2701_afe_pcm_dai_component,
+					 mt2701_afe_pcm_dais,
+					 ARRAY_SIZE(mt2701_afe_pcm_dais));
+	if (ret) {
+		dev_warn(dev, "err_dai_component\n");
+		goto err_dai_component;
+	}
+
+	mt2701_afe_runtime_resume(&pdev->dev);
+
+	return 0;
+
+err_dai_component:
+	snd_soc_unregister_component(&pdev->dev);
+
+err_platform:
+	snd_soc_unregister_platform(&pdev->dev);
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		mt2701_afe_runtime_suspend(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_unregister_platform(&pdev->dev);
+	/* disable afe clock */
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+			   mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+	.driver = {
+		   .name = "mt2701-audio",
+		   .of_match_table = mt2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+		   .pm = &mt2701_afe_pm_ops,
+#endif
+	},
+	.probe = mt2701_afe_pcm_dev_probe,
+	.remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Sun Jun 12 23:24:04 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 14:24:04 +0800
Subject: [alsa-devel] [PATCH v3 1/9] ASoC: mediatek: Refine mt8173 driver and
 change config option
In-Reply-To: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465799052-9657-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465799052-9657-2-git-send-email-garlic.tseng@mediatek.com>

move mt8173 driver to another folder and add prefix.
add config option SND_SOC_MT8173

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                         |  14 +-
 sound/soc/mediatek/Makefile                        |   9 +-
 sound/soc/mediatek/mt8173/Makefile                 |   7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      | 101 +++++
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     | 494 ++++++++++-----------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |   4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |   2 +-
 sound/soc/mediatek/mtk-afe-common.h                | 101 -----
 10 files changed, 367 insertions(+), 369 deletions(-)
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (66%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3abf51c..ae9f664 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,15 @@
-config SND_SOC_MEDIATEK
-	tristate "ASoC support for Mediatek chip"
+config SND_SOC_MT8173
+	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
 	help
-	  This adds ASoC platform driver support for Mediatek chip
+	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
 	  Select Y if you have such device.
 	  Ex: MT8173
 
 config SND_SOC_MT8173_MAX98090
 	tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_MAX98090
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -19,7 +19,7 @@ config SND_SOC_MT8173_MAX98090
 
 config SND_SOC_MT8173_RT5650
 	tristate "ASoC Audio driver for MT8173 with RT5650 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -29,7 +29,7 @@ config SND_SOC_MT8173_RT5650
 
 config SND_SOC_MT8173_RT5650_RT5514
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5514
 	help
@@ -40,7 +40,7 @@ config SND_SOC_MT8173_RT5650_RT5514
 
 config SND_SOC_MT8173_RT5650_RT5676
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5677
 	select SND_SOC_HDMI_CODEC
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index d486860..240dfc70 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,7 +1,2 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 0000000..0357b27
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,7 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644
index 0000000..8f2936d
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,101 @@
+/*
+ * mt8173_afe_common.h  --  Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen at mediatek.com>
+ *             Sascha Hauer <s.hauer at pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+	MT8173_AFE_MEMIF_DL1,
+	MT8173_AFE_MEMIF_DL2,
+	MT8173_AFE_MEMIF_VUL,
+	MT8173_AFE_MEMIF_DAI,
+	MT8173_AFE_MEMIF_AWB,
+	MT8173_AFE_MEMIF_MOD_DAI,
+	MT8173_AFE_MEMIF_HDMI,
+	MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM2,
+	MT8173_AFE_IO_PMIC,
+	MT8173_AFE_IO_I2S,
+	MT8173_AFE_IO_2ND_I2S,
+	MT8173_AFE_IO_HW_GAIN1,
+	MT8173_AFE_IO_HW_GAIN2,
+	MT8173_AFE_IO_MRG_O,
+	MT8173_AFE_IO_MRG_I,
+	MT8173_AFE_IO_DAIBT,
+	MT8173_AFE_IO_HDMI,
+};
+
+enum {
+	MT8173_AFE_IRQ_1,
+	MT8173_AFE_IRQ_2,
+	MT8173_AFE_IRQ_3,
+	MT8173_AFE_IRQ_4,
+	MT8173_AFE_IRQ_5,
+	MT8173_AFE_IRQ_6,
+	MT8173_AFE_IRQ_7,
+	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+	MT8173_CLK_INFRASYS_AUD,
+	MT8173_CLK_TOP_PDN_AUD,
+	MT8173_CLK_TOP_PDN_AUD_BUS,
+	MT8173_CLK_I2S0_M,
+	MT8173_CLK_I2S1_M,
+	MT8173_CLK_I2S2_M,
+	MT8173_CLK_I2S3_M,
+	MT8173_CLK_I2S3_B,
+	MT8173_CLK_BCK0,
+	MT8173_CLK_BCK1,
+	MT8173_CLK_NUM
+};
+
+struct mt8173_afe;
+struct snd_pcm_substream;
+
+struct mt8173_afe_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_shift;
+	int mono_shift;
+	int enable_shift;
+	int irq_reg_cnt;
+	int irq_cnt_shift;
+	int irq_en_shift;
+	int irq_fs_shift;
+	int irq_clr_shift;
+	int msb_shift;
+};
+
+struct mt8173_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mt8173_afe_memif_data *data;
+	const struct mt8173_afe_irq_data *irqdata;
+};
+
+#endif
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
similarity index 66%
rename from sound/soc/mediatek/mtk-afe-pcm.c
rename to sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 793d7e2..4fc52bc 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1,5 +1,5 @@
 /*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
  *
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Koro Chen <koro.chen at mediatek.com>
@@ -24,7 +24,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
-#include "mtk-afe-common.h"
+#include "mt8173-afe-common.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -135,7 +135,7 @@ enum afe_tdm_ch_start {
 	AFE_TDM_CH_ZERO,
 };
 
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
 	AUDIO_TOP_CON0,
 	AFE_CONN1,
 	AFE_CONN2,
@@ -152,18 +152,18 @@ static const unsigned int mtk_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mtk_afe {
+struct mt8173_afe {
 	/* address for ioremap audio hardware register */
 	void __iomem *base_addr;
 	struct device *dev;
 	struct regmap *regmap;
-	struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
-	struct clk *clocks[MTK_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
+	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+	struct clk *clocks[MT8173_CLK_NUM];
+	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
 	bool suspended;
 };
 
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 		 SNDRV_PCM_INFO_MMAP_VALID),
 	.buffer_bytes_max = 256 * 1024,
@@ -174,12 +174,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
+static snd_pcm_uframes_t mt8173_afe_pcm_pointer
 			 (struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int hw_ptr;
 	int ret;
 
@@ -193,40 +193,40 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
 			       hw_ptr - memif->phys_buf_addr);
 }
 
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
 	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mtk_afe_pcm_pointer,
+	.pointer = mt8173_afe_pcm_pointer,
 };
 
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	size_t size;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_pcm *pcm = rtd->pcm;
 
-	size = mtk_afe_hardware.buffer_bytes_max;
+	size = mt8173_afe_hardware.buffer_bytes_max;
 
 	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						     card->dev, size, size);
 }
 
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
 {
 	snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
-	.ops = &mtk_afe_pcm_ops,
-	.pcm_new = mtk_afe_pcm_new,
-	.pcm_free = mtk_afe_pcm_free,
+static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
+	.ops = &mt8173_afe_pcm_ops,
+	.pcm_new = mt8173_afe_pcm_new,
+	.pcm_free = mt8173_afe_pcm_free,
 };
 
-struct mtk_afe_rate {
+struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
 };
 
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
 	{ .rate = 8000, .regvalue = 0 },
 	{ .rate = 11025, .regvalue = 1 },
 	{ .rate = 12000, .regvalue = 2 },
@@ -242,21 +242,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
 	{ .rate = 192000, .regvalue = 14 },
 };
 
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
-		if (mtk_afe_i2s_rates[i].rate == sample_rate)
-			return mtk_afe_i2s_rates[i].regvalue;
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+		if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+			return mt8173_afe_i2s_rates[i].regvalue;
 
 	return -EINVAL;
 }
 
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 {
 	unsigned int val;
-	int fs = mtk_afe_i2s_fs(rate);
+	int fs = mt8173_afe_i2s_fs(rate);
 
 	if (fs < 0)
 		return -EINVAL;
@@ -281,7 +281,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,8 +296,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
-				    struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
 
@@ -319,9 +319,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
-				 struct clk *m_ck, unsigned int mck_rate,
-				 struct clk *b_ck, unsigned int bck_rate)
+static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+				    struct clk *m_ck, unsigned int mck_rate,
+				    struct clk *b_ck, unsigned int bck_rate)
 {
 	int ret;
 
@@ -343,8 +343,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
-				      struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
 		clk_disable_unprepare(m_ck);
@@ -352,11 +352,11 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
 		clk_disable_unprepare(b_ck);
 }
 
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -366,84 +366,82 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
-				 struct snd_soc_dai *dai)
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_set_i2s_enable(afe, false);
+	mt8173_afe_set_i2s_enable(afe, false);
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
 }
 
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	int ret;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
-			      NULL, 0);
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
-			      NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+				 runtime->rate * 256, NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
-	ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
 	if (ret)
 		return ret;
 
-	mtk_afe_set_i2s_enable(afe, true);
+	mt8173_afe_set_i2s_enable(afe, true);
 
 	return 0;
 }
 
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
 
-	mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				 afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				    afe->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				  afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				     afe->clocks[MT8173_CLK_I2S3_B]);
 }
 
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	unsigned int val;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
-			      afe->clocks[MTK_CLK_I2S3_B],
-			      runtime->rate * runtime->channels * 32);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				 runtime->rate * 128,
+				 afe->clocks[MT8173_CLK_I2S3_B],
+				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
 	      AFE_TDM_CON1_LRCK_INV |
@@ -494,11 +492,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -540,18 +538,18 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 	}
 }
 
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int ret;
 
 	memif->substream = substream;
 
-	snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
 
 	/*
 	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
@@ -563,7 +561,7 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 		ret = snd_pcm_hw_constraint_minmax(runtime,
 						   SNDRV_PCM_HW_PARAM_PERIODS,
 						   3,
-						   mtk_afe_hardware.periods_max);
+						   mt8173_afe_hardware.periods_max);
 		if (ret < 0) {
 			dev_err(afe->dev, "hw_constraint_minmax failed\n");
 			return ret;
@@ -576,23 +574,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 
 	memif->substream = NULL;
 }
 
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params,
-				  struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int msb_at_bit33 = 0;
 	int ret;
 
@@ -634,8 +632,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	/* set rate */
 	if (memif->data->fs_shift < 0)
 		return 0;
-	if (memif->data->id == MTK_AFE_MEMIF_DAI ||
-	    memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
+	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
 		unsigned int val;
 
 		switch (params_rate(params)) {
@@ -652,7 +650,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 			return -EINVAL;
 		}
 
-		if (memif->data->id == MTK_AFE_MEMIF_DAI)
+		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
 			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
 					   0x3 << memif->data->fs_shift,
 					   val << memif->data->fs_shift);
@@ -662,7 +660,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 					   val << memif->data->fs_shift);
 
 	} else {
-		int fs = mtk_afe_i2s_fs(params_rate(params));
+		int fs = mt8173_afe_i2s_fs(params_rate(params));
 
 		if (fs < 0)
 			return -EINVAL;
@@ -675,19 +673,19 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	return snd_pcm_lib_free_pages(substream);
 }
 
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int counter = runtime->period_size;
 
 	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
@@ -708,7 +706,7 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set irq fs */
 		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mtk_afe_i2s_fs(runtime->rate);
+			int fs = mt8173_afe_i2s_fs(runtime->rate);
 
 			if (fs < 0)
 				return -EINVAL;
@@ -743,76 +741,76 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 }
 
 /* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
-	.startup	= mtk_afe_dais_startup,
-	.shutdown	= mtk_afe_dais_shutdown,
-	.hw_params	= mtk_afe_dais_hw_params,
-	.hw_free	= mtk_afe_dais_hw_free,
-	.trigger	= mtk_afe_dais_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
+	.startup	= mt8173_afe_dais_startup,
+	.shutdown	= mt8173_afe_dais_shutdown,
+	.hw_params	= mt8173_afe_dais_hw_params,
+	.hw_free	= mt8173_afe_dais_hw_free,
+	.trigger	= mt8173_afe_dais_trigger,
 };
 
 /* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
-	.startup	= mtk_afe_i2s_startup,
-	.shutdown	= mtk_afe_i2s_shutdown,
-	.prepare	= mtk_afe_i2s_prepare,
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+	.startup	= mt8173_afe_i2s_startup,
+	.shutdown	= mt8173_afe_i2s_shutdown,
+	.prepare	= mt8173_afe_i2s_prepare,
 };
 
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
-	.startup	= mtk_afe_hdmi_startup,
-	.shutdown	= mtk_afe_hdmi_shutdown,
-	.prepare	= mtk_afe_hdmi_prepare,
-	.trigger	= mtk_afe_hdmi_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+	.startup	= mt8173_afe_hdmi_startup,
+	.shutdown	= mt8173_afe_hdmi_shutdown,
+	.prepare	= mt8173_afe_hdmi_prepare,
+	.trigger	= mt8173_afe_hdmi_trigger,
 
 };
 
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
+static int mt8173_afe_runtime_suspend(struct device *dev);
+static int mt8173_afe_runtime_resume(struct device *dev);
 
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_read(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
 			    &afe->backup_regs[i]);
 
 	afe->suspended = true;
-	mtk_afe_runtime_suspend(afe->dev);
+	mt8173_afe_runtime_suspend(afe->dev);
 	return 0;
 }
 
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i = 0;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
 		return 0;
 
-	mtk_afe_runtime_resume(afe->dev);
+	mt8173_afe_runtime_resume(afe->dev);
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_write(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
 			     afe->backup_regs[i]);
 
 	afe->suspended = false;
 	return 0;
 }
 
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
-		.id = MTK_AFE_MEMIF_DL1,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_DL1,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -820,12 +818,12 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
-		.id = MTK_AFE_MEMIF_VUL,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_VUL,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -833,11 +831,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
-		.id = MTK_AFE_IO_I2S,
+		.id = MT8173_AFE_IO_I2S,
 		.playback = {
 			.stream_name = "I2S Playback",
 			.channels_min = 1,
@@ -852,18 +850,18 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_i2s_ops,
+		.ops = &mt8173_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
 };
 
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	/* FE DAIs */
 	{
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_HDMI,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -874,11 +872,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
-		.id = MTK_AFE_IO_HDMI,
+		.id = MT8173_AFE_IO_HDMI,
 		.playback = {
 			.stream_name = "HDMIO Playback",
 			.channels_min = 2,
@@ -889,29 +887,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_hdmi_ops,
+		.ops = &mt8173_afe_hdmi_ops,
 	},
 };
 
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
 	/* inter-connections */
 	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -921,16 +919,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
 	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+			   mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
 	SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+			   mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
 	SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+			   mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
 	SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+			   mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
 };
 
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
 	{"I05", NULL, "DL1"},
 	{"I06", NULL, "DL1"},
 	{"I2S Playback", NULL, "O03"},
@@ -949,41 +947,41 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
 	{ "O10", "I04 Switch", "I04" },
 };
 
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
 	{"HDMIO Playback", NULL, "HDMI"},
 };
 
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
-	.name = "mtk-afe-pcm-dai",
-	.dapm_widgets = mtk_afe_pcm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
-	.dapm_routes = mtk_afe_pcm_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+	.name = "mt8173-afe-pcm-dai",
+	.dapm_widgets = mt8173_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+	.dapm_routes = mt8173_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
 };
 
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
-	.name = "mtk-afe-hdmi-dai",
-	.dapm_routes = mtk_afe_hdmi_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+	.name = "mt8173-afe-hdmi-dai",
+	.dapm_routes = mt8173_afe_hdmi_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
 };
 
-static const char *aud_clks[MTK_CLK_NUM] = {
-	[MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
-	[MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
-	[MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
-	[MTK_CLK_I2S0_M] =  "i2s0_m",
-	[MTK_CLK_I2S1_M] =  "i2s1_m",
-	[MTK_CLK_I2S2_M] =  "i2s2_m",
-	[MTK_CLK_I2S3_M] =  "i2s3_m",
-	[MTK_CLK_I2S3_B] =  "i2s3_b",
-	[MTK_CLK_BCK0] =  "bck0",
-	[MTK_CLK_BCK1] =  "bck1",
+static const char *aud_clks[MT8173_CLK_NUM] = {
+	[MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+	[MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+	[MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+	[MT8173_CLK_I2S0_M] =  "i2s0_m",
+	[MT8173_CLK_I2S1_M] =  "i2s1_m",
+	[MT8173_CLK_I2S2_M] =  "i2s2_m",
+	[MT8173_CLK_I2S3_M] =  "i2s3_m",
+	[MT8173_CLK_I2S3_B] =  "i2s3_b",
+	[MT8173_CLK_BCK0] =  "bck0",
+	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
-		.id = MTK_AFE_MEMIF_DL1,
+		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
 		.fs_shift = 0,
@@ -997,7 +995,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 0,
 	}, {
 		.name = "DL2",
-		.id = MTK_AFE_MEMIF_DL2,
+		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
 		.fs_shift = 4,
@@ -1011,7 +1009,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 1,
 	}, {
 		.name = "VUL",
-		.id = MTK_AFE_MEMIF_VUL,
+		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
 		.fs_shift = 16,
@@ -1025,7 +1023,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 6,
 	}, {
 		.name = "DAI",
-		.id = MTK_AFE_MEMIF_DAI,
+		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
 		.fs_shift = 24,
@@ -1039,7 +1037,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 5,
 	}, {
 		.name = "AWB",
-		.id = MTK_AFE_MEMIF_AWB,
+		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
 		.fs_shift = 12,
@@ -1053,7 +1051,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 3,
 	}, {
 		.name = "MOD_DAI",
-		.id = MTK_AFE_MEMIF_MOD_DAI,
+		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
 		.fs_shift = 30,
@@ -1067,7 +1065,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 4,
 	}, {
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
+		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
 		.fs_shift = -1,
@@ -1082,7 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 	},
 };
 
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -1090,9 +1088,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
 	.cache_type = REGCACHE_NONE,
 };
 
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mtk_afe *afe = dev_id;
+	struct mt8173_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1103,8 +1101,8 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
 		goto err_irq;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
-		struct mtk_afe_memif *memif = &afe->memif[i];
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+		struct mt8173_afe_memif *memif = &afe->memif[i];
 
 		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
 			continue;
@@ -1119,9 +1117,9 @@ err_irq:
 	return IRQ_HANDLED;
 }
 
-static int mtk_afe_runtime_suspend(struct device *dev)
+static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1129,45 +1127,44 @@ static int mtk_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
-static int mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1184,23 +1181,22 @@ static int mtk_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
-
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
 {
 	size_t i;
 
@@ -1212,16 +1208,16 @@ static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
 			return PTR_ERR(afe->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mtk_afe *afe;
+	struct mt8173_afe *afe;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1239,7 +1235,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
 		return -ENXIO;
 	}
-	ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+	ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
 			       0, "Afe_ISR_Handle", (void *)afe);
 	if (ret) {
 		dev_err(afe->dev, "could not request_irq\n");
@@ -1252,48 +1248,48 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		return PTR_ERR(afe->base_addr);
 
 	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
-		&mtk_afe_regmap_config);
+		&mt8173_afe_regmap_config);
 	if (IS_ERR(afe->regmap))
 		return PTR_ERR(afe->regmap);
 
 	/* initial audio related clock */
-	ret = mtk_afe_init_audio_clk(afe);
+	ret = mt8173_afe_init_audio_clk(afe);
 	if (ret) {
-		dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+		dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
 		return ret;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
 		afe->memif[i].data = &memif_data[i];
 
 	platform_set_drvdata(pdev, afe);
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = mtk_afe_runtime_resume(&pdev->dev);
+		ret = mt8173_afe_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_pcm_dai_component,
-					 mtk_afe_pcm_dais,
-					 ARRAY_SIZE(mtk_afe_pcm_dais));
+					 &mt8173_afe_pcm_dai_component,
+					 mt8173_afe_pcm_dais,
+					 ARRAY_SIZE(mt8173_afe_pcm_dais));
 	if (ret)
 		goto err_platform;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_hdmi_dai_component,
-					 mtk_afe_hdmi_dais,
-					 ARRAY_SIZE(mtk_afe_hdmi_dais));
+					 &mt8173_afe_hdmi_dai_component,
+					 mt8173_afe_hdmi_dais,
+					 ARRAY_SIZE(mt8173_afe_hdmi_dais));
 	if (ret)
 		goto err_comp;
 
-	dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+	dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
 	return 0;
 
 err_comp:
@@ -1305,38 +1301,38 @@ err_pm_disable:
 	return ret;
 }
 
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		mtk_afe_runtime_suspend(&pdev->dev);
+		mt8173_afe_runtime_suspend(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 	snd_soc_unregister_platform(&pdev->dev);
 	return 0;
 }
 
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
 	{ .compatible = "mediatek,mt8173-afe-pcm", },
 	{ }
 };
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
 
-static const struct dev_pm_ops mtk_afe_pm_ops = {
-	SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
-			   NULL)
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+			   mt8173_afe_runtime_resume, NULL)
 };
 
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
 	.driver = {
-		   .name = "mtk-afe-pcm",
-		   .of_match_table = mtk_afe_pcm_dt_match,
-		   .pm = &mtk_afe_pm_ops,
+		   .name = "mt8173-afe-pcm",
+		   .of_match_table = mt8173_afe_pcm_dt_match,
+		   .pm = &mt8173_afe_pm_ops,
 	},
-	.probe = mtk_afe_pcm_dev_probe,
-	.remove = mtk_afe_pcm_dev_remove,
+	.probe = mt8173_afe_pcm_dev_probe,
+	.remove = mt8173_afe_pcm_dev_remove,
 };
 
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
 
 MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
 MODULE_AUTHOR("Koro Chen <koro.chen at mediatek.com>");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-max98090.c
rename to sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35..5524a2c 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -18,7 +18,7 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 static struct snd_soc_jack mt8173_max98090_jack;
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e0836..467f704 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb59392..1b8b2a7 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -19,8 +19,8 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 072934b..d478976 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index f341f62..0000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * mtk_afe_common.h  --  Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen at mediatek.com>
- *             Sascha Hauer <s.hauer at pengutronix.de>
- *             Hidalgo Huang <hidalgo.huang at mediatek.com>
- *             Ir Lian <ir.lian at mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
-	MTK_AFE_MEMIF_DL1,
-	MTK_AFE_MEMIF_DL2,
-	MTK_AFE_MEMIF_VUL,
-	MTK_AFE_MEMIF_DAI,
-	MTK_AFE_MEMIF_AWB,
-	MTK_AFE_MEMIF_MOD_DAI,
-	MTK_AFE_MEMIF_HDMI,
-	MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM2,
-	MTK_AFE_IO_PMIC,
-	MTK_AFE_IO_I2S,
-	MTK_AFE_IO_2ND_I2S,
-	MTK_AFE_IO_HW_GAIN1,
-	MTK_AFE_IO_HW_GAIN2,
-	MTK_AFE_IO_MRG_O,
-	MTK_AFE_IO_MRG_I,
-	MTK_AFE_IO_DAIBT,
-	MTK_AFE_IO_HDMI,
-};
-
-enum {
-	MTK_AFE_IRQ_1,
-	MTK_AFE_IRQ_2,
-	MTK_AFE_IRQ_3,
-	MTK_AFE_IRQ_4,
-	MTK_AFE_IRQ_5,
-	MTK_AFE_IRQ_6,
-	MTK_AFE_IRQ_7,
-	MTK_AFE_IRQ_8,
-	MTK_AFE_IRQ_NUM,
-};
-
-enum {
-	MTK_CLK_INFRASYS_AUD,
-	MTK_CLK_TOP_PDN_AUD,
-	MTK_CLK_TOP_PDN_AUD_BUS,
-	MTK_CLK_I2S0_M,
-	MTK_CLK_I2S1_M,
-	MTK_CLK_I2S2_M,
-	MTK_CLK_I2S3_M,
-	MTK_CLK_I2S3_B,
-	MTK_CLK_BCK0,
-	MTK_CLK_BCK1,
-	MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mtk_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mtk_afe_memif_data *data;
-	const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
-- 
1.9.1



From ck.hu at mediatek.com  Sun Jun 12 23:43:03 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Mon, 13 Jun 2016 14:43:03 +0800
Subject: [RFC v3 3/5] drm/mediatek: add shadow register support
In-Reply-To: <1465401825-43642-4-git-send-email-yt.shen@mediatek.com>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
 <1465401825-43642-4-git-send-email-yt.shen@mediatek.com>
Message-ID: <1465800183.27253.4.camel@mtksdaap41>

Hi, YT:

One comment inline.

On Thu, 2016-06-09 at 00:03 +0800, YT Shen wrote:
> We need to acquire mutex before using the resources,
> and need to release it after finished.
> So we don't need to write registers in the blanking period.
> 
> Signed-off-by: YT Shen <yt.shen at mediatek.com>
> ---
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.c |   75 +++++++++++++++++++------------
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.c  |   22 +++++++++
>  drivers/gpu/drm/mediatek/mtk_drm_ddp.h  |    2 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
>  5 files changed, 72 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> index 24aa3ba..80d9641 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> @@ -315,6 +315,42 @@ static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
>  	pm_runtime_put(drm->dev);
>  }
>  
> +static void mtk_crtc_ddp_config(struct drm_crtc *crtc)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
> +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> +	unsigned int i;
> +
> +	/*
> +	 * TODO: instead of updating the registers here, we should prepare
> +	 * working registers in atomic_commit and let the hardware command
> +	 * queue update module registers on vblank.
> +	 */
> +	if (state->pending_config) {
> +		mtk_ddp_comp_config(ovl, state->pending_width,
> +				    state->pending_height,
> +				    state->pending_vrefresh);
> +
> +		state->pending_config = false;
> +	}
> +
> +	if (mtk_crtc->pending_planes) {
> +		for (i = 0; i < OVL_LAYER_NR; i++) {
> +			struct drm_plane *plane = &mtk_crtc->planes[i].base;
> +			struct mtk_plane_state *plane_state;
> +
> +			plane_state = to_mtk_plane_state(plane->state);
> +
> +			if (plane_state->pending.config) {
> +				mtk_ddp_comp_layer_config(ovl, i, plane_state);
> +				plane_state->pending.config = false;
> +			}
> +		}
> +		mtk_crtc->pending_planes = false;
> +	}
> +}
> +
>  static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
>  {
>  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> @@ -391,6 +427,7 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
>  				      struct drm_crtc_state *old_crtc_state)
>  {
>  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	struct mtk_drm_private *priv = crtc->dev->dev_private;
>  	unsigned int pending_planes = 0;
>  	int i;
>  
> @@ -409,6 +446,12 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
>  	}
>  	if (pending_planes)
>  		mtk_crtc->pending_planes = true;
> +
> +	if (priv->data->shadow_register) {
> +		mtk_disp_mutex_acquire(mtk_crtc->mutex);
> +		mtk_crtc_ddp_config(crtc);
> +		mtk_disp_mutex_release(mtk_crtc->mutex);
> +	}
>  }
>  
>  static const struct drm_crtc_funcs mtk_crtc_funcs = {
> @@ -453,36 +496,10 @@ err_cleanup_crtc:
>  void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
>  {
>  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> -	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
> -	unsigned int i;
> +	struct mtk_drm_private *priv = crtc->dev->dev_private;
>  
> -	/*
> -	 * TODO: instead of updating the registers here, we should prepare
> -	 * working registers in atomic_commit and let the hardware command
> -	 * queue update module registers on vblank.
> -	 */
> -	if (state->pending_config) {
> -		mtk_ddp_comp_config(ovl, state->pending_width,
> -				    state->pending_height,
> -				    state->pending_vrefresh);
> -
> -		state->pending_config = false;
> -	}
> -
> -	if (mtk_crtc->pending_planes) {
> -		for (i = 0; i < OVL_LAYER_NR; i++) {
> -			struct drm_plane *plane = &mtk_crtc->planes[i].base;
> -			struct mtk_plane_state *plane_state;
> -
> -			plane_state = to_mtk_plane_state(plane->state);
> -
> -			if (plane_state->pending.config) {
> -				mtk_ddp_comp_layer_config(ovl, i, plane_state);
> -				plane_state->pending.config = false;
> -			}
> -		}
> -		mtk_crtc->pending_planes = false;
> -	}
> +	if (!priv->data->shadow_register)
> +		mtk_crtc_ddp_config(crtc);
>  
>  	mtk_drm_finish_page_flip(mtk_crtc);
>  }
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> index 8030769..fa53806 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> @@ -12,6 +12,7 @@
>   */
>  
>  #include <linux/clk.h>
> +#include <linux/iopoll.h>
>  #include <linux/module.h>
>  #include <linux/of_device.h>
>  #include <linux/platform_device.h>
> @@ -32,6 +33,7 @@
>  #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
>  
>  #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
> +#define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
>  #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
>  #define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
>  #define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
> @@ -300,6 +302,26 @@ void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
>  	writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
>  }
>  
> +void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +	u32 tmp;
> +
> +	writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
> +	writel(1, ddp->regs + DISP_REG_MUTEX(mutex->id));
> +	readl_poll_timeout_atomic(ddp->regs + DISP_REG_MUTEX(mutex->id), tmp,
> +				  tmp & 0x2, 1, 10000);
> +}
> +
> +void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex)
> +{
> +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> +					   mutex[mutex->id]);
> +
> +	writel(0, ddp->regs + DISP_REG_MUTEX(mutex->id));
> +}
> +
>  static int mtk_ddp_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> index 92c1175..f9a7991 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> @@ -37,5 +37,7 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
>  				enum mtk_ddp_comp_id id);
>  void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
>  void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex);
> +void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex);
>  
>  #endif /* MTK_DRM_DDP_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index 80b4f54..e9eb535 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -133,6 +133,7 @@ static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
>  	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
>  	.ext_path = mt8173_mtk_ddp_ext,
>  	.ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
> +	.shadow_register = false,

global data is initialized as zero.

>  };
>  
>  static int mtk_drm_kms_init(struct drm_device *drm)
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> index fa0b106..94f8b66 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> @@ -33,6 +33,7 @@ struct mtk_mmsys_driver_data {
>  	unsigned int main_len;
>  	const enum mtk_ddp_comp_id *ext_path;
>  	unsigned int ext_len;
> +	bool shadow_register;
>  };
>  
>  struct mtk_drm_private {




From garlic.tseng at mediatek.com  Mon Jun 13 00:26:44 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:44 +0800
Subject: [alsa-devel] [PATCH v4 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-8-git-send-email-garlic.tseng@mediatek.com>

Add supports for 16k (wideband BT) and add a general compatible
string "linux,bt-sco"

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 Documentation/devicetree/bindings/sound/bt-sco.txt | 2 +-
 sound/soc/codecs/bt-sco.c                          | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt
index 29b8e5d..641edf7 100644
--- a/Documentation/devicetree/bindings/sound/bt-sco.txt
+++ b/Documentation/devicetree/bindings/sound/bt-sco.txt
@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad1..101b384 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -31,14 +31,14 @@ static struct snd_soc_dai_driver bt_sco_dai = {
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	.capture = {
 		 .stream_name = "Capture",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 };
@@ -77,6 +77,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
 	{ .compatible = "delta,dfbmcs320", },
+	{ .compatible = "linux,bt-sco", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:37 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:37 +0800
Subject: [alsa-devel] [PATCH v4 0/9] ASoC: Mediatek: Add support for MT2701 SOC
Message-ID: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>

Sorry for submitting new version in a short time.
I find some comment style error and fix it.
please ignore v3 and directly review v4.

This patch adds basic support for Mediatek AFE for MT2701 SoC.
The patch is based on broonie tree "for-next" branch.

Change since v3:

* fix comments style.

Change since v2:

* fix comments style.
* replace EXPORT_SYMBOL with EXPORT_SYMBOL_GPL
* reduce debug message
* move mutex lock for irq-allocated to AFE struct
* remove func mtk_simple_isr and call snd_pcm_period_elapsed directly
* mt2701 driver probe will fail if clock initial error
* fix dapm on/off switch control naming

Change since v1:

implement common private structure, fe dai and platform driver.
let mt8173 and mt2701 use the common structure.


Garlic Tseng (9):
  ASoC: mediatek: Refine mt8173 driver and change config option
  ASoC: mediatek: implement mediatek common structure
  ASoC: mediatek: let mt8173 use mediatek common structure
  ASoC: mediatek: add documents for mt2701
  ASoC: mediatek: add structure define and clock control for 2701
  ASoC: mediatek: add mt2701 platform driver implementation.
  ASoC: bt-sco: extend rate and add a general compatible string
  ASoC: mediatek: add BT implementation
  ASoC: mediatek: Add mt2701-cs42448 driver and config option.

 Documentation/devicetree/bindings/sound/bt-sco.txt |    2 +-
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   |  150 ++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |   43 +
 sound/soc/codecs/bt-sco.c                          |    5 +-
 sound/soc/mediatek/Kconfig                         |   44 +-
 sound/soc/mediatek/Makefile                        |   11 +-
 sound/soc/mediatek/common/Makefile                 |   17 +
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         |  379 +++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |   45 +
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |   90 ++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |   23 +
 sound/soc/mediatek/common/mtk-base-afe.h           |  104 ++
 sound/soc/mediatek/mt2701/Makefile                 |   19 +
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c  |  464 ++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h  |   38 +
 sound/soc/mediatek/mt2701/mt2701-afe-common.h      |  181 +++
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c         | 1698 ++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c         |  422 +++++
 sound/soc/mediatek/mt2701/mt2701-reg.h             |  186 +++
 sound/soc/mediatek/mt8173/Makefile                 |    7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      |   73 +
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     |  921 +++++------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |    4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |    2 +-
 sound/soc/mediatek/mtk-afe-common.h                |  101 --
 27 files changed, 4399 insertions(+), 634 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
 create mode 100644 sound/soc/mediatek/common/Makefile
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (51%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:41 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:41 +0800
Subject: [alsa-devel] [PATCH v4 4/9] ASoC: mediatek: add documents for mt2701
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-5-git-send-email-garlic.tseng@mediatek.com>

add mt2701-afe-pcm.txt and mt2701-cs42448.txt for mt2701

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   | 150 +++++++++++++++++++++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |  43 ++++++
 2 files changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 0000000..3e623a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm at 11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 0000000..0557444
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:45 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:45 +0800
Subject: [alsa-devel] [PATCH v4 8/9] ASoC: mediatek: add BT implementation
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-9-git-send-email-garlic.tseng@mediatek.com>

Add BT implementation for mt2701 platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 139 +++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index deb5f82..5fd2caa 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -377,6 +377,86 @@ static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+	afe_priv->mrg_enable[substream->stream] = 1;
+	return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_fs;
+	u32 val, msk;
+
+	stream_fs = params_rate(params);
+
+	if ((stream_fs != 8000) && (stream_fs != 16000)) {
+		dev_err(afe->dev, "%s() btmgr not supprt this stream_fs %d\n",
+			__func__, stream_fs);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_I2S_MODE_MASK,
+			   AFE_MRGIF_CON_I2S_MODE_32K);
+
+	val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+	      | AFE_DAIBT_CON0_MRG_USE;
+	msk = val;
+
+	if (stream_fs == 16000)
+		val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+			   AFE_DAIBT_CON0_DAIBT_EN,
+			   AFE_DAIBT_CON0_DAIBT_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_I2S_EN,
+			   AFE_MRGIF_CON_MRG_I2S_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_EN,
+			   AFE_MRGIF_CON_MRG_EN);
+	return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	/* if the other direction stream is not occupied */
+	if (!afe_priv->mrg_enable[!substream->stream]) {
+		regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+				   AFE_DAIBT_CON0_DAIBT_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_I2S_EN, 0);
+		regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+				   AUDIO_TOP_CON4_PDN_MRGIF,
+				   AUDIO_TOP_CON4_PDN_MRGIF);
+	}
+	afe_priv->mrg_enable[substream->stream] = 0;
+}
+
 static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
@@ -560,6 +640,13 @@ static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
 	.set_fmt	= mt2701_afe_i2s_set_fmt,
 };
 
+/* MRG BE DAIs */
+static struct snd_soc_dai_ops mt2701_btmrg_ops = {
+	.startup = mt2701_btmrg_startup,
+	.shutdown = mt2701_btmrg_shutdown,
+	.hw_params = mt2701_btmrg_hw_params,
+};
+
 static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
@@ -610,6 +697,36 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		},
 		.ops = &mt2701_single_memif_dai_ops,
 	},
+	{
+		.name = "PCM_BT_DL",
+		.id = MT2701_MEMIF_DLBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM_BT_UL",
+		.id = MT2701_MEMIF_ULBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "ULBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
 	/* BE DAIs */
 	{
 		.name = "I2S0",
@@ -709,6 +826,28 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		.ops = &mt2701_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
+	{
+		.name = "MRG BT",
+		.id = MT2701_IO_MRG,
+		.playback = {
+			.stream_name = "BT Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.stream_name = "BT Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_btmrg_ops,
+		.symmetric_rates = 1,
+	}
 };
 
 static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:39 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:39 +0800
Subject: [alsa-devel] [PATCH v4 2/9] ASoC: mediatek: implement mediatek common
 structure
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-3-git-send-email-garlic.tseng@mediatek.com>

implement mediatek basic structure, include common private data,
afe fe dai operator and afe platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         | 379 +++++++++++++++++++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |  45 +++
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |  90 +++++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |  23 ++
 sound/soc/mediatek/common/mtk-base-afe.h           | 104 ++++++
 5 files changed, 641 insertions(+)
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h

diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 0000000..b788791b
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,379 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+			   unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	/* enable agent */
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       0 << memif->data->agent_disable_shift);
+
+	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+	/*
+	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+	 * smaller than period_size due to AFE's internal buffer.
+	 * This easily leads to overrun when avail_min is period_size.
+	 * One more period can hold the possible unread buffer.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		int periods_max = mtk_afe_hardware->periods_max;
+
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+						   SNDRV_PCM_HW_PARAM_PERIODS,
+						   3, periods_max);
+		if (ret < 0) {
+			dev_err(afe->dev, "hw_constraint_minmax failed\n");
+			return ret;
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	/* dynamic allocate irq to memif */
+	if (memif->irq_usage < 0) {
+		int irq_id = mtk_dynamic_irq_acquire(afe);
+
+		if (irq_id != afe->irqs_size) {
+			/* link */
+			memif->irq_usage = irq_id;
+		} else {
+			dev_err(afe->dev, "%s() error: no more asys irq\n",
+				__func__);
+			ret = -EBUSY;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int irq_id;
+
+	irq_id = memif->irq_usage;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       1 << memif->data->agent_disable_shift);
+
+	if (!memif->const_irq) {
+		mtk_dynamic_irq_release(afe, irq_id);
+		memif->irq_usage = -1;
+		memif->substream = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int msb_at_bit33 = 0;
+	int ret, fs = 0;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+			 memif->phys_buf_addr);
+	/* end */
+	mtk_regmap_write(afe->regmap,
+			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+			 memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set MSB to 33-bit */
+	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+			       1 << memif->data->msb_shift,
+			       msb_at_bit33 << memif->data->msb_shift);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+				       1 << memif->data->mono_shift,
+				       mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+			       memif->data->fs_maskbit << memif->data->fs_shift,
+			       fs << memif->data->fs_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+	unsigned int counter = runtime->period_size;
+	int fs;
+
+	dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (memif->data->enable_shift >= 0)
+			mtk_regmap_update_bits(afe->regmap,
+					       memif->data->enable_reg,
+					       1 << memif->data->enable_shift,
+					       1 << memif->data->enable_shift);
+
+		/* set irq counter */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+				       irq_data->irq_cnt_maskbit
+				       << irq_data->irq_cnt_shift,
+				       counter << irq_data->irq_cnt_shift);
+
+		/* set irq fs */
+		fs = afe->irq_fs(substream, runtime->rate);
+
+		if (fs < 0)
+			return -EINVAL;
+
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+				       irq_data->irq_fs_maskbit
+				       << irq_data->irq_fs_shift,
+				       fs << irq_data->irq_fs_shift);
+
+		/* enable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       1 << irq_data->irq_en_shift);
+
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+				       1 << memif->data->enable_shift, 0);
+		/* disable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       0 << irq_data->irq_en_shift);
+		/* and clear pending IRQ */
+		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+				 1 << irq_data->irq_clr_shift);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int hd_audio = 0;
+
+	/* set hd mode */
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hd_audio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hd_audio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		hd_audio = 1;
+		break;
+	default:
+		dev_err(afe->dev, "%s() error: unsupported format %d\n",
+			__func__, substream->runtime->format);
+		break;
+	}
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+			       1 << memif->data->hd_shift,
+			       hd_audio << memif->data->hd_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+	.startup	= mtk_afe_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mtk_afe_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+	int i;
+
+	mutex_lock(&afe->irq_alloc_lock);
+	for (i = 0; i < afe->irqs_size; ++i) {
+		if (afe->irqs[i].irq_occupyed == 0) {
+			afe->irqs[i].irq_occupyed = 1;
+			mutex_unlock(&afe->irq_alloc_lock);
+			return i;
+		}
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+	mutex_lock(&afe->irq_alloc_lock);
+	if (irq_id >= 0 && irq_id < afe->irqs_size) {
+		afe->irqs[irq_id].irq_occupyed = 0;
+		mutex_unlock(&afe->irq_alloc_lock);
+		return 0;
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	afe->suspended = true;
+	afe->runtime_suspend(dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	afe->runtime_resume(dev);
+
+	if (!afe->reg_back_up)
+		dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+				 afe->reg_back_up[i]);
+
+	afe->suspended = false;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 0000000..28cb178
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 0000000..82d439c
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+			 (struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	const struct mtk_base_memif_data *memif_data = memif->data;
+	struct regmap *regmap = afe->regmap;
+	struct device *dev = afe->dev;
+	int reg_ofs_base = memif_data->reg_ofs_base;
+	int reg_ofs_cur = memif_data->reg_ofs_cur;
+	unsigned int hw_ptr = 0, hw_base = 0;
+	int ret, pcm_ptr_bytes;
+
+	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+	if (ret || hw_ptr == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+	if (ret || hw_base == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+	.ioctl = snd_pcm_lib_ioctl,
+	.pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	size_t size;
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+	size = afe->mtk_afe_hardware->buffer_bytes_max;
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+	.ops = &mtk_afe_pcm_ops,
+	.pcm_new = mtk_afe_pcm_new,
+	.pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 0000000..a973fc9
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 0000000..3a78f6f
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_reg;
+	int fs_shift;
+	int fs_maskbit;
+	int mono_reg;
+	int mono_shift;
+	int enable_reg;
+	int enable_shift;
+	int hd_reg;
+	int hd_shift;
+	int msb_reg;
+	int msb_shift;
+	int agent_disable_reg;
+	int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+	int id;
+	int irq_cnt_reg;
+	int irq_cnt_shift;
+	int irq_cnt_maskbit;
+	int irq_fs_reg;
+	int irq_fs_shift;
+	int irq_fs_maskbit;
+	int irq_en_reg;
+	int irq_en_shift;
+	int irq_clr_reg;
+	int irq_clr_shift;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+	void __iomem *base_addr;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+	unsigned int const *reg_back_up_list;
+	unsigned int *reg_back_up;
+	unsigned int reg_back_up_list_num;
+
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	bool suspended;
+
+	struct mtk_base_afe_memif *memif;
+	int memif_size;
+	struct mtk_base_afe_irq *irqs;
+	int irqs_size;
+
+	const struct snd_pcm_hardware *mtk_afe_hardware;
+	int (*memif_fs)(struct snd_pcm_substream *substream,
+			unsigned int rate);
+	int (*irq_fs)(struct snd_pcm_substream *substream,
+		      unsigned int rate);
+
+	void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mtk_base_memif_data *data;
+	int irq_usage;
+	int const_irq;
+};
+
+struct mtk_base_afe_irq {
+	const struct mtk_base_irq_data *irq_data;
+	int irq_occupyed;
+};
+
+#endif
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:46 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:46 +0800
Subject: [alsa-devel] [PATCH v4 9/9] ASoC: mediatek: Add mt2701-cs42448 driver
 and config option.
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-10-git-send-email-garlic.tseng@mediatek.com>

Add machine driver and config option for MT2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                 |  21 ++
 sound/soc/mediatek/Makefile                |   1 +
 sound/soc/mediatek/mt2701/Makefile         |  19 ++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c | 422 +++++++++++++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ff1a419..0ecb785 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -7,6 +7,27 @@ config SND_SOC_MEDIATEK
 	  Select Y if you have such device.
 	  If unsure select "N".
 
+config SND_SOC_MT2701
+	tristate "ASoC support for Mediatek MT2701 chip"
+	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+	tristate "SND_SOC_MT2701_CS42448"
+	depends on SND_SOC_MT2701
+	select SND_SOC_CS42XX8_I2C
+	select SND_SOC_BT_SCO
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  with the CS42448 codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 3d893be..5133215 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
 
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 0000000..31c3d04
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644
index 0000000..0e48cb9
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,422 @@
+/*
+ * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian at mediatek.com>
+ *              Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+	int i2s1_in_mux;
+	int i2s1_in_mux_gpio_sel_1;
+	int i2s1_in_mux_gpio_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+	"ADC_SDOUT2",
+	"ADC_SDOUT3",
+	"I2S_IN_1",
+	"I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+	SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+	return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+		return 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 1:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 2:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	case 3:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	default:
+		dev_warn(card->dev, "%s invalid setting\n", __func__);
+	}
+
+	priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget
+			mt2701_cs42448_asoc_card_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+	SND_SOC_DAPM_MIC("AMIC", NULL),
+	SND_SOC_DAPM_LINE("Tuner In", NULL),
+	SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+	SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+	SOC_DAPM_PIN_SWITCH("AMIC"),
+	SOC_DAPM_PIN_SWITCH("Tuner In"),
+	SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+	SOC_DAPM_PIN_SWITCH("AUX In"),
+	SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+		     mt2701_cs42448_i2sin1_mux_get,
+		     mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+		.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+		.list = mt2701_cs42448_sampling_rates,
+		.mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+	int err;
+
+	err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 &mt2701_cs42448_constraints_rates);
+	if (err < 0) {
+		dev_err(substream->pcm->card->dev,
+			"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+			__func__, err);
+		return err;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+	.startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	/* codec slave, mt2701 master */
+	unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			    | SND_SOC_DAIFMT_CONT;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+	unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+	unsigned int div_bck_to_lrck = 64;
+
+	mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+
+	/* mt2701 mclk */
+	snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+	/* mt2701 bck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+	/* mt2701 lrck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+	/* mt2701 master */
+	snd_soc_dai_set_fmt(cpu_dai, fmt);
+
+	/* codec mclk */
+	snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+	/* codec slave */
+	snd_soc_dai_set_fmt(codec_dai, fmt);
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_be_ops = {
+	.hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+	DAI_LINK_FE_MULTI_CH_OUT,
+	DAI_LINK_FE_PCM0_IN,
+	DAI_LINK_FE_PCM1_IN,
+	DAI_LINK_FE_BT_OUT,
+	DAI_LINK_FE_BT_IN,
+	DAI_LINK_BE_I2S0,
+	DAI_LINK_BE_I2S1,
+	DAI_LINK_BE_I2S2,
+	DAI_LINK_BE_I2S3,
+	DAI_LINK_BE_MRG_BT,
+};
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+	/* FE */
+	[DAI_LINK_FE_MULTI_CH_OUT] = {
+		.name = "mt2701-cs42448-multi-ch-out",
+		.stream_name = "mt2701-cs42448-multi-ch-out",
+		.cpu_dai_name = "PCM_multi",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_PCM0_IN] = {
+		.name = "mt2701-cs42448-pcm0",
+		.stream_name = "mt2701-cs42448-pcm0-data-UL",
+		.cpu_dai_name = "PCM0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_PCM1_IN] = {
+		.name = "mt2701-cs42448-pcm1-data-UL",
+		.stream_name = "mt2701-cs42448-pcm1-data-UL",
+		.cpu_dai_name = "PCM1",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_BT_OUT] = {
+		.name = "mt2701-cs42448-pcm-BT-out",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_DL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_BT_IN] = {
+		.name = "mt2701-cs42448-pcm-BT-in",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_UL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	/* BE */
+	[DAI_LINK_BE_I2S0] = {
+		.name = "mt2701-cs42448-I2S0",
+		.cpu_dai_name = "I2S0",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S1] = {
+		.name = "mt2701-cs42448-I2S1",
+		.cpu_dai_name = "I2S1",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S2] = {
+		.name = "mt2701-cs42448-I2S2",
+		.cpu_dai_name = "I2S2",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S3] = {
+		.name = "mt2701-cs42448-I2S3",
+		.cpu_dai_name = "I2S3",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_MRG_BT] = {
+		.name = "mt2701-cs42448-MRG-BT",
+		.cpu_dai_name = "MRG BT",
+		.no_pcm = 1,
+		.codec_dai_name = "bt-sco-pcm",
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+	.name = "mt2701-cs42448",
+	.owner = THIS_MODULE,
+	.dai_link = mt2701_cs42448_dai_links,
+	.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+	.controls = mt2701_cs42448_controls,
+	.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+	.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+	int ret;
+	int i;
+	struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+	struct mt2701_cs42448_private *priv =
+		devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+			     GFP_KERNEL);
+	struct device *dev = &pdev->dev;
+
+	if (!priv)
+		return -ENOMEM;
+
+	platform_node = of_parse_phandle(pdev->dev.of_node,
+					 "mediatek,platform", 0);
+	if (!platform_node) {
+		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].platform_name)
+			continue;
+		mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
+	}
+
+	card->dev = dev;
+
+	codec_node = of_parse_phandle(pdev->dev.of_node,
+				      "mediatek,audio-codec", 0);
+	if (!codec_node) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].codec_name)
+			continue;
+		mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
+	}
+
+	codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+					     "mediatek,audio-codec-bt-mrg", 0);
+	if (!codec_node_bt_mrg) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec-bt-mrg' missing or invalid\n");
+		return -EINVAL;
+	}
+	mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+							= codec_node_bt_mrg;
+
+	ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+		return ret;
+	}
+
+	priv->i2s1_in_mux_gpio_sel_1 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+					"i2s1_in_mux_gpio_sel_1");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+	}
+
+	priv->i2s1_in_mux_gpio_sel_2 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+					"i2s1_in_mux_gpio_sel_2");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+	}
+	snd_soc_card_set_drvdata(card, priv);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret)
+		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+			__func__, ret);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+	{.compatible = "mediatek,mt2701-cs42448-machine",},
+	{}
+};
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+	.driver = {
+		.name = "mt2701-cs42448",
+		   #ifdef CONFIG_OF
+		   .of_match_table = mt2701_cs42448_machine_dt_match,
+		   #endif
+	},
+	.probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:42 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:42 +0800
Subject: [alsa-devel] [PATCH v4 5/9] ASoC: mediatek: add structure define and
 clock control for 2701
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-6-git-send-email-garlic.tseng@mediatek.com>

add structure define and clock control function for 2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c | 464 ++++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h |  38 ++
 sound/soc/mediatek/mt2701/mt2701-afe-common.h     | 181 +++++++++
 sound/soc/mediatek/mt2701/mt2701-reg.h            | 186 +++++++++
 4 files changed, 869 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 0000000..b815ecc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+	[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+	[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+	[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+	[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+	[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+	[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+	[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+	[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+	[MT2701_AUD_APLL_SEL] = "top_apll_sel",
+	[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+	[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+	[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+	[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+	[MT2701_AUD_AUDPLL] = "top_audpll",
+	[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+	[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+	[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+	[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+	[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+	[MT2701_AUD_CLK_26M] = "clk_26m",
+	[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+	[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+	[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+	[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+	[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+	[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+	[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+	[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+	[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+	[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+	[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+	[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+	[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+	[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+	[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+	[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+	[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+	[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+	[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+	[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+	[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+	[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+	[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+	[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i = 0;
+
+	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(aud_clks[i])) {
+			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+				 __func__, aud_clks[i]);
+			return PTR_ERR(aud_clks[i]);
+		}
+	}
+
+	return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+	int ret = 0;
+
+	ret = mt2701_turn_on_a1sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_a2sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_afe_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		mt2701_turn_off_a2sys_clock(afe);
+		return ret;
+	}
+
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON,
+			   AFE_DAC_CON0_AFE_ON);
+	regmap_write(afe->regmap, PWR2_TOP_CON,
+		     PWR2_TOP_CON_INIT_VAL);
+	regmap_write(afe->regmap, PWR1_ASM_CON1,
+		     PWR1_ASM_CON1_INIT_VAL);
+	regmap_write(afe->regmap, PWR2_ASM_CON1,
+		     PWR2_ASM_CON1_INIT_VAL);
+
+	return 0;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+	mt2701_turn_off_afe_clock(afe);
+	mt2701_turn_off_a1sys_clock(afe);
+	mt2701_turn_off_a2sys_clock(afe);
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+			aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+			   MT2701_AUD_AUD_MUX1_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+		goto A1SYS_CLK_AUD_48K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A1SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A1SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+			   MT2701_AUD_AUD_MUX2_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+		goto A2SYS_CLK_AUD_44K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A2SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A2SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	/* enable INFRA_SYS */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto AFE_AUD_INFRA_ERR;
+	}
+
+	/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+			     afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUDINTBUS],
+			aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_M_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+	return 0;
+
+AFE_AUD_ASM_M_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK,
+			   AUDIO_TOP_CON0_PDN_APLL_CK);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS,
+			   AUDIO_TOP_CON4_PDN_A1SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS,
+			   AUDIO_TOP_CON4_PDN_A2SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+	int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+	int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+	/* Set MCLK Kx_SRC_SEL(domain) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_clk_id], ret);
+
+	if (domain == 0) {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+	} else {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+	}
+	clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+	/* Set MCLK Kx_SRC_DIV(divider) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_div_id], ret);
+
+	ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+			aud_clks[aud_src_div_id], mclk, ret);
+	clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 0000000..6497d57
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 0000000..c77166e
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,181 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE	98304000
+#define MT2701_PLL_DOMAIN_1_RATE	90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+	MT2701_I2S_1,
+	MT2701_I2S_2,
+	MT2701_I2S_3,
+	MT2701_I2S_4,
+	MT2701_I2S_NUM,
+};
+
+enum {
+	MT2701_MEMIF_DL1,
+	MT2701_MEMIF_DL2,
+	MT2701_MEMIF_DL3,
+	MT2701_MEMIF_DL4,
+	MT2701_MEMIF_DL5,
+	MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_UL1,
+	MT2701_MEMIF_UL2,
+	MT2701_MEMIF_UL3,
+	MT2701_MEMIF_UL4,
+	MT2701_MEMIF_UL5,
+	MT2701_MEMIF_DLBT,
+	MT2701_MEMIF_ULBT,
+	MT2701_MEMIF_NUM,
+	MT2701_IO_I2S = MT2701_MEMIF_NUM,
+	MT2701_IO_2ND_I2S,
+	MT2701_IO_3RD_I2S,
+	MT2701_IO_4TH_I2S,
+	MT2701_IO_5TH_I2S,
+	MT2701_IO_6TH_I2S,
+	MT2701_IO_MRG,
+};
+
+enum {
+	MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ2,
+	MT2701_IRQ_ASYS_IRQ3,
+	MT2701_IRQ_ASYS_END,
+};
+
+enum {
+	DIV_ID_MCLK_TO_BCK,
+	DIV_ID_BCK_TO_LRCK,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+	MT2701_AUD_INFRA_SYS_AUDIO,
+	MT2701_AUD_AUD_MUX1_SEL,
+	MT2701_AUD_AUD_MUX2_SEL,
+	MT2701_AUD_AUD_MUX1_DIV,
+	MT2701_AUD_AUD_MUX2_DIV,
+	MT2701_AUD_AUD_48K_TIMING,
+	MT2701_AUD_AUD_44K_TIMING,
+	MT2701_AUD_AUDPLL_MUX_SEL,
+	MT2701_AUD_APLL_SEL,
+	MT2701_AUD_AUD1PLL_98M,
+	MT2701_AUD_AUD2PLL_90M,
+	MT2701_AUD_HADDS2PLL_98M,
+	MT2701_AUD_HADDS2PLL_294M,
+	MT2701_AUD_AUDPLL,
+	MT2701_AUD_AUDPLL_D4,
+	MT2701_AUD_AUDPLL_D8,
+	MT2701_AUD_AUDPLL_D16,
+	MT2701_AUD_AUDPLL_D24,
+	MT2701_AUD_AUDINTBUS,
+	MT2701_AUD_CLK_26M,
+	MT2701_AUD_SYSPLL1_D4,
+	MT2701_AUD_AUD_K1_SRC_SEL,
+	MT2701_AUD_AUD_K2_SRC_SEL,
+	MT2701_AUD_AUD_K3_SRC_SEL,
+	MT2701_AUD_AUD_K4_SRC_SEL,
+	MT2701_AUD_AUD_K5_SRC_SEL,
+	MT2701_AUD_AUD_K6_SRC_SEL,
+	MT2701_AUD_AUD_K1_SRC_DIV,
+	MT2701_AUD_AUD_K2_SRC_DIV,
+	MT2701_AUD_AUD_K3_SRC_DIV,
+	MT2701_AUD_AUD_K4_SRC_DIV,
+	MT2701_AUD_AUD_K5_SRC_DIV,
+	MT2701_AUD_AUD_K6_SRC_DIV,
+	MT2701_AUD_AUD_I2S1_MCLK,
+	MT2701_AUD_AUD_I2S2_MCLK,
+	MT2701_AUD_AUD_I2S3_MCLK,
+	MT2701_AUD_AUD_I2S4_MCLK,
+	MT2701_AUD_AUD_I2S5_MCLK,
+	MT2701_AUD_AUD_I2S6_MCLK,
+	MT2701_AUD_ASM_M_SEL,
+	MT2701_AUD_ASM_H_SEL,
+	MT2701_AUD_UNIVPLL2_D4,
+	MT2701_AUD_UNIVPLL2_D2,
+	MT2701_AUD_SYSPLL_D5,
+	MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AUDIO_TOP_CON4,
+	AUDIO_TOP_CON5,
+	ASYS_TOP_CON,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN2,
+	AFE_CONN3,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN22,
+	AFE_DAC_CON0,
+	AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+	int i2s_ctrl_reg;
+	int i2s_pwn_shift;
+	int i2s_asrc_fs_shift;
+	int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+	I2S_OUT,
+	I2S_IN,
+	I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+	int dai_id;
+	int mclk_rate;
+	int div_mclk_to_bck;
+	int div_bck_to_lrck;
+	int format;
+	snd_pcm_format_t stream_fmt;
+	int on[I2S_DIR_NUM];
+	int occupied[I2S_DIR_NUM];
+	const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+	struct clk *clocks[MT2701_CLOCK_NUM];
+	struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+	bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 0000000..bb62b1c
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON	(0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE		(0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK	(0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN	(0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS	(0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS	(0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN	(0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF	(0x1 << 25)
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN		(0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN	(0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY	(0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN	(0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE		(0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL		(0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN		(0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN	(0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK	(0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K	(0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS		(0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)	((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH		(0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN		(0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN		(0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE	(0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)	((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL		(0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL		(0xffffffff)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL		(0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON		(0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK		(0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE	(0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE	(0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK			(0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK		(0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)		((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK	(0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES		(0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS			(0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)		((x) << 8)
+#define ASYS_I2S_CON_RESET		(0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN		(0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE	(0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE		(0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)	((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX		(0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:40 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:40 +0800
Subject: [alsa-devel] [PATCH v4 3/9] ASoC: mediatek: let mt8173 use mediatek
 common structure
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-4-git-send-email-garlic.tseng@mediatek.com>

Modify mt8173 driver implementation to use common structure.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                    |  11 +
 sound/soc/mediatek/Makefile                   |   3 +-
 sound/soc/mediatek/common/Makefile            |  17 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
 sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 679 +++++++++++---------------
 5 files changed, 327 insertions(+), 425 deletions(-)
 create mode 100644 sound/soc/mediatek/common/Makefile

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ae9f664..ff1a419 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,16 @@
+config SND_SOC_MEDIATEK
+	tristate "SND_SOC_MEDIATEK"
+	depends on ARCH_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
 	help
 	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
@@ -49,3 +59,4 @@ config SND_SOC_MT8173_RT5650_RT5676
 	  with the RT5650 and RT5676 codecs.
 	  Select Y if you have such device.
 	  If unsure select "N".
+
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 240dfc70..3d893be 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,3 @@
-# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
+
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 0000000..0fcd921
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
+
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
index 8f2936d..9a4837c 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -46,14 +46,13 @@ enum {
 };
 
 enum {
-	MT8173_AFE_IRQ_1,
-	MT8173_AFE_IRQ_2,
-	MT8173_AFE_IRQ_3,
-	MT8173_AFE_IRQ_4,
-	MT8173_AFE_IRQ_5,
-	MT8173_AFE_IRQ_6,
-	MT8173_AFE_IRQ_7,
-	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_DL1,
+	MT8173_AFE_IRQ_DL2,
+	MT8173_AFE_IRQ_VUL,
+	MT8173_AFE_IRQ_DAI,
+	MT8173_AFE_IRQ_AWB,
+	MT8173_AFE_IRQ_MOD_DAI,
+	MT8173_AFE_IRQ_HDMI,
 	MT8173_AFE_IRQ_NUM,
 };
 
@@ -71,31 +70,4 @@ enum {
 	MT8173_CLK_NUM
 };
 
-struct mt8173_afe;
-struct snd_pcm_substream;
-
-struct mt8173_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mt8173_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mt8173_afe_memif_data *data;
-	const struct mt8173_afe_irq_data *irqdata;
-};
-
 #endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 4fc52bc..8a643a3 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -25,6 +25,9 @@
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1		0x0548
 #define AFE_TDM_CON2		0x054c
 
-#define AFE_BASE_END_OFFSET	8
 #define AFE_IRQ_STATUS_BITS	0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -152,15 +154,8 @@ static const unsigned int mt8173_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mt8173_afe {
-	/* address for ioremap audio hardware register */
-	void __iomem *base_addr;
-	struct device *dev;
-	struct regmap *regmap;
-	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+struct mt8173_afe_private {
 	struct clk *clocks[MT8173_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
-	bool suspended;
 };
 
 static const struct snd_pcm_hardware mt8173_afe_hardware = {
@@ -174,53 +169,6 @@ static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mt8173_afe_pcm_pointer
-			 (struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int hw_ptr;
-	int ret;
-
-	ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-	if (ret || hw_ptr == 0) {
-		dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-		hw_ptr = memif->phys_buf_addr;
-	}
-
-	return bytes_to_frames(substream->runtime,
-			       hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
-	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mt8173_afe_pcm_pointer,
-};
-
-static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-	size_t size;
-	struct snd_card *card = rtd->card->snd_card;
-	struct snd_pcm *pcm = rtd->pcm;
-
-	size = mt8173_afe_hardware.buffer_bytes_max;
-
-	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-						     card->dev, size, size);
-}
-
-static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
-{
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
-	.ops = &mt8173_afe_pcm_ops,
-	.pcm_new = mt8173_afe_pcm_new,
-	.pcm_free = mt8173_afe_pcm_free,
-};
-
 struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
@@ -253,7 +201,7 @@ static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 	return -EINVAL;
 }
 
-static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
 	unsigned int val;
 	int fs = mt8173_afe_i2s_fs(rate);
@@ -281,7 +229,7 @@ static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,7 +244,7 @@ static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
 				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
@@ -319,7 +267,7 @@ static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
 				    struct clk *m_ck, unsigned int mck_rate,
 				    struct clk *b_ck, unsigned int bck_rate)
 {
@@ -343,7 +291,7 @@ static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
 					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
@@ -356,7 +304,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
 				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -370,7 +318,7 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
@@ -386,12 +334,13 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
 				 runtime->rate * 256, NULL, 0);
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
 				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
 	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
@@ -407,13 +356,14 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return 0;
 
-	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				    afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
@@ -421,13 +371,14 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
 				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return;
 
-	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				     afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				     afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
 static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
@@ -435,12 +386,14 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
 	unsigned int val;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
 				 runtime->rate * 128,
-				 afe->clocks[MT8173_CLK_I2S3_B],
+				 afe_priv->clocks[MT8173_CLK_I2S3_B],
 				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
@@ -496,7 +449,7 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -508,10 +461,14 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
 		regmap_write(afe->regmap, AFE_HDMI_CONN0,
-			     AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-			     AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-			     AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-			     AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+				 AFE_HDMI_CONN0_O30_I30 |
+				 AFE_HDMI_CONN0_O31_I31 |
+				 AFE_HDMI_CONN0_O32_I34 |
+				 AFE_HDMI_CONN0_O33_I35 |
+				 AFE_HDMI_CONN0_O34_I32 |
+				 AFE_HDMI_CONN0_O35_I33 |
+				 AFE_HDMI_CONN0_O36_I36 |
+				 AFE_HDMI_CONN0_O37_I37);
 
 		/* enable Out control */
 		regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -531,224 +488,46 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int ret;
-
-	memif->substream = substream;
-
-	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
-
-	/*
-	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-	 * smaller than period_size due to AFE's internal buffer.
-	 * This easily leads to overrun when avail_min is period_size.
-	 * One more period can hold the possible unread buffer.
-	 */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		ret = snd_pcm_hw_constraint_minmax(runtime,
-						   SNDRV_PCM_HW_PARAM_PERIODS,
-						   3,
-						   mt8173_afe_hardware.periods_max);
-		if (ret < 0) {
-			dev_err(afe->dev, "hw_constraint_minmax failed\n");
-			return ret;
-		}
-	}
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-	return ret;
-}
-
-static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				     struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-	memif->substream = NULL;
-}
-
-static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				     struct snd_pcm_hw_params *params,
-				     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int msb_at_bit33 = 0;
-	int ret;
-
-	dev_dbg(afe->dev,
-		"%s period = %u, rate= %u, channels=%u\n",
-		__func__, params_period_size(params), params_rate(params),
-		params_channels(params));
-
-	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-	if (ret < 0)
-		return ret;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int fs;
 
-	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-	memif->buffer_size = substream->runtime->dma_bytes;
-
-	/* start */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base, memif->phys_buf_addr);
-	/* end */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-		     memif->phys_buf_addr + memif->buffer_size - 1);
-
-	/* set MSB to 33-bit */
-	regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-			   1 << memif->data->msb_shift,
-			   msb_at_bit33 << memif->data->msb_shift);
-
-	/* set channel */
-	if (memif->data->mono_shift >= 0) {
-		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   1 << memif->data->mono_shift,
-				   mono << memif->data->mono_shift);
-	}
-
-	/* set rate */
-	if (memif->data->fs_shift < 0)
-		return 0;
 	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
 	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
-		unsigned int val;
-
-		switch (params_rate(params)) {
+		switch (rate) {
 		case 8000:
-			val = 0;
+			fs = 0;
 			break;
 		case 16000:
-			val = 1;
+			fs = 1;
 			break;
 		case 32000:
-			val = 2;
+			fs = 2;
 			break;
 		default:
 			return -EINVAL;
 		}
-
-		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-		else
-			regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-
 	} else {
-		int fs = mt8173_afe_i2s_fs(params_rate(params));
-
-		if (fs < 0)
-			return -EINVAL;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   0xf << memif->data->fs_shift,
-				   fs << memif->data->fs_shift);
+		fs = mt8173_afe_i2s_fs(rate);
 	}
-
-	return 0;
+	return fs;
 }
 
-static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-	return snd_pcm_lib_free_pages(substream);
+	return mt8173_afe_i2s_fs(rate);
 }
 
-static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int counter = runtime->period_size;
-
-	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift,
-					   1 << memif->data->enable_shift);
-
-		/* set irq counter */
-		regmap_update_bits(afe->regmap,
-				   memif->data->irq_reg_cnt,
-				   0x3ffff << memif->data->irq_cnt_shift,
-				   counter << memif->data->irq_cnt_shift);
-
-		/* set irq fs */
-		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mt8173_afe_i2s_fs(runtime->rate);
-
-			if (fs < 0)
-				return -EINVAL;
-
-			regmap_update_bits(afe->regmap,
-					   AFE_IRQ_MCU_CON,
-					   0xf << memif->data->irq_fs_shift,
-					   fs << memif->data->irq_fs_shift);
-		}
-		/* enable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   1 << memif->data->irq_en_shift);
-
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift, 0);
-		/* disable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   0 << memif->data->irq_en_shift);
-		/* and clear pending IRQ */
-		regmap_write(afe->regmap, AFE_IRQ_CLR,
-			     1 << memif->data->irq_clr_shift);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
-	.startup	= mt8173_afe_dais_startup,
-	.shutdown	= mt8173_afe_dais_shutdown,
-	.hw_params	= mt8173_afe_dais_hw_params,
-	.hw_free	= mt8173_afe_dais_hw_free,
-	.trigger	= mt8173_afe_dais_trigger,
-};
-
 /* BE DAIs */
 static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
 	.startup	= mt8173_afe_i2s_startup,
@@ -761,56 +540,15 @@ static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
 	.shutdown	= mt8173_afe_hdmi_shutdown,
 	.prepare	= mt8173_afe_hdmi_prepare,
 	.trigger	= mt8173_afe_hdmi_trigger,
-
 };
 
-static int mt8173_afe_runtime_suspend(struct device *dev);
-static int mt8173_afe_runtime_resume(struct device *dev);
-
-static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
-			    &afe->backup_regs[i]);
-
-	afe->suspended = true;
-	mt8173_afe_runtime_suspend(afe->dev);
-	return 0;
-}
-
-static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i = 0;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-		return 0;
-
-	mt8173_afe_runtime_resume(afe->dev);
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
-			     afe->backup_regs[i]);
-
-	afe->suspended = false;
-	return 0;
-}
-
 static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
 		.id = MT8173_AFE_MEMIF_DL1,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -818,12 +556,12 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
 		.id = MT8173_AFE_MEMIF_VUL,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -831,7 +569,7 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
@@ -860,8 +598,8 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	{
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -872,7 +610,7 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
@@ -978,105 +716,222 @@ static const char *aud_clks[MT8173_CLK_NUM] = {
 	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
 		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 21,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 1,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 0,
-		.irq_fs_shift = 4,
-		.irq_clr_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 0,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DL2",
 		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 22,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 2,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 2,
-		.irq_fs_shift = 16,
-		.irq_clr_shift = 2,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "VUL",
 		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 27,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 3,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 1,
-		.irq_fs_shift = 8,
-		.irq_clr_shift = 1,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 6,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DAI",
 		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON0,
 		.fs_shift = 24,
+		.fs_maskbit = 0x3,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 4,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 5,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "AWB",
 		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 24,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 6,
-		.irq_reg_cnt = AFE_IRQ_CNT7,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 14,
-		.irq_fs_shift = 24,
-		.irq_clr_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 3,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "MOD_DAI",
 		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 30,
+		.fs_maskbit = 0x3,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 30,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 7,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 4,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
 		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = -1,
 		.enable_shift = -1,
-		.irq_reg_cnt = AFE_IRQ_CNT5,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 8,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+	{
+		.id = MT8173_AFE_IRQ_DL1,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8173_AFE_IRQ_DL2,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 2,
+
+	}, {
+		.id = MT8173_AFE_IRQ_VUL,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_AWB,
+		.irq_cnt_reg = AFE_IRQ_CNT7,
 		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_HDMI,
+		.irq_cnt_reg = AFE_IRQ_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
 		.irq_en_shift = 12,
+		.irq_fs_reg = -1,
 		.irq_fs_shift = -1,
+		.irq_fs_maskbit = -1,
+		.irq_clr_reg = AFE_IRQ_CLR,
 		.irq_clr_shift = 4,
-		.msb_shift = 8,
 	},
 };
 
@@ -1090,7 +945,7 @@ static const struct regmap_config mt8173_afe_regmap_config = {
 
 static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mt8173_afe *afe = dev_id;
+	struct mtk_base_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1102,9 +957,15 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 	}
 
 	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
-		struct mt8173_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *irq;
 
-		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+		if (memif->irq_usage < 0)
+			continue;
+
+		irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
 			continue;
 
 		snd_pcm_period_elapsed(memif->substream);
@@ -1112,14 +973,16 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 
 err_irq:
 	/* clear irq */
-	regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+	regmap_write(afe->regmap, AFE_IRQ_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
 
 	return IRQ_HANDLED;
 }
 
 static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1127,44 +990,46 @@ static int mt8173_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
 static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1181,35 +1046,37 @@ static int mt8173_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
+
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
 	size_t i;
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-		afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-		if (IS_ERR(afe->clocks[i])) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(afe_priv->clocks[i])) {
 			dev_err(afe->dev, "%s devm_clk_get %s fail\n",
 				__func__, aud_clks[i]);
-			return PTR_ERR(afe->clocks[i]);
+			return PTR_ERR(afe_priv->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
@@ -1217,7 +1084,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mt8173_afe *afe;
+	struct mtk_base_afe *afe;
+	struct mt8173_afe_private *afe_priv;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1228,6 +1096,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 	if (!afe)
 		return -ENOMEM;
 
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe_priv)
+		return -ENOMEM;
+
 	afe->dev = &pdev->dev;
 
 	irq_id = platform_get_irq(pdev, 0);
@@ -1259,8 +1133,30 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
+	/* memif % irq initialize*/
+	afe->memif_size = MT8173_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8173_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++) {
 		afe->memif[i].data = &memif_data[i];
+		afe->irqs[i].irq_data = &irq_data[i];
+		afe->irqs[i].irq_occupyed = true;
+		afe->memif[i].irq_usage = i;
+		afe->memif[i].const_irq = 1;
+	}
+
+	afe->mtk_afe_hardware = &mt8173_afe_hardware;
+	afe->memif_fs = mt8173_memif_fs;
+	afe->irq_fs = mt8173_irq_fs;
 
 	platform_set_drvdata(pdev, afe);
 
@@ -1271,7 +1167,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
+	afe->reg_back_up_list = mt8173_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+	afe->runtime_resume = mt8173_afe_runtime_resume;
+	afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:38 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:38 +0800
Subject: [alsa-devel] [PATCH v4 1/9] ASoC: mediatek: Refine mt8173 driver and
 change config option
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-2-git-send-email-garlic.tseng@mediatek.com>

move mt8173 driver to another folder and add prefix.
add config option SND_SOC_MT8173

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                         |  14 +-
 sound/soc/mediatek/Makefile                        |   9 +-
 sound/soc/mediatek/mt8173/Makefile                 |   7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      | 101 +++++
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     | 494 ++++++++++-----------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |   4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |   2 +-
 sound/soc/mediatek/mtk-afe-common.h                | 101 -----
 10 files changed, 367 insertions(+), 369 deletions(-)
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (66%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3abf51c..ae9f664 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,15 @@
-config SND_SOC_MEDIATEK
-	tristate "ASoC support for Mediatek chip"
+config SND_SOC_MT8173
+	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
 	help
-	  This adds ASoC platform driver support for Mediatek chip
+	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
 	  Select Y if you have such device.
 	  Ex: MT8173
 
 config SND_SOC_MT8173_MAX98090
 	tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_MAX98090
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -19,7 +19,7 @@ config SND_SOC_MT8173_MAX98090
 
 config SND_SOC_MT8173_RT5650
 	tristate "ASoC Audio driver for MT8173 with RT5650 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -29,7 +29,7 @@ config SND_SOC_MT8173_RT5650
 
 config SND_SOC_MT8173_RT5650_RT5514
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5514
 	help
@@ -40,7 +40,7 @@ config SND_SOC_MT8173_RT5650_RT5514
 
 config SND_SOC_MT8173_RT5650_RT5676
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5677
 	select SND_SOC_HDMI_CODEC
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index d486860..240dfc70 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,7 +1,2 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 0000000..0357b27
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,7 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644
index 0000000..8f2936d
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,101 @@
+/*
+ * mt8173_afe_common.h  --  Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen at mediatek.com>
+ *             Sascha Hauer <s.hauer at pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+	MT8173_AFE_MEMIF_DL1,
+	MT8173_AFE_MEMIF_DL2,
+	MT8173_AFE_MEMIF_VUL,
+	MT8173_AFE_MEMIF_DAI,
+	MT8173_AFE_MEMIF_AWB,
+	MT8173_AFE_MEMIF_MOD_DAI,
+	MT8173_AFE_MEMIF_HDMI,
+	MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM2,
+	MT8173_AFE_IO_PMIC,
+	MT8173_AFE_IO_I2S,
+	MT8173_AFE_IO_2ND_I2S,
+	MT8173_AFE_IO_HW_GAIN1,
+	MT8173_AFE_IO_HW_GAIN2,
+	MT8173_AFE_IO_MRG_O,
+	MT8173_AFE_IO_MRG_I,
+	MT8173_AFE_IO_DAIBT,
+	MT8173_AFE_IO_HDMI,
+};
+
+enum {
+	MT8173_AFE_IRQ_1,
+	MT8173_AFE_IRQ_2,
+	MT8173_AFE_IRQ_3,
+	MT8173_AFE_IRQ_4,
+	MT8173_AFE_IRQ_5,
+	MT8173_AFE_IRQ_6,
+	MT8173_AFE_IRQ_7,
+	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+	MT8173_CLK_INFRASYS_AUD,
+	MT8173_CLK_TOP_PDN_AUD,
+	MT8173_CLK_TOP_PDN_AUD_BUS,
+	MT8173_CLK_I2S0_M,
+	MT8173_CLK_I2S1_M,
+	MT8173_CLK_I2S2_M,
+	MT8173_CLK_I2S3_M,
+	MT8173_CLK_I2S3_B,
+	MT8173_CLK_BCK0,
+	MT8173_CLK_BCK1,
+	MT8173_CLK_NUM
+};
+
+struct mt8173_afe;
+struct snd_pcm_substream;
+
+struct mt8173_afe_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_shift;
+	int mono_shift;
+	int enable_shift;
+	int irq_reg_cnt;
+	int irq_cnt_shift;
+	int irq_en_shift;
+	int irq_fs_shift;
+	int irq_clr_shift;
+	int msb_shift;
+};
+
+struct mt8173_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mt8173_afe_memif_data *data;
+	const struct mt8173_afe_irq_data *irqdata;
+};
+
+#endif
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
similarity index 66%
rename from sound/soc/mediatek/mtk-afe-pcm.c
rename to sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 793d7e2..4fc52bc 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1,5 +1,5 @@
 /*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
  *
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Koro Chen <koro.chen at mediatek.com>
@@ -24,7 +24,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
-#include "mtk-afe-common.h"
+#include "mt8173-afe-common.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -135,7 +135,7 @@ enum afe_tdm_ch_start {
 	AFE_TDM_CH_ZERO,
 };
 
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
 	AUDIO_TOP_CON0,
 	AFE_CONN1,
 	AFE_CONN2,
@@ -152,18 +152,18 @@ static const unsigned int mtk_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mtk_afe {
+struct mt8173_afe {
 	/* address for ioremap audio hardware register */
 	void __iomem *base_addr;
 	struct device *dev;
 	struct regmap *regmap;
-	struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
-	struct clk *clocks[MTK_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
+	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+	struct clk *clocks[MT8173_CLK_NUM];
+	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
 	bool suspended;
 };
 
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 		 SNDRV_PCM_INFO_MMAP_VALID),
 	.buffer_bytes_max = 256 * 1024,
@@ -174,12 +174,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
+static snd_pcm_uframes_t mt8173_afe_pcm_pointer
 			 (struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int hw_ptr;
 	int ret;
 
@@ -193,40 +193,40 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
 			       hw_ptr - memif->phys_buf_addr);
 }
 
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
 	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mtk_afe_pcm_pointer,
+	.pointer = mt8173_afe_pcm_pointer,
 };
 
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	size_t size;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_pcm *pcm = rtd->pcm;
 
-	size = mtk_afe_hardware.buffer_bytes_max;
+	size = mt8173_afe_hardware.buffer_bytes_max;
 
 	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						     card->dev, size, size);
 }
 
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
 {
 	snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
-	.ops = &mtk_afe_pcm_ops,
-	.pcm_new = mtk_afe_pcm_new,
-	.pcm_free = mtk_afe_pcm_free,
+static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
+	.ops = &mt8173_afe_pcm_ops,
+	.pcm_new = mt8173_afe_pcm_new,
+	.pcm_free = mt8173_afe_pcm_free,
 };
 
-struct mtk_afe_rate {
+struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
 };
 
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
 	{ .rate = 8000, .regvalue = 0 },
 	{ .rate = 11025, .regvalue = 1 },
 	{ .rate = 12000, .regvalue = 2 },
@@ -242,21 +242,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
 	{ .rate = 192000, .regvalue = 14 },
 };
 
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
-		if (mtk_afe_i2s_rates[i].rate == sample_rate)
-			return mtk_afe_i2s_rates[i].regvalue;
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+		if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+			return mt8173_afe_i2s_rates[i].regvalue;
 
 	return -EINVAL;
 }
 
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 {
 	unsigned int val;
-	int fs = mtk_afe_i2s_fs(rate);
+	int fs = mt8173_afe_i2s_fs(rate);
 
 	if (fs < 0)
 		return -EINVAL;
@@ -281,7 +281,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,8 +296,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
-				    struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
 
@@ -319,9 +319,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
-				 struct clk *m_ck, unsigned int mck_rate,
-				 struct clk *b_ck, unsigned int bck_rate)
+static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+				    struct clk *m_ck, unsigned int mck_rate,
+				    struct clk *b_ck, unsigned int bck_rate)
 {
 	int ret;
 
@@ -343,8 +343,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
-				      struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
 		clk_disable_unprepare(m_ck);
@@ -352,11 +352,11 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
 		clk_disable_unprepare(b_ck);
 }
 
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -366,84 +366,82 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
-				 struct snd_soc_dai *dai)
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_set_i2s_enable(afe, false);
+	mt8173_afe_set_i2s_enable(afe, false);
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
 }
 
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	int ret;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
-			      NULL, 0);
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
-			      NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+				 runtime->rate * 256, NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
-	ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
 	if (ret)
 		return ret;
 
-	mtk_afe_set_i2s_enable(afe, true);
+	mt8173_afe_set_i2s_enable(afe, true);
 
 	return 0;
 }
 
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
 
-	mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				 afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				    afe->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				  afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				     afe->clocks[MT8173_CLK_I2S3_B]);
 }
 
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	unsigned int val;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
-			      afe->clocks[MTK_CLK_I2S3_B],
-			      runtime->rate * runtime->channels * 32);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				 runtime->rate * 128,
+				 afe->clocks[MT8173_CLK_I2S3_B],
+				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
 	      AFE_TDM_CON1_LRCK_INV |
@@ -494,11 +492,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -540,18 +538,18 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 	}
 }
 
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int ret;
 
 	memif->substream = substream;
 
-	snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
 
 	/*
 	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
@@ -563,7 +561,7 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 		ret = snd_pcm_hw_constraint_minmax(runtime,
 						   SNDRV_PCM_HW_PARAM_PERIODS,
 						   3,
-						   mtk_afe_hardware.periods_max);
+						   mt8173_afe_hardware.periods_max);
 		if (ret < 0) {
 			dev_err(afe->dev, "hw_constraint_minmax failed\n");
 			return ret;
@@ -576,23 +574,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 
 	memif->substream = NULL;
 }
 
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params,
-				  struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int msb_at_bit33 = 0;
 	int ret;
 
@@ -634,8 +632,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	/* set rate */
 	if (memif->data->fs_shift < 0)
 		return 0;
-	if (memif->data->id == MTK_AFE_MEMIF_DAI ||
-	    memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
+	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
 		unsigned int val;
 
 		switch (params_rate(params)) {
@@ -652,7 +650,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 			return -EINVAL;
 		}
 
-		if (memif->data->id == MTK_AFE_MEMIF_DAI)
+		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
 			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
 					   0x3 << memif->data->fs_shift,
 					   val << memif->data->fs_shift);
@@ -662,7 +660,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 					   val << memif->data->fs_shift);
 
 	} else {
-		int fs = mtk_afe_i2s_fs(params_rate(params));
+		int fs = mt8173_afe_i2s_fs(params_rate(params));
 
 		if (fs < 0)
 			return -EINVAL;
@@ -675,19 +673,19 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	return snd_pcm_lib_free_pages(substream);
 }
 
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int counter = runtime->period_size;
 
 	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
@@ -708,7 +706,7 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set irq fs */
 		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mtk_afe_i2s_fs(runtime->rate);
+			int fs = mt8173_afe_i2s_fs(runtime->rate);
 
 			if (fs < 0)
 				return -EINVAL;
@@ -743,76 +741,76 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 }
 
 /* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
-	.startup	= mtk_afe_dais_startup,
-	.shutdown	= mtk_afe_dais_shutdown,
-	.hw_params	= mtk_afe_dais_hw_params,
-	.hw_free	= mtk_afe_dais_hw_free,
-	.trigger	= mtk_afe_dais_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
+	.startup	= mt8173_afe_dais_startup,
+	.shutdown	= mt8173_afe_dais_shutdown,
+	.hw_params	= mt8173_afe_dais_hw_params,
+	.hw_free	= mt8173_afe_dais_hw_free,
+	.trigger	= mt8173_afe_dais_trigger,
 };
 
 /* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
-	.startup	= mtk_afe_i2s_startup,
-	.shutdown	= mtk_afe_i2s_shutdown,
-	.prepare	= mtk_afe_i2s_prepare,
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+	.startup	= mt8173_afe_i2s_startup,
+	.shutdown	= mt8173_afe_i2s_shutdown,
+	.prepare	= mt8173_afe_i2s_prepare,
 };
 
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
-	.startup	= mtk_afe_hdmi_startup,
-	.shutdown	= mtk_afe_hdmi_shutdown,
-	.prepare	= mtk_afe_hdmi_prepare,
-	.trigger	= mtk_afe_hdmi_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+	.startup	= mt8173_afe_hdmi_startup,
+	.shutdown	= mt8173_afe_hdmi_shutdown,
+	.prepare	= mt8173_afe_hdmi_prepare,
+	.trigger	= mt8173_afe_hdmi_trigger,
 
 };
 
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
+static int mt8173_afe_runtime_suspend(struct device *dev);
+static int mt8173_afe_runtime_resume(struct device *dev);
 
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_read(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
 			    &afe->backup_regs[i]);
 
 	afe->suspended = true;
-	mtk_afe_runtime_suspend(afe->dev);
+	mt8173_afe_runtime_suspend(afe->dev);
 	return 0;
 }
 
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i = 0;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
 		return 0;
 
-	mtk_afe_runtime_resume(afe->dev);
+	mt8173_afe_runtime_resume(afe->dev);
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_write(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
 			     afe->backup_regs[i]);
 
 	afe->suspended = false;
 	return 0;
 }
 
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
-		.id = MTK_AFE_MEMIF_DL1,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_DL1,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -820,12 +818,12 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
-		.id = MTK_AFE_MEMIF_VUL,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_VUL,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -833,11 +831,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
-		.id = MTK_AFE_IO_I2S,
+		.id = MT8173_AFE_IO_I2S,
 		.playback = {
 			.stream_name = "I2S Playback",
 			.channels_min = 1,
@@ -852,18 +850,18 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_i2s_ops,
+		.ops = &mt8173_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
 };
 
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	/* FE DAIs */
 	{
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_HDMI,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -874,11 +872,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
-		.id = MTK_AFE_IO_HDMI,
+		.id = MT8173_AFE_IO_HDMI,
 		.playback = {
 			.stream_name = "HDMIO Playback",
 			.channels_min = 2,
@@ -889,29 +887,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_hdmi_ops,
+		.ops = &mt8173_afe_hdmi_ops,
 	},
 };
 
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
 	/* inter-connections */
 	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -921,16 +919,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
 	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+			   mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
 	SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+			   mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
 	SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+			   mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
 	SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+			   mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
 };
 
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
 	{"I05", NULL, "DL1"},
 	{"I06", NULL, "DL1"},
 	{"I2S Playback", NULL, "O03"},
@@ -949,41 +947,41 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
 	{ "O10", "I04 Switch", "I04" },
 };
 
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
 	{"HDMIO Playback", NULL, "HDMI"},
 };
 
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
-	.name = "mtk-afe-pcm-dai",
-	.dapm_widgets = mtk_afe_pcm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
-	.dapm_routes = mtk_afe_pcm_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+	.name = "mt8173-afe-pcm-dai",
+	.dapm_widgets = mt8173_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+	.dapm_routes = mt8173_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
 };
 
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
-	.name = "mtk-afe-hdmi-dai",
-	.dapm_routes = mtk_afe_hdmi_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+	.name = "mt8173-afe-hdmi-dai",
+	.dapm_routes = mt8173_afe_hdmi_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
 };
 
-static const char *aud_clks[MTK_CLK_NUM] = {
-	[MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
-	[MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
-	[MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
-	[MTK_CLK_I2S0_M] =  "i2s0_m",
-	[MTK_CLK_I2S1_M] =  "i2s1_m",
-	[MTK_CLK_I2S2_M] =  "i2s2_m",
-	[MTK_CLK_I2S3_M] =  "i2s3_m",
-	[MTK_CLK_I2S3_B] =  "i2s3_b",
-	[MTK_CLK_BCK0] =  "bck0",
-	[MTK_CLK_BCK1] =  "bck1",
+static const char *aud_clks[MT8173_CLK_NUM] = {
+	[MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+	[MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+	[MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+	[MT8173_CLK_I2S0_M] =  "i2s0_m",
+	[MT8173_CLK_I2S1_M] =  "i2s1_m",
+	[MT8173_CLK_I2S2_M] =  "i2s2_m",
+	[MT8173_CLK_I2S3_M] =  "i2s3_m",
+	[MT8173_CLK_I2S3_B] =  "i2s3_b",
+	[MT8173_CLK_BCK0] =  "bck0",
+	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
-		.id = MTK_AFE_MEMIF_DL1,
+		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
 		.fs_shift = 0,
@@ -997,7 +995,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 0,
 	}, {
 		.name = "DL2",
-		.id = MTK_AFE_MEMIF_DL2,
+		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
 		.fs_shift = 4,
@@ -1011,7 +1009,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 1,
 	}, {
 		.name = "VUL",
-		.id = MTK_AFE_MEMIF_VUL,
+		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
 		.fs_shift = 16,
@@ -1025,7 +1023,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 6,
 	}, {
 		.name = "DAI",
-		.id = MTK_AFE_MEMIF_DAI,
+		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
 		.fs_shift = 24,
@@ -1039,7 +1037,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 5,
 	}, {
 		.name = "AWB",
-		.id = MTK_AFE_MEMIF_AWB,
+		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
 		.fs_shift = 12,
@@ -1053,7 +1051,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 3,
 	}, {
 		.name = "MOD_DAI",
-		.id = MTK_AFE_MEMIF_MOD_DAI,
+		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
 		.fs_shift = 30,
@@ -1067,7 +1065,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 4,
 	}, {
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
+		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
 		.fs_shift = -1,
@@ -1082,7 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 	},
 };
 
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -1090,9 +1088,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
 	.cache_type = REGCACHE_NONE,
 };
 
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mtk_afe *afe = dev_id;
+	struct mt8173_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1103,8 +1101,8 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
 		goto err_irq;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
-		struct mtk_afe_memif *memif = &afe->memif[i];
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+		struct mt8173_afe_memif *memif = &afe->memif[i];
 
 		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
 			continue;
@@ -1119,9 +1117,9 @@ err_irq:
 	return IRQ_HANDLED;
 }
 
-static int mtk_afe_runtime_suspend(struct device *dev)
+static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1129,45 +1127,44 @@ static int mtk_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
-static int mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1184,23 +1181,22 @@ static int mtk_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
-
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
 {
 	size_t i;
 
@@ -1212,16 +1208,16 @@ static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
 			return PTR_ERR(afe->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mtk_afe *afe;
+	struct mt8173_afe *afe;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1239,7 +1235,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
 		return -ENXIO;
 	}
-	ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+	ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
 			       0, "Afe_ISR_Handle", (void *)afe);
 	if (ret) {
 		dev_err(afe->dev, "could not request_irq\n");
@@ -1252,48 +1248,48 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		return PTR_ERR(afe->base_addr);
 
 	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
-		&mtk_afe_regmap_config);
+		&mt8173_afe_regmap_config);
 	if (IS_ERR(afe->regmap))
 		return PTR_ERR(afe->regmap);
 
 	/* initial audio related clock */
-	ret = mtk_afe_init_audio_clk(afe);
+	ret = mt8173_afe_init_audio_clk(afe);
 	if (ret) {
-		dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+		dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
 		return ret;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
 		afe->memif[i].data = &memif_data[i];
 
 	platform_set_drvdata(pdev, afe);
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = mtk_afe_runtime_resume(&pdev->dev);
+		ret = mt8173_afe_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_pcm_dai_component,
-					 mtk_afe_pcm_dais,
-					 ARRAY_SIZE(mtk_afe_pcm_dais));
+					 &mt8173_afe_pcm_dai_component,
+					 mt8173_afe_pcm_dais,
+					 ARRAY_SIZE(mt8173_afe_pcm_dais));
 	if (ret)
 		goto err_platform;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_hdmi_dai_component,
-					 mtk_afe_hdmi_dais,
-					 ARRAY_SIZE(mtk_afe_hdmi_dais));
+					 &mt8173_afe_hdmi_dai_component,
+					 mt8173_afe_hdmi_dais,
+					 ARRAY_SIZE(mt8173_afe_hdmi_dais));
 	if (ret)
 		goto err_comp;
 
-	dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+	dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
 	return 0;
 
 err_comp:
@@ -1305,38 +1301,38 @@ err_pm_disable:
 	return ret;
 }
 
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		mtk_afe_runtime_suspend(&pdev->dev);
+		mt8173_afe_runtime_suspend(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 	snd_soc_unregister_platform(&pdev->dev);
 	return 0;
 }
 
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
 	{ .compatible = "mediatek,mt8173-afe-pcm", },
 	{ }
 };
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
 
-static const struct dev_pm_ops mtk_afe_pm_ops = {
-	SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
-			   NULL)
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+			   mt8173_afe_runtime_resume, NULL)
 };
 
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
 	.driver = {
-		   .name = "mtk-afe-pcm",
-		   .of_match_table = mtk_afe_pcm_dt_match,
-		   .pm = &mtk_afe_pm_ops,
+		   .name = "mt8173-afe-pcm",
+		   .of_match_table = mt8173_afe_pcm_dt_match,
+		   .pm = &mt8173_afe_pm_ops,
 	},
-	.probe = mtk_afe_pcm_dev_probe,
-	.remove = mtk_afe_pcm_dev_remove,
+	.probe = mt8173_afe_pcm_dev_probe,
+	.remove = mt8173_afe_pcm_dev_remove,
 };
 
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
 
 MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
 MODULE_AUTHOR("Koro Chen <koro.chen at mediatek.com>");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-max98090.c
rename to sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35..5524a2c 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -18,7 +18,7 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 static struct snd_soc_jack mt8173_max98090_jack;
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e0836..467f704 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb59392..1b8b2a7 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -19,8 +19,8 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 072934b..d478976 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index f341f62..0000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * mtk_afe_common.h  --  Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen at mediatek.com>
- *             Sascha Hauer <s.hauer at pengutronix.de>
- *             Hidalgo Huang <hidalgo.huang at mediatek.com>
- *             Ir Lian <ir.lian at mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
-	MTK_AFE_MEMIF_DL1,
-	MTK_AFE_MEMIF_DL2,
-	MTK_AFE_MEMIF_VUL,
-	MTK_AFE_MEMIF_DAI,
-	MTK_AFE_MEMIF_AWB,
-	MTK_AFE_MEMIF_MOD_DAI,
-	MTK_AFE_MEMIF_HDMI,
-	MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM2,
-	MTK_AFE_IO_PMIC,
-	MTK_AFE_IO_I2S,
-	MTK_AFE_IO_2ND_I2S,
-	MTK_AFE_IO_HW_GAIN1,
-	MTK_AFE_IO_HW_GAIN2,
-	MTK_AFE_IO_MRG_O,
-	MTK_AFE_IO_MRG_I,
-	MTK_AFE_IO_DAIBT,
-	MTK_AFE_IO_HDMI,
-};
-
-enum {
-	MTK_AFE_IRQ_1,
-	MTK_AFE_IRQ_2,
-	MTK_AFE_IRQ_3,
-	MTK_AFE_IRQ_4,
-	MTK_AFE_IRQ_5,
-	MTK_AFE_IRQ_6,
-	MTK_AFE_IRQ_7,
-	MTK_AFE_IRQ_8,
-	MTK_AFE_IRQ_NUM,
-};
-
-enum {
-	MTK_CLK_INFRASYS_AUD,
-	MTK_CLK_TOP_PDN_AUD,
-	MTK_CLK_TOP_PDN_AUD_BUS,
-	MTK_CLK_I2S0_M,
-	MTK_CLK_I2S1_M,
-	MTK_CLK_I2S2_M,
-	MTK_CLK_I2S3_M,
-	MTK_CLK_I2S3_B,
-	MTK_CLK_BCK0,
-	MTK_CLK_BCK1,
-	MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mtk_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mtk_afe_memif_data *data;
-	const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Mon Jun 13 00:26:43 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Mon, 13 Jun 2016 15:26:43 +0800
Subject: [alsa-devel] [PATCH v4 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465802806-19197-7-git-send-email-garlic.tseng@mediatek.com>

Add mt2701 platform driver implementation for playback and capture.
The implement follow DAPM structure (memory interface as FE and I2S
as BE).
Because of the hardware design, i2s out required to be enabled when
we need to enable i2s in. This patch includes the implementation.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 1559 ++++++++++++++++++++++++++++
 1 file changed, 1559 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644
index 0000000..deb5f82
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1559 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS	0xff
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+		| SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+		   | SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 1024 * 256,
+	.periods_min = 4,
+	.periods_max = 1024,
+	.buffer_bytes_max = 1024 * 1024 * 16,
+	.fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+	unsigned int rate;
+	unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+	{ .rate = 8000, .regvalue = 0 },
+	{ .rate = 12000, .regvalue = 1 },
+	{ .rate = 16000, .regvalue = 2 },
+	{ .rate = 24000, .regvalue = 3 },
+	{ .rate = 32000, .regvalue = 4 },
+	{ .rate = 48000, .regvalue = 5 },
+	{ .rate = 96000, .regvalue = 6 },
+	{ .rate = 192000, .regvalue = 7 },
+	{ .rate = 384000, .regvalue = 8 },
+	{ .rate = 7350, .regvalue = 16 },
+	{ .rate = 11025, .regvalue = 17 },
+	{ .rate = 14700, .regvalue = 18 },
+	{ .rate = 22050, .regvalue = 19 },
+	{ .rate = 29400, .regvalue = 20 },
+	{ .rate = 44100, .regvalue = 21 },
+	{ .rate = 88200, .regvalue = 22 },
+	{ .rate = 176400, .regvalue = 23 },
+	{ .rate = 352800, .regvalue = 24 },
+};
+
+int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+	int val = num - MT2701_IO_I2S;
+
+	if (val < 0 || val >= MT2701_I2S_NUM) {
+		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+			__func__, num, val);
+		return -EINVAL;
+	}
+	return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+		if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+			return mt2701_afe_i2s_rates[i].regvalue;
+
+	return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+	int ret = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* enable mclk */
+	ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+	if (ret)
+		dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+			i2s_num);
+
+	return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai,
+					int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	int stream_dir = substream->stream;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert)	{
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	i2s_path->on[stream_dir]--;
+	if (i2s_path->on[stream_dir] < 0) {
+		dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+			 i2s_path->on[stream_dir], stream_dir);
+		i2s_path->on[stream_dir] = 0;
+	}
+	if (i2s_path->on[stream_dir])
+		return 0;
+
+	/* disable i2s */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   1 << i2s_data->i2s_pwn_shift);
+	return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+	if (i2s_num < 0)
+		return;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (i2s_path->occupied[substream->stream])
+		i2s_path->occupied[substream->stream] = 0;
+	else
+		goto I2S_UNSTART;
+
+	mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+	/* need to disable i2s-out path when disable i2s-in */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+	/* disable mclk */
+	clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+					  struct snd_soc_dai *dai,
+					  int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	int reg, fs, w_len = 1;
+	int stream_dir = substream->stream;
+	unsigned int mask = 0, val = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert) {
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	/* no need to enable if already done */
+	i2s_path->on[stream_dir]++;
+
+	if (i2s_path->on[stream_dir] != 1)
+		return 0;
+
+	fs = mt2701_afe_i2s_fs(runtime->rate);
+
+	if (i2s_path->div_bck_to_lrck == 32)
+		w_len = 0;
+	else if (i2s_path->div_bck_to_lrck == 64)
+		w_len = 1;
+	else
+		dev_warn(dai->dev, "%s() bad bit count %d\n", __func__,
+			 afe_priv->i2s_path[i2s_num].div_bck_to_lrck);
+
+	mask = ASYS_I2S_CON_FS |
+	       ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
+	       ASYS_I2S_CON_I2S_MODE |
+	       ASYS_I2S_CON_WIDE_MODE;
+
+	val = ASYS_I2S_CON_FS_SET(fs) |
+	      ASYS_I2S_CON_I2S_MODE |
+	      ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+	if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+		mask |= ASYS_I2S_IN_PHASE_FIX;
+		val |= ASYS_I2S_IN_PHASE_FIX;
+	}
+
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = ASMO_TIMING_CON1;
+	else
+		reg = ASMI_TIMING_CON1;
+
+	regmap_update_bits(afe->regmap, reg,
+			   i2s_data->i2s_asrc_fs_mask
+			   << i2s_data->i2s_asrc_fs_shift,
+			   fs << i2s_data->i2s_asrc_fs_shift);
+
+	/* enable i2s */
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   0 << i2s_data->i2s_pwn_shift);
+
+	/* reset i2s hw status before enable */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, 0);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+	return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	int clk_domain;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int mclk_rate;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+	mclk_rate = i2s_path->mclk_rate;
+
+	if (i2s_path->occupied[substream->stream])
+		return -EBUSY;
+	i2s_path->occupied[substream->stream] = 1;
+
+	if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+		clk_domain = 0;
+	} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+		clk_domain = 1;
+	} else {
+		dev_err(dai->dev, "%s() bad mclk rate %d\n",
+			__func__, mclk_rate);
+		return -EINVAL;
+	}
+	mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	} else {
+		/* need to enable i2s-out path when enable i2s-in */
+		/* prepare for another direction "out" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 1);
+		/* prepare for "in" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	}
+
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				     unsigned int freq, int dir)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* mclk */
+	if (dir == SND_SOC_CLOCK_IN) {
+		dev_warn(dai->dev,
+			 "%s() warning: mt2701 doesn't support mclk input\n",
+			__func__);
+		return -EINVAL;
+	}
+	afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+				     int div)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	switch (div_id) {
+	case DIV_ID_MCLK_TO_BCK:
+		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
+		break;
+	case DIV_ID_BCK_TO_LRCK:
+		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	afe_priv->i2s_path[i2s_num].format = fmt;
+	return 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif_tmp;
+
+	/* can't run single DL & DLM at the same time */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+		if (memif_tmp->substream) {
+			dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+				 __func__, stream_dir, memif_num);
+			return -EBUSY;
+		}
+	}
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+
+	/* single DL use PAIR_INTERLEAVE */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_update_bits(afe->regmap,
+				   AFE_MEMIF_PBUF_SIZE,
+				   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+				   AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+	}
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp;
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_tmp = &afe->memif[i];
+		if (memif_tmp->substream)
+			return -EBUSY;
+	}
+
+	/* enable agent for all signal DL (due to hw design) */
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   0 << memif_data->agent_disable_shift);
+	}
+
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   1 << memif_data->agent_disable_shift);
+	}
+	return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int channels = params_channels(params);
+
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+			   AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+				 int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift,
+				   1 << memif_tmp->data->enable_shift);
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift, 0);
+
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int fs;
+
+	if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT)
+		fs = mt2701_afe_i2s_fs(rate);
+	else
+		fs = (rate == 16000 ? 1 : 0);
+	return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+	return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+	.startup	= mt2701_simple_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mt2701_simple_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+	.startup	= mt2701_dlm_fe_startup,
+	.shutdown	= mt2701_dlm_fe_shutdown,
+	.hw_params	= mt2701_dlm_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+	.startup	= mt2701_afe_i2s_startup,
+	.shutdown	= mt2701_afe_i2s_shutdown,
+	.prepare	= mt2701_afe_i2s_prepare,
+	.set_sysclk	= mt2701_afe_i2s_set_sysclk,
+	.set_clkdiv	= mt2701_afe_i2s_set_clkdiv,
+	.set_fmt	= mt2701_afe_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+	/* FE DAIs: memory intefaces to CPU */
+	{
+		.name = "PCM_multi",
+		.id = MT2701_MEMIF_DLM,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLM",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_dlm_memif_dai_ops,
+	},
+	{
+		.name = "PCM0",
+		.id = MT2701_MEMIF_UL1,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM1",
+		.id = MT2701_MEMIF_UL2,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL2",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	/* BE DAIs */
+	{
+		.name = "I2S0",
+		.id = MT2701_IO_I2S,
+		.playback = {
+			.stream_name = "I2S0 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.capture = {
+			.stream_name = "I2S0 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S1",
+		.id = MT2701_IO_2ND_I2S,
+		.playback = {
+			.stream_name = "I2S1 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S1 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S2",
+		.id = MT2701_IO_3RD_I2S,
+		.playback = {
+			.stream_name = "I2S2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S3",
+		.id = MT2701_IO_4TH_I2S,
+		.playback = {
+			.stream_name = "I2S3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S3 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o23_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+	SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S0 Out Switch",
+				    ASYS_I2SO1_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S1 Out Switch",
+				    ASYS_I2SO2_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S2 Out Switch",
+				    PWR2_TOP_CON, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S3 Out Switch",
+				    PWR2_TOP_CON, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S4 Out Switch",
+				    PWR2_TOP_CON, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1,
+				    1),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+			   ARRAY_SIZE(mt2701_afe_i02_mix)),
+	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+			   ARRAY_SIZE(mt2701_afe_o00_mix)),
+	SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+			   ARRAY_SIZE(mt2701_afe_o01_mix)),
+	SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+			   ARRAY_SIZE(mt2701_afe_o02_mix)),
+	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+			   ARRAY_SIZE(mt2701_afe_o03_mix)),
+	SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+			   ARRAY_SIZE(mt2701_afe_o14_mix)),
+	SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+			   ARRAY_SIZE(mt2701_afe_o15_mix)),
+	SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+			   ARRAY_SIZE(mt2701_afe_o16_mix)),
+	SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+			   ARRAY_SIZE(mt2701_afe_o17_mix)),
+	SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+			   ARRAY_SIZE(mt2701_afe_o18_mix)),
+	SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+			   ARRAY_SIZE(mt2701_afe_o19_mix)),
+	SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+			   ARRAY_SIZE(mt2701_afe_o20_mix)),
+	SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+			   ARRAY_SIZE(mt2701_afe_o21_mix)),
+	SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+			   ARRAY_SIZE(mt2701_afe_o22_mix)),
+	SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+			   ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+	SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+	SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+	SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+	SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+
+	SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+	SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+	SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+	SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+	{"I12", NULL, "DL1"},
+	{"I13", NULL, "DL1"},
+	{"I35", NULL, "DLBT"},
+
+	{"I2S0 Playback", NULL, "O15"},
+	{"I2S0 Playback", NULL, "O16"},
+
+	{"I2S1 Playback", NULL, "O17"},
+	{"I2S1 Playback", NULL, "O18"},
+	{"I2S2 Playback", NULL, "O19"},
+	{"I2S2 Playback", NULL, "O20"},
+	{"I2S3 Playback", NULL, "O21"},
+	{"I2S3 Playback", NULL, "O22"},
+	{"BT Playback", NULL, "O31"},
+
+	{"UL1", NULL, "O00"},
+	{"UL1", NULL, "O01"},
+	{"UL2", NULL, "O02"},
+	{"UL2", NULL, "O03"},
+	{"ULBT", NULL, "O14"},
+
+	{"I00", NULL, "I2S0 Capture"},
+	{"I01", NULL, "I2S0 Capture"},
+
+	{"I02", NULL, "I2S1 Capture"},
+	{"I03", NULL, "I2S1 Capture"},
+	/* I02,03 link to UL2, also need to open I2S0 */
+	{"I02", "I2S0 Switch", "I2S0 Capture"},
+
+	{"I26", NULL, "BT Capture"},
+
+	{"ASRC_O0", "Asrc0 out Switch", "DLM"},
+	{"ASRC_O1", "Asrc1 out Switch", "DLM"},
+	{"ASRC_O2", "Asrc2 out Switch", "DLM"},
+	{"ASRC_O3", "Asrc3 out Switch", "DLM"},
+
+	{"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"},
+	{"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"},
+	{"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"},
+	{"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"},
+
+	{ "I12", NULL, "I12I13" },
+	{ "I13", NULL, "I12I13" },
+	{ "I14", NULL, "I14I15" },
+	{ "I15", NULL, "I14I15" },
+	{ "I16", NULL, "I16I17" },
+	{ "I17", NULL, "I16I17" },
+	{ "I18", NULL, "I18I19" },
+	{ "I19", NULL, "I18I19" },
+
+	{ "O00", "I00 Switch", "I00" },
+	{ "O01", "I01 Switch", "I01" },
+	{ "O02", "I02 Switch", "I02" },
+	{ "O03", "I03 Switch", "I03" },
+	{ "O14", "I26 Switch", "I26" },
+	{ "O15", "I12 Switch", "I12" },
+	{ "O16", "I13 Switch", "I13" },
+	{ "O17", "I14 Switch", "I14" },
+	{ "O18", "I15 Switch", "I15" },
+	{ "O19", "I16 Switch", "I16" },
+	{ "O20", "I17 Switch", "I17" },
+	{ "O21", "I18 Switch", "I18" },
+	{ "O22", "I19 Switch", "I19" },
+	{ "O31", "I35 Switch", "I35" },
+
+};
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+	.name = "mt2701-afe-pcm-dai",
+	.dapm_widgets = mt2701_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+	.dapm_routes = mt2701_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+};
+
+static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
+	{
+		.name = "DL1",
+		.id = MT2701_MEMIF_DL1,
+		.reg_ofs_base = AFE_DL1_BASE,
+		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 16,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 1,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 6,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL2",
+		.id = MT2701_MEMIF_DL2,
+		.reg_ofs_base = AFE_DL2_BASE,
+		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 17,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 2,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 7,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL3",
+		.id = MT2701_MEMIF_DL3,
+		.reg_ofs_base = AFE_DL3_BASE,
+		.reg_ofs_cur = AFE_DL3_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 18,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 3,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 4,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 8,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL4",
+		.id = MT2701_MEMIF_DL4,
+		.reg_ofs_base = AFE_DL4_BASE,
+		.reg_ofs_cur = AFE_DL4_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 19,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 4,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 9,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL5",
+		.id = MT2701_MEMIF_DL5,
+		.reg_ofs_base = AFE_DL5_BASE,
+		.reg_ofs_cur = AFE_DL5_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 20,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 20,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 5,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 10,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLM",
+		.id = MT2701_MEMIF_DLM,
+		.reg_ofs_base = AFE_DLMCH_BASE,
+		.reg_ofs_cur = AFE_DLMCH_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 7,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 28,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 12,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL1",
+		.id = MT2701_MEMIF_UL1,
+		.reg_ofs_base = AFE_VUL_BASE,
+		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 0,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 10,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 0,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL2",
+		.id = MT2701_MEMIF_UL2,
+		.reg_ofs_base = AFE_UL2_BASE,
+		.reg_ofs_cur = AFE_UL2_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 2,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 11,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 1,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL3",
+		.id = MT2701_MEMIF_UL3,
+		.reg_ofs_base = AFE_UL3_BASE,
+		.reg_ofs_cur = AFE_UL3_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 4,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 12,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 2,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL4",
+		.id = MT2701_MEMIF_UL4,
+		.reg_ofs_base = AFE_UL4_BASE,
+		.reg_ofs_cur = AFE_UL4_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 6,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 13,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 3,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL5",
+		.id = MT2701_MEMIF_UL5,
+		.reg_ofs_base = AFE_UL5_BASE,
+		.reg_ofs_cur = AFE_UL5_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 20,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 8,
+		.fs_maskbit = 0x1f,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 14,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 4,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLBT",
+		.id = MT2701_MEMIF_DLBT,
+		.reg_ofs_base = AFE_ARB1_BASE,
+		.reg_ofs_cur = AFE_ARB1_CUR,
+		.fs_reg = AFE_DAC_CON3,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 22,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 8,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 14,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 13,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "ULBT",
+		.id = MT2701_MEMIF_ULBT,
+		.reg_ofs_base = AFE_DAI_BASE,
+		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 30,
+		.fs_maskbit = 0x1,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 17,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 20,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 16,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+	{
+		.id = MT2701_IRQ_ASYS_IRQ1,
+		.irq_cnt_reg = ASYS_IRQ1_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ1_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ1_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 0,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ2,
+		.irq_cnt_reg = ASYS_IRQ2_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ2_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ2_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 1,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ3,
+		.irq_cnt_reg = ASYS_IRQ3_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ3_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ3_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 2,
+	}
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = {
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO1_CON,
+			.i2s_pwn_shift = 6,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN1_CON,
+			.i2s_pwn_shift = 0,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO2_CON,
+			.i2s_pwn_shift = 7,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN2_CON,
+			.i2s_pwn_shift = 1,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO3_CON,
+			.i2s_pwn_shift = 8,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN3_CON,
+			.i2s_pwn_shift = 2,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO4_CON,
+			.i2s_pwn_shift = 9,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN4_CON,
+			.i2s_pwn_shift = 3,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AFE_END_ADDR,
+	.cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+	int id;
+	struct mtk_base_afe *afe = dev;
+	struct mtk_base_afe_memif *memif;
+	struct mtk_base_afe_irq *irq;
+	u32 status;
+
+	regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+	regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+	for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+		memif = &afe->memif[id];
+		if (memif->irq_usage < 0)
+			continue;
+		irq = &afe->irqs[memif->irq_usage];
+		if (status & 1 << (irq->irq_data->irq_clr_shift))
+			snd_pcm_period_elapsed(memif->substream);
+	}
+	return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	return mt2701_afe_enable_clock(afe);
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+	int ret, i;
+	unsigned int irq_id;
+	struct mtk_base_afe *afe;
+	struct mt2701_afe_private *afe_priv;
+	struct resource *res;
+	struct device *dev;
+
+	ret = 0;
+	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe)
+		return -ENOMEM;
+
+	afe->dev = &pdev->dev;
+	dev = afe->dev;
+
+	irq_id = platform_get_irq(pdev, 0);
+	if (!irq_id) {
+		dev_err(dev, "%s no irq found\n", dev->of_node->name);
+		return -ENXIO;
+	}
+	ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+			       IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+	if (ret) {
+		dev_err(dev, "could not request_irq for asys-isr\n");
+		return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+	if (IS_ERR(afe->base_addr))
+		return PTR_ERR(afe->base_addr);
+
+	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+		&mt2701_afe_regmap_config);
+	if (IS_ERR(afe->regmap))
+		return PTR_ERR(afe->regmap);
+
+	mutex_init(&afe->irq_alloc_lock);
+
+	/* memif initialize */
+	afe->memif_size = MT2701_MEMIF_NUM;
+	afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+				  GFP_KERNEL);
+
+	if (!afe->memif)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->memif_size; i++) {
+		afe->memif[i].data = &memif_data[i];
+		afe->memif[i].irq_usage = -1;
+	}
+
+	/* irq initialize */
+	afe->irqs_size = MT2701_IRQ_ASYS_END;
+	afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+				 GFP_KERNEL);
+
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++)
+		afe->irqs[i].irq_data = &irq_data[i];
+
+	/* I2S initialize */
+	for (i = 0; i < MT2701_I2S_NUM; i++) {
+		afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+			= &mt2701_i2s_data[i][I2S_OUT];
+		afe_priv->i2s_path[i].i2s_data[I2S_IN]
+			= &mt2701_i2s_data[i][I2S_IN];
+	}
+
+	afe->mtk_afe_hardware = &mt2701_afe_hardware;
+	afe->memif_fs = mt2701_memif_fs;
+	afe->irq_fs = mt2701_irq_fs;
+
+	afe->reg_back_up_list = mt2701_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+	afe->runtime_resume = mt2701_afe_runtime_resume;
+	afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+	/* initial audio related clock */
+	ret = mt2701_init_clock(afe);
+	if (ret) {
+		dev_err(dev, "init clock error\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, afe);
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev))
+		goto err_pm_disable;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	if (ret) {
+		dev_warn(dev, "err_platform\n");
+		goto err_platform;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev,
+					 &mt2701_afe_pcm_dai_component,
+					 mt2701_afe_pcm_dais,
+					 ARRAY_SIZE(mt2701_afe_pcm_dais));
+	if (ret) {
+		dev_warn(dev, "err_dai_component\n");
+		goto err_dai_component;
+	}
+
+	mt2701_afe_runtime_resume(&pdev->dev);
+
+	return 0;
+
+err_dai_component:
+	snd_soc_unregister_component(&pdev->dev);
+
+err_platform:
+	snd_soc_unregister_platform(&pdev->dev);
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		mt2701_afe_runtime_suspend(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_unregister_platform(&pdev->dev);
+	/* disable afe clock */
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+			   mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+	.driver = {
+		   .name = "mt2701-audio",
+		   .of_match_table = mt2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+		   .pm = &mt2701_afe_pm_ops,
+#endif
+	},
+	.probe = mt2701_afe_pcm_dev_probe,
+	.remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
-- 
1.9.1



From p.zabel at pengutronix.de  Mon Jun 13 02:44:40 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Mon, 13 Jun 2016 11:44:40 +0200
Subject: [GIT PULL] drm/mediatek: MT8173 HDMI support
Message-ID: <1465811080.3553.8.camel@pengutronix.de>

Hi Dave,

please consider merging this tag, which contains the v16 MT8173 HDMI
patches I sent on 2016-05-26, rebased onto v4.7-rc2. There have been no
further comments.

regards
Philipp

The following changes since commit af8c34ce6ae32addda3788d54a7e340cad22516b:

  Linux 4.7-rc2 (2016-06-05 14:31:26 -0700)

are available in the git repository at:

  git://git.pengutronix.de/git/pza/linux.git tags/mediatek-drm-2016-06-13

for you to fetch changes up to 21ff16b77e779f9f9dd56522613f3835b0a3a386:

  dt-bindings: hdmi-connector: add DDC I2C bus phandle documentation (2016-06-13 11:08:27 +0200)

----------------------------------------------------------------
MT8173 HDMI support

- device tree binding documentation for MT8173 HDMI encoder, CEC, DDC,
  and PHY
- drivers for MT8173 HDMI encoder, CEC (HPD only for now), DDC, and PHY
- enable HDMI output via a custom SMCCC call
- add ddc-i2c-bus property to HDMI connector device tree binding

----------------------------------------------------------------
Jie Qiu (2):
      drm/mediatek: Add HDMI support
      drm/mediatek: enable hdmi output control bit

Philipp Zabel (2):
      dt-bindings: drm/mediatek: Add Mediatek HDMI dts binding
      dt-bindings: hdmi-connector: add DDC I2C bus phandle documentation

 .../bindings/display/connector/hdmi-connector.txt  |    1 +
 .../bindings/display/mediatek/mediatek,hdmi.txt    |  148 ++
 drivers/gpu/drm/mediatek/Kconfig                   |    8 +
 drivers/gpu/drm/mediatek/Makefile                  |    7 +
 drivers/gpu/drm/mediatek/mtk_cec.c                 |  265 +++
 drivers/gpu/drm/mediatek/mtk_cec.h                 |   26 +
 drivers/gpu/drm/mediatek/mtk_hdmi.c                | 1828 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_hdmi.h                |   23 +
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c            |  358 ++++
 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h           |  238 +++
 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c     |  515 ++++++
 11 files changed, 3417 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,hdmi.txt
 create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c


From robh at kernel.org  Mon Jun 13 11:54:54 2016
From: robh at kernel.org (Rob Herring)
Date: Mon, 13 Jun 2016 13:54:54 -0500
Subject: [PATCH v3,1/5] dt-bindings: mt8173-xhci: support host side of
 dual-role mode
In-Reply-To: <1465543962-11678-2-git-send-email-chunfeng.yun@mediatek.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
 <1465543962-11678-2-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <20160613185454.GA21742@rob-hp-laptop>

On Fri, Jun 10, 2016 at 03:32:38PM +0800, Chunfeng Yun wrote:
> Some resources, such as IPPC register etc, shared with device
> driver are moved into common glue layer when xHCI driver is the
> host side of dual-role mode and they should be changed as optional
> properties if they are required ones before. For clarity, add
> a new part of binding to support host side of dual-role mode.
> 
> Additionally add optional properties of pinctrl for host only mode
> 
> Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> ---
>  .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++++++++++++++++++++
>  1 file changed, 48 insertions(+)

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


From chunfeng.yun at mediatek.com  Mon Jun 13 19:19:40 2016
From: chunfeng.yun at mediatek.com (chunfeng yun)
Date: Tue, 14 Jun 2016 10:19:40 +0800
Subject: [PATCH v3,2/5] dt-bindings: mt8173-mtu3: add devicetree bindings
In-Reply-To: <878tyd761u.fsf@linux.intel.com>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
 <1465543962-11678-3-git-send-email-chunfeng.yun@mediatek.com>
 <878tyd761u.fsf@linux.intel.com>
Message-ID: <1465870780.19154.0.camel@mhfsdcap03>

On Fri, 2016-06-10 at 12:21 +0300, Felipe Balbi wrote:
> Hi,
> 
> Chunfeng Yun <chunfeng.yun at mediatek.com> writes:
> > + - enable-manual-drd : supports manual dual-role switch via sysfs; only used
> > +	when receptacle is TYPE-A and also wants to support dual-role mode.
> 
> sorry, but this manual-drd can't be accepted. You should rely on a DT
> property for this and you shouldn't expose it to sysfs. Use DebugFS for
> that.
I will make use of debugfs instead
Thanks a lot
> 
> 




From chunfeng.yun at mediatek.com  Mon Jun 13 19:20:15 2016
From: chunfeng.yun at mediatek.com (chunfeng yun)
Date: Tue, 14 Jun 2016 10:20:15 +0800
Subject: [PATCH v3,1/5] dt-bindings: mt8173-xhci: support host side of
 dual-role mode
In-Reply-To: <20160613185454.GA21742@rob-hp-laptop>
References: <1465543962-11678-1-git-send-email-chunfeng.yun@mediatek.com>
 <1465543962-11678-2-git-send-email-chunfeng.yun@mediatek.com>
 <20160613185454.GA21742@rob-hp-laptop>
Message-ID: <1465870815.19154.1.camel@mhfsdcap03>

On Mon, 2016-06-13 at 13:54 -0500, Rob Herring wrote:
> On Fri, Jun 10, 2016 at 03:32:38PM +0800, Chunfeng Yun wrote:
> > Some resources, such as IPPC register etc, shared with device
> > driver are moved into common glue layer when xHCI driver is the
> > host side of dual-role mode and they should be changed as optional
> > properties if they are required ones before. For clarity, add
> > a new part of binding to support host side of dual-role mode.
> > 
> > Additionally add optional properties of pinctrl for host only mode
> > 
> > Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> > ---
> >  .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++++++++++++++++++++
> >  1 file changed, 48 insertions(+)
> 
> Acked-by: Rob Herring <robh at kernel.org>

Thank you




From mars.cheng at mediatek.com  Mon Jun 13 19:20:05 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 14 Jun 2016 10:20:05 +0800
Subject: [PATCH 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>

This adds basic chip support for MT6755 SoC.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/Makefile         |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-phone.dts |   39 +++++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi      |  143 +++++++++++++++++++++++++
 3 files changed, 183 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi

diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
index e0a4bff..9abdb01 100644
--- a/arch/arm64/boot/dts/mediatek/Makefile
+++ b/arch/arm64/boot/dts/mediatek/Makefile
@@ -1,3 +1,4 @@
+dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-phone.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
 
diff --git a/arch/arm64/boot/dts/mediatek/mt6755-phone.dts b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
new file mode 100644
index 0000000..f551666
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+#include "mt6755.dtsi"
+
+/ {
+	model = "MediaTek MT6755 phone";
+	compatible = "mediatek,mt6755-phone", "mediatek,mt6755";
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+	};
+
+	memory at 40000000 {
+		device_type = "memory";
+		reg = <0 0x40000000 0 0x1e800000>;
+	};
+
+	chosen {
+		stdout-path = "serial0:921600n8";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
new file mode 100644
index 0000000..36c3384
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+	compatible = "mediatek,mt6755";
+	interrupt-parent = <&sysirq>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	psci {
+		compatible = "arm,psci-0.2";
+		method = "smc";
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x000>;
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x001>;
+		};
+
+		cpu2: cpu at 2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x002>;
+		};
+
+		cpu3: cpu at 3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x003>;
+		};
+
+		cpu4: cpu at 100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x100>;
+		};
+
+		cpu5: cpu at 101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x101>;
+		};
+
+		cpu6: cpu at 102 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x102>;
+		};
+
+		cpu7: cpu at 103 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x103>;
+		};
+	};
+
+	uart_clk: dummy26m {
+		compatible = "fixed-clock";
+		clock-frequency = <26000000>;
+		#clock-cells = <0>;
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupt-parent = <&gic>;
+		interrupts = <GIC_PPI 13
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
+	};
+
+	sysirq: intpol-controller at 10200620 {
+		compatible = "mediatek,mt6755-sysirq",
+			     "mediatek,mt6577-sysirq";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		reg = <0 0x10200620 0 0x20>;
+	};
+
+	gic: interrupt-controller at 10231000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		interrupt-controller;
+		reg = <0 0x10231000 0 0x1000>,
+		      <0 0x10232000 0 0x2000>,
+		      <0 0x10234000 0 0x2000>,
+		      <0 0x10236000 0 0x2000>;
+	};
+
+	uart0: serial at 11002000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11002000 0 0x400>;
+		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+	};
+
+	uart1: serial at 11003000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11003000 0 0x400>;
+		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+	};
+};
-- 
1.7.9.5



From mars.cheng at mediatek.com  Mon Jun 13 19:20:03 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 14 Jun 2016 10:20:03 +0800
Subject: Add mt6755 basic chip support
Message-ID: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>

This patch adds basic support for Mediatek's new 8-core chip, mt6755.
It is also named as Helio P10. It is based on 4.7-rc1

Mars Cheng (2):
  Document: DT: Add bindings for mediatek MT6755 SoC Platform
  arm64: dts: mediatek: add mt6755 support

 Documentation/devicetree/bindings/arm/mediatek.txt |    4 +
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 arch/arm64/boot/dts/mediatek/Makefile              |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-phone.dts      |   39 ++++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi           |  143 ++++++++++++++++++++
 6 files changed, 189 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi



From mars.cheng at mediatek.com  Mon Jun 13 19:20:04 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 14 Jun 2016 10:20:04 +0800
Subject: [PATCH 1/2] Document: DT: Add bindings for mediatek MT6755 SoC
 Platform
In-Reply-To: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1465870805-10451-2-git-send-email-mars.cheng@mediatek.com>

This adds DT binding documentation for Mediatek MT6755.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
---
 Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 3 files changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/mediatek.txt b/Documentation/devicetree/bindings/arm/mediatek.txt
index d9c2a37..e2f3b04 100644
--- a/Documentation/devicetree/bindings/arm/mediatek.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek.txt
@@ -10,6 +10,7 @@ compatible: Must contain one of
    "mediatek,mt6580"
    "mediatek,mt6589"
    "mediatek,mt6592"
+   "mediatek,mt6755"
    "mediatek,mt6795"
    "mediatek,mt7623"
    "mediatek,mt8127"
@@ -31,6 +32,9 @@ Supported boards:
 - Evaluation board for MT6592:
     Required root node properties:
       - compatible = "mediatek,mt6592-evb", "mediatek,mt6592";
+- Evaluation phone for MT6755(Helio P10):
+    Required root node properties:
+      - compatible = "mediatek,mt6755-phone", "mediatek,mt6755";
 - Evaluation board for MT6795(Helio X10):
     Required root node properties:
       - compatible = "mediatek,mt6795-evb", "mediatek,mt6795";
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
index 8cf564d..9d1d72c 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
@@ -9,6 +9,7 @@ Required properties:
 	"mediatek,mt8135-sysirq"
 	"mediatek,mt8127-sysirq"
 	"mediatek,mt6795-sysirq"
+	"mediatek,mt6755-sysirq"
 	"mediatek,mt6592-sysirq"
 	"mediatek,mt6589-sysirq"
 	"mediatek,mt6582-sysirq"
diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt
index e99e10a..0015c72 100644
--- a/Documentation/devicetree/bindings/serial/mtk-uart.txt
+++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt
@@ -6,6 +6,7 @@ Required properties:
   * "mediatek,mt6580-uart" for MT6580 compatible UARTS
   * "mediatek,mt6582-uart" for MT6582 compatible UARTS
   * "mediatek,mt6589-uart" for MT6589 compatible UARTS
+  * "mediatek,mt6755-uart" for MT6755 compatible UARTS
   * "mediatek,mt6795-uart" for MT6795 compatible UARTS
   * "mediatek,mt7623-uart" for MT7623 compatible UARTS
   * "mediatek,mt8127-uart" for MT8127 compatible UARTS
-- 
1.7.9.5



From bibby.hsieh at mediatek.com  Mon Jun 13 19:55:52 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Tue, 14 Jun 2016 10:55:52 +0800
Subject: [PATCH 1/2] drm/mediatek: Add gamma correction
Message-ID: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>

Apply gamma function to correct brightness values.
It applies arbitrary mapping curve to compensate the
incorrect transfer function of the panel.

Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   12 ++++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   56 ++++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    9 +++++
 4 files changed, 77 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 24aa3ba..1b38406 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -127,6 +127,16 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
 	state->base.crtc = crtc;
 }
 
+static void mtk_crtc_gamma_set(struct drm_crtc *crtc, u16 *r,
+			       u16 *g, u16 *b, uint32_t start, uint32_t size)
+{
+	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	unsigned int i;
+
+	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+		mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], r, g, b, start, size);
+}
+
 static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
 {
 	struct mtk_crtc_state *state;
@@ -418,6 +428,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
 	.reset			= mtk_drm_crtc_reset,
 	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
 	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
+	.gamma_set		= mtk_crtc_gamma_set,
 };
 
 static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
@@ -569,6 +580,7 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
 	if (ret < 0)
 		goto unprepare;
 
+	drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
 	priv->crtc[pipe] = &mtk_crtc->base;
 	priv->num_pipes++;
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
index 81e5566..d332564 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -19,6 +19,7 @@
 #include "mtk_drm_plane.h"
 
 #define OVL_LAYER_NR	4
+#define MTK_LUT_SIZE	512
 
 int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
 void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 3970fcf..3fd5141 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -24,6 +24,7 @@
 #include "mtk_drm_drv.h"
 #include "mtk_drm_plane.h"
 #include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_crtc.h"
 
 #define DISP_OD_EN				0x0000
 #define DISP_OD_INTEN				0x0008
@@ -38,6 +39,18 @@
 #define DISP_COLOR_WIDTH			0x0c50
 #define DISP_COLOR_HEIGHT			0x0c54
 
+#define DISP_AAL_EN				0x000
+#define DISP_AAL_SIZE				0x030
+
+#define DISP_GAMMA_CFG				0x020
+#define DISP_GAMMA_LUT				0x700
+
+#define LUT_10BIT_MASK				0x3ff
+
+#define AAL_EN			BIT(0)
+
+#define GAMMA_LUT_EN		BIT(1)
+
 #define	OD_RELAY_MODE		BIT(0)
 
 #define	UFO_BYPASS		BIT(2)
@@ -76,6 +89,40 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
 	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
 }
 
+static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
+			   unsigned int h, unsigned int vrefresh)
+{
+	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
+}
+
+static void mtk_aal_start(struct mtk_ddp_comp *comp)
+{
+	writel(AAL_EN, comp->regs  + DISP_AAL_EN);
+}
+
+static void mtk_aal_stop(struct mtk_ddp_comp *comp)
+{
+	writel_relaxed(0x0, comp->regs  + DISP_AAL_EN);
+}
+
+static void mtk_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
+			  u16 *b, uint32_t start, uint32_t size)
+{
+	int i;
+	unsigned int lut;
+	int end = (start + size > MTK_LUT_SIZE) ? MTK_LUT_SIZE : start + size;
+	void __iomem *lut_base;
+
+	writel(GAMMA_LUT_EN, comp->regs + DISP_GAMMA_CFG);
+	lut_base = comp->regs + DISP_GAMMA_LUT;
+	for (i = start; i < end; i++) {
+		lut = (((r[i] >> 6) & LUT_10BIT_MASK) << 20) +
+			(((g[i] >> 6) & LUT_10BIT_MASK) << 10) +
+			((b[i] >> 6) & LUT_10BIT_MASK);
+		writel(lut, (lut_base + i * 4));
+	}
+}
+
 static const struct mtk_ddp_comp_funcs ddp_color = {
 	.config = mtk_color_config,
 	.start = mtk_color_start,
@@ -90,6 +137,13 @@ static const struct mtk_ddp_comp_funcs ddp_ufoe = {
 	.start = mtk_ufoe_start,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_aal = {
+	.gamma_set = mtk_gamma_set,
+	.config = mtk_aal_config,
+	.start = mtk_aal_start,
+	.stop = mtk_aal_stop,
+};
+
 static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_OVL] = "ovl",
 	[MTK_DISP_RDMA] = "rdma",
@@ -112,7 +166,7 @@ struct mtk_ddp_comp_match {
 };
 
 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
-	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
+	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
 	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
 	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 6b13ba9..0832179 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -73,6 +73,8 @@ struct mtk_ddp_comp_funcs {
 	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
 	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
 			     struct mtk_plane_state *state);
+	void (*gamma_set)(struct mtk_ddp_comp *comp, u16 *r, u16 *g, u16 *b,
+			  u32 start, u32 size);
 };
 
 struct mtk_ddp_comp {
@@ -139,6 +141,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
 		comp->funcs->layer_config(comp, idx, state);
 }
 
+static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
+				     u16 *b, uint32_t start, uint32_t size)
+{
+	if (comp->funcs && comp->funcs->gamma_set)
+		comp->funcs->gamma_set(comp, r, g, b, start, size);
+}
+
 int mtk_ddp_comp_get_id(struct device_node *node,
 			enum mtk_ddp_comp_type comp_type);
 int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
-- 
1.7.9.5



From bibby.hsieh at mediatek.com  Mon Jun 13 19:55:53 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Tue, 14 Jun 2016 10:55:53 +0800
Subject: [PATCH 2/2] drm/mediatek: set mt8173 dithering function
In-Reply-To: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>
References: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>
Message-ID: <1465872953-47706-2-git-send-email-bibby.hsieh@mediatek.com>

Some panels only accept bpc (bit per color) 6-bit.
But, the default bpc in mt8173 display data path is 8-bit.
If we didn't enable dithering function to convert bpc,
display cannot show the smooth grayscale image.

In mt8173, the dithering function in OD (OverDrive) and
GAMMA module, we have to config them with
connector->display_mode.bpc when CRTC initial.

1. Clear the default value at *_DITHER_5 and *_DITHER_7 register.
2. Calculate the LSB_ERR_SHIFT bits and ADD_LSHIFT bits two values.
i.e. Input bpc of OD is 10 bits, we assume the bpc of panel is 6-bit,
so, we need to set 4-bit to LSB_ERR_SHIFT and ADD_LSHIFT bits respectively.
3. Then, set the OD or GAMMA to dithering mode depends on path-1 or path-2.

Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   18 +++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   44 +++++++++++++++++++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    6 ++--
 5 files changed, 64 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 8f62671f..019b7ca 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -103,7 +103,8 @@ static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
-			   unsigned int h, unsigned int vrefresh)
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc)
 {
 	if (w != 0 && h != 0)
 		writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index 5fb80cb..0df05f9 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -106,7 +106,8 @@ static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
-			    unsigned int height, unsigned int vrefresh)
+			    unsigned int height, unsigned int vrefresh,
+			    unsigned int bpc)
 {
 	unsigned int threshold;
 	unsigned int reg;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 1b38406..96c1b31 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -52,6 +52,7 @@ struct mtk_drm_crtc {
 	struct mtk_disp_mutex		*mutex;
 	unsigned int			ddp_comp_nr;
 	struct mtk_ddp_comp		**ddp_comp;
+	unsigned int			bpc;
 };
 
 struct mtk_crtc_state {
@@ -232,6 +233,8 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
 static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 {
 	struct drm_crtc *crtc = &mtk_crtc->base;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
 	unsigned int width, height, vrefresh;
 	int ret;
 	int i;
@@ -243,6 +246,15 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 	width = crtc->state->adjusted_mode.hdisplay;
 	height = crtc->state->adjusted_mode.vdisplay;
 	vrefresh = crtc->state->adjusted_mode.vrefresh;
+	drm_for_each_encoder(encoder, crtc->dev) {
+		if (encoder->crtc != crtc)
+			continue;
+		drm_for_each_connector(connector, crtc->dev) {
+			if (connector->encoder != encoder)
+				continue;
+			mtk_crtc->bpc = connector->display_info.bpc;
+		}
+	}
 
 	ret = pm_runtime_get_sync(crtc->dev->dev);
 	if (ret < 0) {
@@ -276,7 +288,8 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 
-		mtk_ddp_comp_config(comp, width, height, vrefresh);
+		mtk_ddp_comp_config(comp, width, height, vrefresh,
+				    mtk_crtc->bpc);
 		mtk_ddp_comp_start(comp);
 	}
 
@@ -475,7 +488,8 @@ void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
 	if (state->pending_config) {
 		mtk_ddp_comp_config(ovl, state->pending_width,
 				    state->pending_height,
-				    state->pending_vrefresh);
+				    state->pending_vrefresh,
+				    mtk_crtc->bpc);
 
 		state->pending_config = false;
 	}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 3fd5141..c574f6d 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -31,6 +31,10 @@
 #define DISP_OD_INTSTA				0x000c
 #define DISP_OD_CFG				0x0020
 #define DISP_OD_SIZE				0x0030
+#define DISP_OD_DITHER_5			0x0114
+#define DISP_OD_DITHER_7			0x011c
+#define DISP_OD_DITHER_15			0x013c
+#define DISP_OD_DITHER_16			0x0140
 
 #define DISP_REG_UFO_START			0x0000
 
@@ -52,14 +56,31 @@
 #define GAMMA_LUT_EN		BIT(1)
 
 #define	OD_RELAY_MODE		BIT(0)
+#define OD_DITHERING		BIT(2)
 
 #define	UFO_BYPASS		BIT(2)
 
 #define	COLOR_BYPASS_ALL	BIT(7)
 #define	COLOR_SEQ_SEL		BIT(13)
 
+#define DITHERING_ALGORITHM_EN	BIT(0)
+
+#define DITHERING_LSB_ERR_SHIFT_R(bits)		((bits) << 28)
+#define DITHERING_OVFLW_R(bits)			((bits) << 24)
+#define DITHERING_ADD_LSHIFT_R(bits)		((bits) << 20)
+#define DITHERING_INPUT_RSHIFT_R(bits)		((bits) << 16)
+#define DITHERING_LSB_ERR_SHIFT_B(bits)		((bits) << 28)
+#define DITHERING_OVFLW_B(bits)			((bits) << 24)
+#define DITHERING_ADD_LSHIFT_B(bits)		((bits) << 20)
+#define DITHERING_INPUT_RSHIFT_B(bits)		((bits) << 16)
+#define DITHERING_LSB_ERR_SHIFT_G(bits)		((bits) << 12)
+#define DITHERING_OVFLW_G(bits)			((bits) << 8)
+#define DITHERING_ADD_LSHIFT_G(bits)		((bits) << 4)
+#define DITHERING_INPUT_RSHIFT_G(bits)		(bits)
+
 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
-			     unsigned int h, unsigned int vrefresh)
+			     unsigned int h, unsigned int vrefresh,
+			     unsigned int bpc)
 {
 	writel(w, comp->regs + DISP_COLOR_WIDTH);
 	writel(h, comp->regs + DISP_COLOR_HEIGHT);
@@ -73,9 +94,25 @@ static void mtk_color_start(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
-			  unsigned int h, unsigned int vrefresh)
+			  unsigned int h, unsigned int vrefresh,
+			  unsigned int bpc)
 {
 	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
+	if (bpc < 10) {
+		writel(0, comp->regs + DISP_OD_DITHER_5);
+		writel(0, comp->regs + DISP_OD_DITHER_7);
+		writel(DITHERING_LSB_ERR_SHIFT_R(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_R(10 - bpc) |
+		       DITHERING_ALGORITHM_EN, comp->regs + DISP_OD_DITHER_15);
+		writel(DITHERING_LSB_ERR_SHIFT_B(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_B(10 - bpc) |
+		       DITHERING_LSB_ERR_SHIFT_G(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_G(10 - bpc),
+		       comp->regs + DISP_OD_DITHER_16);
+		writel(OD_DITHERING, comp->regs + DISP_OD_CFG);
+	} else {
+		writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
+	}
 }
 
 static void mtk_od_start(struct mtk_ddp_comp *comp)
@@ -90,7 +127,8 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
-			   unsigned int h, unsigned int vrefresh)
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc)
 {
 	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 0832179..02ca1be 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -64,7 +64,7 @@ struct mtk_ddp_comp;
 
 struct mtk_ddp_comp_funcs {
 	void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
-		       unsigned int h, unsigned int vrefresh);
+		       unsigned int h, unsigned int vrefresh, unsigned int bpc);
 	void (*start)(struct mtk_ddp_comp *comp);
 	void (*stop)(struct mtk_ddp_comp *comp);
 	void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
@@ -88,10 +88,10 @@ struct mtk_ddp_comp {
 
 static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
 				       unsigned int w, unsigned int h,
-				       unsigned int vrefresh)
+				       unsigned int vrefresh, unsigned int bpc)
 {
 	if (comp->funcs && comp->funcs->config)
-		comp->funcs->config(comp, w, h, vrefresh);
+		comp->funcs->config(comp, w, h, vrefresh, bpc);
 }
 
 static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
-- 
1.7.9.5



From daniel at ffwll.ch  Mon Jun 13 22:43:22 2016
From: daniel at ffwll.ch (Daniel Vetter)
Date: Tue, 14 Jun 2016 07:43:22 +0200
Subject: [PATCH 1/2] drm/mediatek: Add gamma correction
In-Reply-To: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>
References: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>
Message-ID: <20160614054322.GL1338@phenom.ffwll.local>

On Tue, Jun 14, 2016 at 10:55:52AM +0800, Bibby Hsieh wrote:
> Apply gamma function to correct brightness values.
> It applies arbitrary mapping curve to compensate the
> incorrect transfer function of the panel.
> 
> Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>

I think it would be much better to use the new atomic color management
support, which includes gamma. See drm_crtc_enable_color_mgmt.
-Daniel

> ---
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   12 ++++++
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   56 ++++++++++++++++++++++++++-
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    9 +++++
>  4 files changed, 77 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> index 24aa3ba..1b38406 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> @@ -127,6 +127,16 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
>  	state->base.crtc = crtc;
>  }
>  
> +static void mtk_crtc_gamma_set(struct drm_crtc *crtc, u16 *r,
> +			       u16 *g, u16 *b, uint32_t start, uint32_t size)
> +{
> +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> +	unsigned int i;
> +
> +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> +		mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], r, g, b, start, size);
> +}
> +
>  static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
>  {
>  	struct mtk_crtc_state *state;
> @@ -418,6 +428,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
>  	.reset			= mtk_drm_crtc_reset,
>  	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
>  	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
> +	.gamma_set		= mtk_crtc_gamma_set,
>  };
>  
>  static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
> @@ -569,6 +580,7 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
>  	if (ret < 0)
>  		goto unprepare;
>  
> +	drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
>  	priv->crtc[pipe] = &mtk_crtc->base;
>  	priv->num_pipes++;
>  
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> index 81e5566..d332564 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> @@ -19,6 +19,7 @@
>  #include "mtk_drm_plane.h"
>  
>  #define OVL_LAYER_NR	4
> +#define MTK_LUT_SIZE	512
>  
>  int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
>  void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> index 3970fcf..3fd5141 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> @@ -24,6 +24,7 @@
>  #include "mtk_drm_drv.h"
>  #include "mtk_drm_plane.h"
>  #include "mtk_drm_ddp_comp.h"
> +#include "mtk_drm_crtc.h"
>  
>  #define DISP_OD_EN				0x0000
>  #define DISP_OD_INTEN				0x0008
> @@ -38,6 +39,18 @@
>  #define DISP_COLOR_WIDTH			0x0c50
>  #define DISP_COLOR_HEIGHT			0x0c54
>  
> +#define DISP_AAL_EN				0x000
> +#define DISP_AAL_SIZE				0x030
> +
> +#define DISP_GAMMA_CFG				0x020
> +#define DISP_GAMMA_LUT				0x700
> +
> +#define LUT_10BIT_MASK				0x3ff
> +
> +#define AAL_EN			BIT(0)
> +
> +#define GAMMA_LUT_EN		BIT(1)
> +
>  #define	OD_RELAY_MODE		BIT(0)
>  
>  #define	UFO_BYPASS		BIT(2)
> @@ -76,6 +89,40 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
>  	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
>  }
>  
> +static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
> +			   unsigned int h, unsigned int vrefresh)
> +{
> +	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
> +}
> +
> +static void mtk_aal_start(struct mtk_ddp_comp *comp)
> +{
> +	writel(AAL_EN, comp->regs  + DISP_AAL_EN);
> +}
> +
> +static void mtk_aal_stop(struct mtk_ddp_comp *comp)
> +{
> +	writel_relaxed(0x0, comp->regs  + DISP_AAL_EN);
> +}
> +
> +static void mtk_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
> +			  u16 *b, uint32_t start, uint32_t size)
> +{
> +	int i;
> +	unsigned int lut;
> +	int end = (start + size > MTK_LUT_SIZE) ? MTK_LUT_SIZE : start + size;
> +	void __iomem *lut_base;
> +
> +	writel(GAMMA_LUT_EN, comp->regs + DISP_GAMMA_CFG);
> +	lut_base = comp->regs + DISP_GAMMA_LUT;
> +	for (i = start; i < end; i++) {
> +		lut = (((r[i] >> 6) & LUT_10BIT_MASK) << 20) +
> +			(((g[i] >> 6) & LUT_10BIT_MASK) << 10) +
> +			((b[i] >> 6) & LUT_10BIT_MASK);
> +		writel(lut, (lut_base + i * 4));
> +	}
> +}
> +
>  static const struct mtk_ddp_comp_funcs ddp_color = {
>  	.config = mtk_color_config,
>  	.start = mtk_color_start,
> @@ -90,6 +137,13 @@ static const struct mtk_ddp_comp_funcs ddp_ufoe = {
>  	.start = mtk_ufoe_start,
>  };
>  
> +static const struct mtk_ddp_comp_funcs ddp_aal = {
> +	.gamma_set = mtk_gamma_set,
> +	.config = mtk_aal_config,
> +	.start = mtk_aal_start,
> +	.stop = mtk_aal_stop,
> +};
> +
>  static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
>  	[MTK_DISP_OVL] = "ovl",
>  	[MTK_DISP_RDMA] = "rdma",
> @@ -112,7 +166,7 @@ struct mtk_ddp_comp_match {
>  };
>  
>  static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> -	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
> +	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
>  	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
>  	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
>  	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> index 6b13ba9..0832179 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> @@ -73,6 +73,8 @@ struct mtk_ddp_comp_funcs {
>  	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
>  	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
>  			     struct mtk_plane_state *state);
> +	void (*gamma_set)(struct mtk_ddp_comp *comp, u16 *r, u16 *g, u16 *b,
> +			  u32 start, u32 size);
>  };
>  
>  struct mtk_ddp_comp {
> @@ -139,6 +141,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
>  		comp->funcs->layer_config(comp, idx, state);
>  }
>  
> +static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
> +				     u16 *b, uint32_t start, uint32_t size)
> +{
> +	if (comp->funcs && comp->funcs->gamma_set)
> +		comp->funcs->gamma_set(comp, r, g, b, start, size);
> +}
> +
>  int mtk_ddp_comp_get_id(struct device_node *node,
>  			enum mtk_ddp_comp_type comp_type);
>  int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
> -- 
> 1.7.9.5
> 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch


From hs.liao at mediatek.com  Tue Jun 14 00:44:28 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Tue, 14 Jun 2016 15:44:28 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <57583B45.2080504@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
Message-ID: <1465890268.7191.13.camel@mtksdaap41>

Hi Matthias,

On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
> 
> On 08/06/16 14:25, Horng-Shyang Liao wrote:
> > Hi Matthias,
> >
> > On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> >>
> >> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> >>> Hi Matthias,
> >>>
> >>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> >>>>
> >>>> On 03/06/16 15:11, Matthias Brugger wrote:
> >>>>>
> >>>>>
> >>>> [...]
> >>>>
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>>>>>> task->command_size,
> >>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>>>>>> +}
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>>>>>> +{
> >>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>>>>>> +    bool err;
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>>>>>> +        err = true;
> >>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>>>>>> +        err = false;
> >>>>>>>>>>>>>> +    else
> >>>>>>>>>>>>>> +        return;
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>>>>>> +
> >>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>>>>>> +                 list_entry) {
> >>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>>>>>> commands in the
> >>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>>>>>> guarantees
> >>>>>>>>>>>>> about dma_handle?
> >>>>>>>>>>>>
> >>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>>>>>> 2. Yes.
> >>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>>>>>> calling
> >>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>>>>>> use this
> >>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>>>>>> currently
> >>>>>>>>>>> executing. So I think this mecanism is not working.
> >>>>>>>>>>
> >>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>>>>>      From CPU's point of view, tasks are linked by the busy list.
> >>>>>>>>>>      From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>>>>>
> >>>>>>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>>>>>
> >>>>>>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>>>>>> when it is running ISR.
> >>>>>>>>>>
> >>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>>>>>> we need to do it in a thread context.
> >>>>>>>>
> >>>>>>>> Actually, our initial implementation is similar to your suggestion,
> >>>>>>>> but display needs CMDQ to return callback function very precisely,
> >>>>>>>> else display will drop frame.
> >>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>>>>>> and CMDQ needs to call callback function in ISR.
> >>>>>>>> If we defer callback to workqueue, the time interval may be larger than
> >>>>>>>> 32 ms.sometimes.
> >>>>>>>>
> >>>>>>>
> >>>>>>> I think the problem is, that you implemented the workqueue as a ordered
> >>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
> >>>>>>> you need the workqueue to be ordered. Can you please explain.
> >>>>>>
> >>>>>> The order should be kept.
> >>>>>> Let me use mouse cursor as an example.
> >>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> >>>>>> and task 3 means point C, our expected result is A -> B -> C.
> >>>>>> If the order is not kept, the result could become A -> C -> B.
> >>>>>>
> >>>>>
> >>>>> Got it, thanks for the clarification.
> >>>>>
> >>>>
> >>>> I think a way to get rid of the workqueue is to use a timer, which gets
> >>>> programmed to the time a timeout in the first task in the busy list
> >>>> would happen. Everytime we update the busy list (e.g. because of task
> >>>> got finished by the thread), we update the timer. When the timer
> >>>> triggers, which hopefully won't happen too often, we return timeout on
> >>>> the busy list elements, until the time is lower then the actual time.
> >>>>
> >>>> At least with this we can reduce the data structures in this driver and
> >>>> make it more lightweight.
> >>>
> >>>   From my understanding, your proposed method can handle timeout case.
> >>>
> >>> However, the workqueue is also in charge of releasing tasks.
> >>> Do you take releasing tasks into consideration by using the proposed
> >>> timer method?
> >>> Furthermore, I think the code will become more complex if we also use
> >>> timer to implement releasing tasks.
> >>>
> >>
> >> Can't we call
> >>           clk_disable_unprepare(cmdq->clock);
> >>           cmdq_task_release(task);
> >> after invoking the callback?
> >
> > Do you mean just call these two functions in ISR?
> > My major concern is dma_free_coherent() and kfree() in
> > cmdq_task_release(task).
> 
> Why do we need the dma calls at all? Can't we just calculate the 
> physical address using __pa(x)?

I prefer to use dma_map_single/dma_unmap_single.

> > Therefore, your suggestion is to use GFP_ATOMIC for both
> > dma_alloc_coherent() and kzalloc(). Right?
> 
> I don't think we need GFP_ATOMIC, the critical path will just free the 
> memory.

I tested these two functions, and kfree was safe.
However, dma_free_coherent raised BUG.
BUG: failure at
/mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
1512 void vunmap(const void *addr)
1513 {
1514         BUG_ON(in_interrupt());		// <-- here
1515         might_sleep();
1516         if (addr)
1517                 __vunmap(addr, 0);
1518 }
1519 EXPORT_SYMBOL(vunmap);

Therefore, I plan to use kmalloc + dma_map_single instead of
dma_alloc_coherent, and dma_unmap_single + kfree instead of
dma_free_coherent.

What do you think about the function replacement?

> > If so, I can try to implement timeout by timer, and discuss with you
> > if I have further questions.
> >
> 
> Sounds good :)
> 
> Thanks,
> Matthias

Thanks,
HS

> >> Regrading the clock, wouldn't it be easier to handle the clock
> >> enable/disable depending on the state of task_busy_list? I suppose we
> >> can't as we would need to check the task_busy_list of all threads, right?
> >>
> >> Regards,
> >> Matthias
> >
> > Thanks,
> > HS
> >




From djkurtz at chromium.org  Tue Jun 14 01:09:23 2016
From: djkurtz at chromium.org (Daniel Kurtz)
Date: Tue, 14 Jun 2016 16:09:23 +0800
Subject: [PATCH 2/2 v16] drm/bridge: Add I2C based driver for ps8640 bridge
In-Reply-To: <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>
References: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>
 <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>
Message-ID: <CAGS+omDpakAsjijbhUG9gJZ6WA=+UBJ2fhhmp-ZEz7CtK8AHCw@mail.gmail.com>

Hi Jitao,

On Thu, Jun 2, 2016 at 5:57 PM, Jitao Shi <jitao.shi at mediatek.com> wrote:
>
> This patch adds drm_bridge driver for parade DSI to eDP bridge chip.
>
> Signed-off-by: Jitao Shi <jitao.shi at mediatek.com>
> Reviewed-by: Daniel Kurtz <djkurtz at chromium.org>
> ---
> Changes since v15:
>  - Drop drm_connector_(un)register calls from parade ps8640.
>    The main DRM driver mtk_drm_drv now calls
>    drm_connector_register_all() after drm_dev_register() in the
>    mtk_drm_bind() function. That function should iterate over all
>    connectors and call drm_connector_register() for each of them.
>    So, remove drm_connector_(un)register calls from parade ps8640.

[snip...]

> +static void ps8640_pre_enable(struct drm_bridge *bridge)
> +{
> +       struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +       struct i2c_client *client = ps_bridge->page[2];
> +       int err;
> +       u8 set_vdo_done;
> +       ktime_t timeout;
> +
> +       if (ps_bridge->in_fw_update)
> +               return;
> +
> +       if (ps_bridge->enabled)
> +               return;
> +
> +       err = drm_panel_prepare(ps_bridge->panel);
> +       if (err < 0) {
> +               DRM_ERROR("failed to prepare panel: %d\n", err);
> +               return;
> +       }
> +

(1) For patch v10, Philipp Zabel commented that gpio_slp_n &
gpio_rst_n are both active low, and that the device tree should
contain a reset-gpios property with the GPIO_ACTIVE_LOW flag set.
(2) However, you did change the the reset logic from v10 -> v11, but
it isn't clear why (nor mentioned in the patch notes).

v10 (https://patchwork.kernel.org/patch/8357851/) had:

+ gpiod_set_value(ps_bridge->gpio_slp_n, 1);
+ gpiod_set_value(ps_bridge->gpio_rst_n, 0);
+
+ err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
+    ps_bridge->supplies);
+ if (err < 0) {
+ DRM_ERROR("cannot enable regulators %d\n", err);
+ goto err_panel_unprepare;
+ }
+
+ usleep_range(500, 700);
+ gpiod_set_value(ps_bridge->gpio_rst_n, 1);

In other words:
  (a) de-assert power down
  (b) assert reset
  (c) enable 1.2 & 3.3 regulators
  (d) <wait for  regulator delay> (aka regulator-ramp-delay in device-tree)
  (e) wait an additional 2 ms (as requested by Parade for ps8640 to stabilize?)
  (f) de-assert reset
  (g) wait 200 ms (for ps8640 FW to load?)

This mostly made sense to me, except for step (a)... I'm not sure why
you de-assert power down before enabling the regulators.  It seems
like you'd want to do that later, maybe after reset (can you ask
Paradetech?).

Now (as of v11) it has changed to:

> +       err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
> +                                   ps_bridge->supplies);
> +       if (err < 0) {
> +               DRM_ERROR("cannot enable regulators %d\n", err);
> +               goto err_panel_unprepare;
> +       }
> +
> +       gpiod_set_value(ps_bridge->gpio_slp_n, 1);
> +       gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> +       usleep_range(2000, 2500);
> +       gpiod_set_value(ps_bridge->gpio_rst_n, 1);

Two additional comments:

(3) if you correctly configure these gpios as GPIO_ACTIVE_LOW, you can
drop the "_n" suffix, which will make the driver code easier to
understand.
(4) "gpio_slp_n" is called "PD#" by the PS8640 datasheet, so a better
name might be: "gpio_power_down".

Thanks,
-Dan


From wuchengli at chromium.org  Tue Jun 14 01:44:36 2016
From: wuchengli at chromium.org (=?UTF-8?B?V3UtQ2hlbmcgTGkgKOadjuWLmeiqoCk=?=)
Date: Tue, 14 Jun 2016 16:44:36 +0800
Subject: [PATCH v3 0/9] Add MT8173 Video Decoder Driver
In-Reply-To: <575746EE.3030706@cisco.com>
References: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
 <20160607112235.475c2e4c@recife.lan> <575746EE.3030706@cisco.com>
Message-ID: <CAOMLVLjnktOoDWseZBwsFwLbym8mX7CQzOHW3tbum1yWJjfFNA@mail.gmail.com>

On Wed, Jun 8, 2016 at 6:13 AM, Hans Verkuil <hansverk at cisco.com> wrote:
>
>
> On 06/07/2016 11:22 PM, Mauro Carvalho Chehab wrote:
>>
>> Em Mon, 30 May 2016 20:29:14 +0800
>> Tiffany Lin <tiffany.lin at mediatek.com> escreveu:
>>
>>> ==============
>>>   Introduction
>>> ==============
>>>
>>> The purpose of this series is to add the driver for video codec hw
>>> embedded in the Mediatek's MT8173 SoCs.
>>> Mediatek Video Codec is able to handle video decoding of in a range of
>>> formats.
>>>
>>> This patch series add Mediatek block format V4L2_PIX_FMT_MT21, the
>>> decoder driver will decoded bitstream to
>>> V4L2_PIX_FMT_MT21 format.
>>>
>>> This patch series rely on MTK VPU driver in patch series "Add MT8173
>>> Video Encoder Driver and VPU Driver"[1]
>>> and patch "CHROMIUM: v4l: Add V4L2_PIX_FMT_VP9 definition"[2] for VP9
>>> support.
>>> Mediatek Video Decoder driver rely on VPU driver to load, communicate
>>> with VPU.
>>>
>>> Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI
>>> both have been merged in v4.6-rc1.
>>>
>>> [1]https://patchwork.linuxtv.org/patch/33734/
>>> [2]https://chromium-review.googlesource.com/#/c/245241/
>>
>>
>> Hmm... I'm not seeing the firmware for this driver at the
>> linux-firmware tree:
>>
>> https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/log/
Tiffany. Can you check the license and add the firmware to linux-firmware?

For the information, both encoder and decoder drivers require the
firmware to work.
>>
>> Nor I'm seeing any pull request for them. Did you send it?
>> I'll only merge the driver upstream after seeing such pull request.
>
>
> Mauro, are you confusing the decoder and encoder driver? I haven't
> thoroughly reviewed the decoder driver
> yet, so there is no pull request for the decoder driver.
>
> The only pull request I made was for the encoder driver.
>
> Regards,
>
>         Hans
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


From matthias.bgg at gmail.com  Tue Jun 14 03:17:30 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Tue, 14 Jun 2016 12:17:30 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1465890268.7191.13.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41>
Message-ID: <575FD9BA.8040708@gmail.com>



On 14/06/16 09:44, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
>>
>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
>>> Hi Matthias,
>>>
>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>>>
>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>>>> Hi Matthias,
>>>>>
>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>>>
>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>>>
>>>>>>>
>>>>>> [...]
>>>>>>
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>>>> calling
>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>>>> use this
>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>>>> currently
>>>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>>>
>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>>>       From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>>>       From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>>>
>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>>>
>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>>>> when it is running ISR.
>>>>>>>>>>>>
>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>>>> we need to do it in a thread context.
>>>>>>>>>>
>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>>>> else display will drop frame.
>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>>>> 32 ms.sometimes.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>>>
>>>>>>>> The order should be kept.
>>>>>>>> Let me use mouse cursor as an example.
>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>>>
>>>>>>>
>>>>>>> Got it, thanks for the clarification.
>>>>>>>
>>>>>>
>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>>>> programmed to the time a timeout in the first task in the busy list
>>>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>>>> got finished by the thread), we update the timer. When the timer
>>>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>>>> the busy list elements, until the time is lower then the actual time.
>>>>>>
>>>>>> At least with this we can reduce the data structures in this driver and
>>>>>> make it more lightweight.
>>>>>
>>>>>    From my understanding, your proposed method can handle timeout case.
>>>>>
>>>>> However, the workqueue is also in charge of releasing tasks.
>>>>> Do you take releasing tasks into consideration by using the proposed
>>>>> timer method?
>>>>> Furthermore, I think the code will become more complex if we also use
>>>>> timer to implement releasing tasks.
>>>>>
>>>>
>>>> Can't we call
>>>>            clk_disable_unprepare(cmdq->clock);
>>>>            cmdq_task_release(task);
>>>> after invoking the callback?
>>>
>>> Do you mean just call these two functions in ISR?
>>> My major concern is dma_free_coherent() and kfree() in
>>> cmdq_task_release(task).
>>
>> Why do we need the dma calls at all? Can't we just calculate the
>> physical address using __pa(x)?
>
> I prefer to use dma_map_single/dma_unmap_single.
>

Can you please elaborate why you need this. We don't do dma, so we 
should not use dma memory for this.

>>> Therefore, your suggestion is to use GFP_ATOMIC for both
>>> dma_alloc_coherent() and kzalloc(). Right?
>>
>> I don't think we need GFP_ATOMIC, the critical path will just free the
>> memory.
>
> I tested these two functions, and kfree was safe.
> However, dma_free_coherent raised BUG.
> BUG: failure at
> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!

Just a general hint. Please try to evaluate on a recent kernel. It looks 
like as if you tried this on a v3.18 based one.

Best regards,
Matthias

> 1512 void vunmap(const void *addr)
> 1513 {
> 1514         BUG_ON(in_interrupt());		// <-- here
> 1515         might_sleep();
> 1516         if (addr)
> 1517                 __vunmap(addr, 0);
> 1518 }
> 1519 EXPORT_SYMBOL(vunmap);
>
> Therefore, I plan to use kmalloc + dma_map_single instead of
> dma_alloc_coherent, and dma_unmap_single + kfree instead of
> dma_free_coherent.
>
> What do you think about the function replacement?
>
>>> If so, I can try to implement timeout by timer, and discuss with you
>>> if I have further questions.
>>>
>>
>> Sounds good :)
>>
>> Thanks,
>> Matthias
>
> Thanks,
> HS
>
>>>> Regrading the clock, wouldn't it be easier to handle the clock
>>>> enable/disable depending on the state of task_busy_list? I suppose we
>>>> can't as we would need to check the task_busy_list of all threads, right?
>>>>
>>>> Regards,
>>>> Matthias
>>>
>>> Thanks,
>>> HS
>>>
>
>


From tiffany.lin at mediatek.com  Tue Jun 14 04:08:08 2016
From: tiffany.lin at mediatek.com (tiffany lin)
Date: Tue, 14 Jun 2016 19:08:08 +0800
Subject: [PATCH v3 0/9] Add MT8173 Video Decoder Driver
In-Reply-To: <575746EE.3030706@cisco.com>
References: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
 <20160607112235.475c2e4c@recife.lan> <575746EE.3030706@cisco.com>
Message-ID: <1465902488.27938.7.camel@mtksdaap41>

Hi Mauro,


On Wed, 2016-06-08 at 07:13 +0900, Hans Verkuil wrote:
> 
> On 06/07/2016 11:22 PM, Mauro Carvalho Chehab wrote:
> > Em Mon, 30 May 2016 20:29:14 +0800
> > Tiffany Lin <tiffany.lin at mediatek.com> escreveu:
> >
> >> ==============
> >>   Introduction
> >> ==============
> >>
> >> The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
> >> Mediatek Video Codec is able to handle video decoding of in a range of formats.
> >>
> >> This patch series add Mediatek block format V4L2_PIX_FMT_MT21, the decoder driver will decoded bitstream to
> >> V4L2_PIX_FMT_MT21 format.
> >>
> >> This patch series rely on MTK VPU driver in patch series "Add MT8173 Video Encoder Driver and VPU Driver"[1]
> >> and patch "CHROMIUM: v4l: Add V4L2_PIX_FMT_VP9 definition"[2] for VP9 support.
> >> Mediatek Video Decoder driver rely on VPU driver to load, communicate with VPU.
> >>
> >> Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI both have been merged in v4.6-rc1.
> >>
> >> [1]https://patchwork.linuxtv.org/patch/33734/
> >> [2]https://chromium-review.googlesource.com/#/c/245241/
> >
> > Hmm... I'm not seeing the firmware for this driver at the
> > linux-firmware tree:
> > 	https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/log/
> >
> > Nor I'm seeing any pull request for them. Did you send it?
> > I'll only merge the driver upstream after seeing such pull request.
> 
Sorry, I am not familiar with how to upstream firmware.
Do you mean we need to upstream vpu firmware first before merge encoder
driver upstream?

In
https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/tree/README, it mentions that 
"To submit firmware to this repository, please send either a git binary
diff or preferably a git pull request to: linux-firmware at kernel.org and
also cc: to related mailing lists."

How we made a git pull request to linux-firmware at kernel.org? 
How we find out related mailing lists?

best regards,
Tiffany

> Mauro, are you confusing the decoder and encoder driver? I haven't thoroughly reviewed the decoder driver
> yet, so there is no pull request for the decoder driver.
> 
> The only pull request I made was for the encoder driver.
> 
> Regards,
> 
> 	Hans




From hs.liao at mediatek.com  Tue Jun 14 05:07:43 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Tue, 14 Jun 2016 20:07:43 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <575FD9BA.8040708@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
Message-ID: <1465906063.20796.20.camel@mtksdaap41>

Hi Matthias,

On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
> 
> On 14/06/16 09:44, Horng-Shyang Liao wrote:
> > Hi Matthias,
> >
> > On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
> >>
> >> On 08/06/16 14:25, Horng-Shyang Liao wrote:
> >>> Hi Matthias,
> >>>
> >>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> >>>>
> >>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> >>>>> Hi Matthias,
> >>>>>
> >>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> >>>>>>
> >>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
> >>>>>>>
> >>>>>>>
> >>>>>> [...]
> >>>>>>
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>>>>>>>> task->command_size,
> >>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>>>>>>>> +}
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>>>>>>>> +{
> >>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>>>>>>>> +    bool err;
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>>>>>>>> +        err = true;
> >>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>>>>>>>> +        err = false;
> >>>>>>>>>>>>>>>> +    else
> >>>>>>>>>>>>>>>> +        return;
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>>>>>>>> +                 list_entry) {
> >>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>>>>>>>> commands in the
> >>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>>>>>>>> guarantees
> >>>>>>>>>>>>>>> about dma_handle?
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>>>>>>>> 2. Yes.
> >>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>>>>>>>> calling
> >>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>>>>>>>> use this
> >>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>>>>>>>> currently
> >>>>>>>>>>>>> executing. So I think this mecanism is not working.
> >>>>>>>>>>>>
> >>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>>>>>>>       From CPU's point of view, tasks are linked by the busy list.
> >>>>>>>>>>>>       From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>>>>>>>
> >>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>>>>>>>
> >>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>>>>>>>> when it is running ISR.
> >>>>>>>>>>>>
> >>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>>>>>>>> we need to do it in a thread context.
> >>>>>>>>>>
> >>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
> >>>>>>>>>> but display needs CMDQ to return callback function very precisely,
> >>>>>>>>>> else display will drop frame.
> >>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>>>>>>>> and CMDQ needs to call callback function in ISR.
> >>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
> >>>>>>>>>> 32 ms.sometimes.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
> >>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
> >>>>>>>>> you need the workqueue to be ordered. Can you please explain.
> >>>>>>>>
> >>>>>>>> The order should be kept.
> >>>>>>>> Let me use mouse cursor as an example.
> >>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> >>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
> >>>>>>>> If the order is not kept, the result could become A -> C -> B.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Got it, thanks for the clarification.
> >>>>>>>
> >>>>>>
> >>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
> >>>>>> programmed to the time a timeout in the first task in the busy list
> >>>>>> would happen. Everytime we update the busy list (e.g. because of task
> >>>>>> got finished by the thread), we update the timer. When the timer
> >>>>>> triggers, which hopefully won't happen too often, we return timeout on
> >>>>>> the busy list elements, until the time is lower then the actual time.
> >>>>>>
> >>>>>> At least with this we can reduce the data structures in this driver and
> >>>>>> make it more lightweight.
> >>>>>
> >>>>>    From my understanding, your proposed method can handle timeout case.
> >>>>>
> >>>>> However, the workqueue is also in charge of releasing tasks.
> >>>>> Do you take releasing tasks into consideration by using the proposed
> >>>>> timer method?
> >>>>> Furthermore, I think the code will become more complex if we also use
> >>>>> timer to implement releasing tasks.
> >>>>>
> >>>>
> >>>> Can't we call
> >>>>            clk_disable_unprepare(cmdq->clock);
> >>>>            cmdq_task_release(task);
> >>>> after invoking the callback?
> >>>
> >>> Do you mean just call these two functions in ISR?
> >>> My major concern is dma_free_coherent() and kfree() in
> >>> cmdq_task_release(task).
> >>
> >> Why do we need the dma calls at all? Can't we just calculate the
> >> physical address using __pa(x)?
> >
> > I prefer to use dma_map_single/dma_unmap_single.
> >
> 
> Can you please elaborate why you need this. We don't do dma, so we 
> should not use dma memory for this.

We need a buffer to share between CPU and GCE, so we do need DMA.
CPU is in charge of writing GCE commands into this buffer.
GCE is in charge of reading and running GCE commands from this buffer.
When we chain CMDQ tasks, we also need to modify GCE JUMP command.
Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.

However, if we want to use timer to handle timeout, we need to release
memory in ISR.
In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
instead of dma_alloc_coherent/dma_free_coherent is an alternative
solution, but taking care the synchronization between cache and memory
is the expected overhead.

> >>> Therefore, your suggestion is to use GFP_ATOMIC for both
> >>> dma_alloc_coherent() and kzalloc(). Right?
> >>
> >> I don't think we need GFP_ATOMIC, the critical path will just free the
> >> memory.
> >
> > I tested these two functions, and kfree was safe.
> > However, dma_free_coherent raised BUG.
> > BUG: failure at
> > /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
> 
> Just a general hint. Please try to evaluate on a recent kernel. It looks 
> like as if you tried this on a v3.18 based one.

This driver should be backward compatible to v3.18 for a MTK project.

> Best regards,
> Matthias

Thanks,
HS

> > 1512 void vunmap(const void *addr)
> > 1513 {
> > 1514         BUG_ON(in_interrupt());		// <-- here
> > 1515         might_sleep();
> > 1516         if (addr)
> > 1517                 __vunmap(addr, 0);
> > 1518 }
> > 1519 EXPORT_SYMBOL(vunmap);
> >
> > Therefore, I plan to use kmalloc + dma_map_single instead of
> > dma_alloc_coherent, and dma_unmap_single + kfree instead of
> > dma_free_coherent.
> >
> > What do you think about the function replacement?
> >
> >>> If so, I can try to implement timeout by timer, and discuss with you
> >>> if I have further questions.
> >>>
> >>
> >> Sounds good :)
> >>
> >> Thanks,
> >> Matthias
> >
> > Thanks,
> > HS
> >
> >>>> Regrading the clock, wouldn't it be easier to handle the clock
> >>>> enable/disable depending on the state of task_busy_list? I suppose we
> >>>> can't as we would need to check the task_busy_list of all threads, right?
> >>>>
> >>>> Regards,
> >>>> Matthias
> >>>
> >>> Thanks,
> >>> HS
> >>>
> >
> >




From jorge.ramirez-ortiz at linaro.org  Tue Jun 14 08:50:49 2016
From: jorge.ramirez-ortiz at linaro.org (Jorge Ramirez-Ortiz)
Date: Tue, 14 Jun 2016 11:50:49 -0400
Subject: [PATCH v5 0/2] MTK Smart Device Gen1 NAND Driver
Message-ID: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>

  Fixes/Changes on v5 relative to: 
  --------------------------------

  tree		: https://github.com/bbrezillon/linux-0day
  branch	: nand/next
  commit	:
 		'commit "mtd: nand: sunxi: fix return value check..."'
		 Author: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
		 Date: Mon Jun 13 14:27:18 2016 +0000

  Patch v5
  --------
  mtd: mediatek: device tree bindings for MTK Smart Device Gen1 NAND
       - remove unused property
  
  mtd: mediatek: driver for MTK Smart Device Gen1 NAND
       - clean up macros and clarify semantics.
       - replace codec requests with operations.
       - replace semaphore with mutex on ecc accesses.
       - addtional comments to some functions.
       - explicit error for 16bits buswidth (not supported).
       - resolve endian issues.
       - determine active ecc codec by register inspection. 

  Patch v4
  --------
  mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
       - nand-on-flash-bbt, nand-ecc-mode, nand-ecc-step-size and
         nand-ecc-strength are now optional parameters
       - spare-per-sector removed from DT settings.
       - extend documentation.

  mtd: mediatek: driver for MTK Smart Device Gen1 NAND
       - rename functions (semantic issues).
       - add lock to ECC engine.
       - rework ECC interface (single enable/disable function).
       - rework bad block mark (now stored in OOB).
       - fix stat reporting.
       - rework FDM calculation.
       - handle optional DT settings (strength, mode, ecc step size).
       - spare per sector clamped to valid register values (hw spec).
		
  Patch v3
  --------
  mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
	- rename nandc (nand controller)

  mtd: mediatek: driver for MTK Smart Device Gen1 NAND
 	- rename source and header files
	- register definitions move to source files
	- ecc interface dropped
	- blank lines between functions (checkpatch.pl doesn't catch them)
	- ecc_check function renamed ecc_get_stats
	- ecc->dev assigned during probe()
	- generic ecc control function replaced by individual functions
	- required ecc parameters passed in ecc specific structures
	- fix memory corruption (size 0 array)
	- clarify nand offset/ptr function helpers (naming corrected)
	- move ecc config call to select_chip()
	- comment on hw issue workaround during read_byte
	- implement write_buf
	- remove switch_oob
	- fix suspend/resume operation
	- make nand-ecc-mode mandatory in device tree 

  RFC v2
  ------
 
  mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
	- split nand flash interface and ecc engine

  mtd: mediatek: driver for MTK Smart Device Gen1 NAND
	- split host and ecc in two separate drivers.
	- expose ecc layout through mtd_ooblayout_ops.
	- fix wait loops.
	- use device tree to describe ecc strength and ecc size.
	- use device tree to describe CS.
	- replace cmdfunc with cmd_ctrl.
	- fix read byte interface.

  dont enable controller in mt2701.dtsi
	- requires additional device tree changes. This will be done in
	  the near future.

Overview:
=========

The following patch-set adds support for Mediatek's Smart Device
Gen1 NAND controller.

* Controller expected layout:
-----------------------------

The controller expects the OOB information relevant to each sector
within the page to follow immediately the data field.

For example for a 4KiB page with 1KiB sectors and 224B of OOB information,
the controller would expect the following layout.

-----------------------------------------------------
| 1KiB | 56B | 1KiB | 56B | 1KiB | 56B | 1KiB | 56B |
-----------------------------------------------------

This implies that the following actions are required:

- raw writes:
  to match the controller expected layout, the driver uses the CPU and
  a private bounce buffer to reassemble the page before the actual
  writes.
  
- non raw writes:
  ecc is hardware generated and data DMA'd in a single transfer.

Bad Block mark
==============
The bad block mark is stored in the OOB region (one byte)

Controller limitations:
=======================

 * dma chaning not supported:
	To guarantee that the pages corresponding to the buffers are contiguous
	in memory the driver enables NAND_USE_BOUNCE_BUFFER.
 * spare per sector: 16B to 128B
 * sector sizes: 512B or 1024B
 * programable page sizes: 512B, 2KB, 4KB, 8K, 16K 

Tests:
======

* UBIFS support has been validated on 512MiB device (iozone, dd)
* All drivers/mtd/tests/* pass clean.
* Speed tests:
  eraseblock write speed:	7563 KiB/s
  eraseblock read speed:	10904 KiB/s
  page write speed:		7497 KiB/s
  page read speed:		10864 KiB/s
  2 page write speed:		7527 KiB/s
  2 page read speed:		10881 KiB/s
  erase speed:			103157 KiB/s
  2x multi-block erase speed:	357445 KiB/s
  4x multi-block erase speed:	362944 KiB/s
  8x multi-block erase speed:	363805 KiB/s
  16x multi-block erase speed:	364237 KiB/s
  32x multi-block erase speed:	364670 KiB/s
  64x multi-block erase speed:	365104 KiB/s

Jorge Ramirez-Ortiz (2):
  mtd: mediatek: device tree bindings for MTK Smart Device Gen1 NAND
  mtd: mediatek: driver for MTK Smart Device Gen1 NAND

 Documentation/devicetree/bindings/mtd/mtk-nand.txt |  160 +++
 drivers/mtd/nand/Kconfig                           |    7 +
 drivers/mtd/nand/Makefile                          |    1 +
 drivers/mtd/nand/mtk_ecc.c                         |  528 +++++++
 drivers/mtd/nand/mtk_ecc.h                         |   50 +
 drivers/mtd/nand/mtk_nand.c                        | 1509 ++++++++++++++++++++
 6 files changed, 2255 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/mtk-nand.txt
 create mode 100644 drivers/mtd/nand/mtk_ecc.c
 create mode 100644 drivers/mtd/nand/mtk_ecc.h
 create mode 100644 drivers/mtd/nand/mtk_nand.c

-- 
2.7.4



From jorge.ramirez-ortiz at linaro.org  Tue Jun 14 08:50:50 2016
From: jorge.ramirez-ortiz at linaro.org (Jorge Ramirez-Ortiz)
Date: Tue, 14 Jun 2016 11:50:50 -0400
Subject: [PATCH v5 1/2] mtd: mediatek: device tree bindings for MTK
In-Reply-To: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
Message-ID: <1465919451-1891-2-git-send-email-jorge.ramirez-ortiz@linaro.org>

Documentation support for Smart Device Gen1 type of NAND controllers

Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz at linaro.org>
---
 Documentation/devicetree/bindings/mtd/mtk-nand.txt | 160 +++++++++++++++++++++
 1 file changed, 160 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/mtk-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/mtk-nand.txt b/Documentation/devicetree/bindings/mtd/mtk-nand.txt
new file mode 100644
index 0000000..069c192
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/mtk-nand.txt
@@ -0,0 +1,160 @@
+MTK SoCs NAND FLASH controller (NFC) DT binding
+
+This file documents the device tree bindings for MTK SoCs NAND controllers.
+The functional split of the controller requires two drivers to operate:
+the nand controller interface driver and the ECC engine driver.
+
+The hardware description for both devices must be captured as device
+tree nodes.
+
+1) NFC NAND Controller Interface (NFI):
+=======================================
+
+The first part of NFC is NAND Controller Interface (NFI) HW.
+Required NFI properties:
+- compatible:			Should be "mediatek,mtxxxx-nfc".
+- reg:				Base physical address and size of NFI.
+- interrupts:			Interrupts of NFI.
+- clocks:			NFI required clocks.
+- clock-names:			NFI clocks internal name.
+- status:			Disabled default. Then set "okay" by platform.
+- ecc-engine:			Required ECC Engine node.
+- #address-cells:		NAND chip index, should be 1.
+- #size-cells:			Should be 0.
+
+Example:
+
+	nandc: nfi at 1100d000 {
+		compatible = "mediatek,mt2701-nfc";
+		reg = <0 0x1100d000 0 0x1000>;
+		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&pericfg CLK_PERI_NFI>,
+			 <&pericfg CLK_PERI_NFI_PAD>;
+		clock-names = "nfi_clk", "pad_clk";
+		status = "disabled";
+		ecc-engine = <&bch>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+        };
+
+Platform related properties, should be set in {platform_name}.dts:
+- children nodes:	NAND chips.
+
+Children nodes properties:
+- reg:			Chip Select Signal, default 0.
+			Set as reg = <0>, <1> when need 2 CS.
+Optional:
+- nand-on-flash-bbt:	Store BBT on NAND Flash.
+- nand-ecc-mode:	the NAND ecc mode (check driver for supported modes)
+- nand-ecc-step-size:	Number of data bytes covered by a single ECC step.
+			valid values: 512 and 1024.
+			1024 is recommended for large page NANDs.
+- nand-ecc-strength:	Number of bits to correct per ECC step.
+			The valid values that the controller supports are: 4, 6,
+			8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44,
+			48, 52, 56, 60.
+			The strength should be calculated as follows:
+			E = (S - F) * 8 / 14
+			S = O / (P / Q)
+				E :	nand-ecc-strength.
+				S :	spare size per sector.
+				F :	FDM size, should be in the range [1,8].
+					It is used to store free oob data.
+				O :	oob size.
+				P :	page size.
+				Q :	nand-ecc-step-size.
+			If the result does not match any one of the listed
+			choices above, please select the smaller valid value from
+			the list.
+			(otherwise the driver will do the adjustment at runtime)
+- pinctrl-names:	Default NAND pin GPIO setting name.
+- pinctrl-0:		GPIO setting node.
+
+Example:
+	&pio {
+		nand_pins_default: nanddefault {
+			pins_dat {
+				pinmux = <MT2701_PIN_111_MSDC0_DAT7__FUNC_NLD7>,
+					 <MT2701_PIN_112_MSDC0_DAT6__FUNC_NLD6>,
+					 <MT2701_PIN_114_MSDC0_DAT4__FUNC_NLD4>,
+					 <MT2701_PIN_118_MSDC0_DAT3__FUNC_NLD3>,
+					 <MT2701_PIN_121_MSDC0_DAT0__FUNC_NLD0>,
+					 <MT2701_PIN_120_MSDC0_DAT1__FUNC_NLD1>,
+					 <MT2701_PIN_113_MSDC0_DAT5__FUNC_NLD5>,
+					 <MT2701_PIN_115_MSDC0_RSTB__FUNC_NLD8>,
+					 <MT2701_PIN_119_MSDC0_DAT2__FUNC_NLD2>;
+				input-enable;
+				drive-strength = <MTK_DRIVE_8mA>;
+				bias-pull-up;
+			};
+
+			pins_we {
+				pinmux = <MT2701_PIN_117_MSDC0_CLK__FUNC_NWEB>;
+				drive-strength = <MTK_DRIVE_8mA>;
+				bias-pull-up = <MTK_PUPD_SET_R1R0_10>;
+			};
+
+			pins_ale {
+				pinmux = <MT2701_PIN_116_MSDC0_CMD__FUNC_NALE>;
+				drive-strength = <MTK_DRIVE_8mA>;
+				bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
+			};
+		};
+	};
+
+	&nandc {
+		status = "okay";
+		pinctrl-names = "default";
+		pinctrl-0 = <&nand_pins_default>;
+		nand at 0 {
+			reg = <0>;
+			nand-on-flash-bbt;
+			nand-ecc-mode = "hw";
+			nand-ecc-strength = <24>;
+			nand-ecc-step-size = <1024>;
+		};
+	};
+
+NAND chip optional subnodes:
+- Partitions, see Documentation/devicetree/bindings/mtd/partition.txt
+
+Example:
+	nand at 0 {
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			preloader at 0 {
+				label = "pl";
+				read-only;
+				reg = <0x00000000 0x00400000>;
+			};
+			android at 0x00400000 {
+				label = "android";
+				reg = <0x00400000 0x12c00000>;
+			};
+		};
+	};
+
+2) ECC Engine:
+==============
+
+Required BCH properties:
+- compatible:	Should be "mediatek,mtxxxx-ecc".
+- reg:		Base physical address and size of ECC.
+- interrupts:	Interrupts of ECC.
+- clocks:	ECC required clocks.
+- clock-names:	ECC clocks internal name.
+- status:	Disabled default. Then set "okay" by platform.
+
+Example:
+
+	bch: ecc at 1100e000 {
+		compatible = "mediatek,mt2701-ecc";
+		reg = <0 0x1100e000 0 0x1000>;
+		interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&pericfg CLK_PERI_NFI_ECC>;
+		clock-names = "nfiecc_clk";
+		status = "disabled";
+	};
-- 
2.7.4



From jorge.ramirez-ortiz at linaro.org  Tue Jun 14 08:50:51 2016
From: jorge.ramirez-ortiz at linaro.org (Jorge Ramirez-Ortiz)
Date: Tue, 14 Jun 2016 11:50:51 -0400
Subject: [PATCH v5 2/2] mtd: mediatek: driver for MTK Smart Device
In-Reply-To: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
Message-ID: <1465919451-1891-3-git-send-email-jorge.ramirez-ortiz@linaro.org>

Add support for mediatek's SDG1 NFC nand controller embedded in SoC
2701

Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz at linaro.org>
---
 drivers/mtd/nand/Kconfig    |    7 +
 drivers/mtd/nand/Makefile   |    1 +
 drivers/mtd/nand/mtk_ecc.c  |  528 +++++++++++++++
 drivers/mtd/nand/mtk_ecc.h  |   50 ++
 drivers/mtd/nand/mtk_nand.c | 1509 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 2095 insertions(+)
 create mode 100644 drivers/mtd/nand/mtk_ecc.c
 create mode 100644 drivers/mtd/nand/mtk_ecc.h
 create mode 100644 drivers/mtd/nand/mtk_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f05e0e9..3c26e89 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -563,4 +563,11 @@ config MTD_NAND_QCOM
 	  Enables support for NAND flash chips on SoCs containing the EBI2 NAND
 	  controller. This controller is found on IPQ806x SoC.
 
+config MTD_NAND_MTK
+	tristate "Support for NAND controller on MTK SoCs"
+	depends on HAS_DMA
+	help
+	  Enables support for NAND controller on MTK SoCs.
+	  This controller is found on mt27xx, mt81xx, mt65xx SoCs.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f553353..cafde6f 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
 obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
 obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
+obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_nand.o mtk_ecc.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o
diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c
new file mode 100644
index 0000000..2ab82ea
--- /dev/null
+++ b/drivers/mtd/nand/mtk_ecc.c
@@ -0,0 +1,528 @@
+/*
+ * MTK ECC controller driver.
+ * Copyright (C) 2016  MediaTek Inc.
+ * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
+ *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mutex.h>
+
+#include "mtk_ecc.h"
+
+#define ECC_IDLE_MASK		BIT(0)
+#define ECC_IRQ_EN		BIT(0)
+#define ECC_OP_ENABLE		(1)
+#define ECC_OP_DISABLE		(0)
+
+#define ECC_ENCCON		(0x00)
+#define ECC_ENCCNFG		(0x04)
+#define		ECC_CNFG_4BIT		(0)
+#define		ECC_CNFG_6BIT		(1)
+#define		ECC_CNFG_8BIT		(2)
+#define		ECC_CNFG_10BIT		(3)
+#define		ECC_CNFG_12BIT		(4)
+#define		ECC_CNFG_14BIT		(5)
+#define		ECC_CNFG_16BIT		(6)
+#define		ECC_CNFG_18BIT		(7)
+#define		ECC_CNFG_20BIT		(8)
+#define		ECC_CNFG_22BIT		(9)
+#define		ECC_CNFG_24BIT		(0xa)
+#define		ECC_CNFG_28BIT		(0xb)
+#define		ECC_CNFG_32BIT		(0xc)
+#define		ECC_CNFG_36BIT		(0xd)
+#define		ECC_CNFG_40BIT		(0xe)
+#define		ECC_CNFG_44BIT		(0xf)
+#define		ECC_CNFG_48BIT		(0x10)
+#define		ECC_CNFG_52BIT		(0x11)
+#define		ECC_CNFG_56BIT		(0x12)
+#define		ECC_CNFG_60BIT		(0x13)
+#define		ECC_MODE_SHIFT		(5)
+#define		ECC_MS_SHIFT		(16)
+#define ECC_ENCDIADDR		(0x08)
+#define ECC_ENCIDLE		(0x0C)
+#define ECC_ENCPAR(x)		(0x10 + (x) * sizeof(u32))
+#define ECC_ENCIRQ_EN		(0x80)
+#define ECC_ENCIRQ_STA		(0x84)
+#define ECC_DECCON		(0x100)
+#define ECC_DECCNFG		(0x104)
+#define		DEC_EMPTY_EN		BIT(31)
+#define		DEC_CNFG_CORRECT	(0x3 << 12)
+#define ECC_DECIDLE		(0x10C)
+#define ECC_DECENUM0		(0x114)
+#define		ERR_MASK		(0x3f)
+#define ECC_DECDONE		(0x124)
+#define ECC_DECIRQ_EN		(0x200)
+#define ECC_DECIRQ_STA		(0x204)
+
+#define ECC_TIMEOUT		(500000)
+
+#define ECC_IDLE_REG(op)	((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
+#define ECC_CTL_REG(op)		((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
+#define ECC_IRQ_REG(op)		((op) == ECC_ENCODE ? \
+					ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
+
+struct mtk_ecc {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *clk;
+
+	struct completion done;
+	struct mutex lock;
+	u32 sectors;
+};
+
+static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+					enum mtk_ecc_operation op)
+{
+	struct device *dev = ecc->dev;
+	u32 val;
+	int ret;
+
+	ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val,
+					val & ECC_IDLE_MASK,
+					10, ECC_TIMEOUT);
+	if (ret)
+		dev_warn(dev, "%s NOT idle\n",
+			op == ECC_ENCODE ? "encoder" : "decoder");
+}
+
+static irqreturn_t mtk_ecc_irq(int irq, void *id)
+{
+	struct mtk_ecc *ecc = id;
+	enum mtk_ecc_operation op;
+	u32 dec, enc;
+
+	dec = readw(ecc->regs + ECC_DECIRQ_STA) & ECC_IRQ_EN;
+	if (dec) {
+		op = ECC_DECODE;
+		dec = readw(ecc->regs + ECC_DECDONE);
+		if (dec & ecc->sectors) {
+			ecc->sectors = 0;
+			complete(&ecc->done);
+		} else
+			return IRQ_HANDLED;
+	} else {
+		enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ECC_IRQ_EN;
+		if (enc) {
+			op = ECC_ENCODE;
+			complete(&ecc->done);
+		} else
+			return IRQ_NONE;
+	}
+
+	writel(0, ecc->regs + ECC_IRQ_REG(op));
+
+	return IRQ_HANDLED;
+}
+
+static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+{
+	u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
+	u32 reg;
+
+	switch (config->strength) {
+	case 4:
+		ecc_bit = ECC_CNFG_4BIT;
+		break;
+	case 6:
+		ecc_bit = ECC_CNFG_6BIT;
+		break;
+	case 8:
+		ecc_bit = ECC_CNFG_8BIT;
+		break;
+	case 10:
+		ecc_bit = ECC_CNFG_10BIT;
+		break;
+	case 12:
+		ecc_bit = ECC_CNFG_12BIT;
+		break;
+	case 14:
+		ecc_bit = ECC_CNFG_14BIT;
+		break;
+	case 16:
+		ecc_bit = ECC_CNFG_16BIT;
+		break;
+	case 18:
+		ecc_bit = ECC_CNFG_18BIT;
+		break;
+	case 20:
+		ecc_bit = ECC_CNFG_20BIT;
+		break;
+	case 22:
+		ecc_bit = ECC_CNFG_22BIT;
+		break;
+	case 24:
+		ecc_bit = ECC_CNFG_24BIT;
+		break;
+	case 28:
+		ecc_bit = ECC_CNFG_28BIT;
+		break;
+	case 32:
+		ecc_bit = ECC_CNFG_32BIT;
+		break;
+	case 36:
+		ecc_bit = ECC_CNFG_36BIT;
+		break;
+	case 40:
+		ecc_bit = ECC_CNFG_40BIT;
+		break;
+	case 44:
+		ecc_bit = ECC_CNFG_44BIT;
+		break;
+	case 48:
+		ecc_bit = ECC_CNFG_48BIT;
+		break;
+	case 52:
+		ecc_bit = ECC_CNFG_52BIT;
+		break;
+	case 56:
+		ecc_bit = ECC_CNFG_56BIT;
+		break;
+	case 60:
+		ecc_bit = ECC_CNFG_60BIT;
+		break;
+	default:
+		dev_err(ecc->dev, "invalid strength %d, default to 4 bits\n",
+			config->strength);
+	}
+
+	if (config->op == ECC_ENCODE) {
+		/* configure ECC encoder (in bits) */
+		enc_sz = config->len << 3;
+
+		reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
+		reg |= (enc_sz << ECC_MS_SHIFT);
+		writel(reg, ecc->regs + ECC_ENCCNFG);
+
+		if (config->mode != ECC_NFI_MODE)
+			writel(lower_32_bits(config->addr),
+				ecc->regs + ECC_ENCDIADDR);
+
+	} else {
+		/* configure ECC decoder (in bits) */
+		dec_sz = (config->len << 3) +
+					config->strength * ECC_PARITY_BITS;
+
+		reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
+		reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
+		reg |= DEC_EMPTY_EN;
+		writel(reg, ecc->regs + ECC_DECCNFG);
+
+		if (config->sectors)
+			ecc->sectors = 1 << (config->sectors - 1);
+	}
+}
+
+void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
+			int sectors)
+{
+	u32 offset, i, err;
+	u32 bitflips = 0;
+
+	stats->corrected = 0;
+	stats->failed = 0;
+
+	for (i = 0; i < sectors; i++) {
+		offset = (i >> 2) << 2;
+		err = readl(ecc->regs + ECC_DECENUM0 + offset);
+		err = err >> ((i % 4) * 8);
+		err &= ERR_MASK;
+		if (err == ERR_MASK) {
+			/* uncorrectable errors */
+			stats->failed++;
+			continue;
+		}
+
+		stats->corrected += err;
+		bitflips = max_t(u32, bitflips, err);
+	}
+
+	stats->bitflips = bitflips;
+}
+EXPORT_SYMBOL(mtk_ecc_get_stats);
+
+void mtk_ecc_release(struct mtk_ecc *ecc)
+{
+	clk_disable_unprepare(ecc->clk);
+	put_device(ecc->dev);
+}
+EXPORT_SYMBOL(mtk_ecc_release);
+
+static void mtk_ecc_hw_init(struct mtk_ecc *ecc)
+{
+	mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+	writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
+
+	mtk_ecc_wait_idle(ecc, ECC_DECODE);
+	writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
+}
+
+static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
+{
+	struct platform_device *pdev;
+	struct mtk_ecc *ecc;
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev || !platform_get_drvdata(pdev))
+		return ERR_PTR(-EPROBE_DEFER);
+
+	get_device(&pdev->dev);
+	ecc = platform_get_drvdata(pdev);
+	clk_prepare_enable(ecc->clk);
+	mtk_ecc_hw_init(ecc);
+
+	return ecc;
+}
+
+struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
+{
+	struct mtk_ecc *ecc = NULL;
+	struct device_node *np;
+
+	np = of_parse_phandle(of_node, "ecc-engine", 0);
+	if (np) {
+		ecc = mtk_ecc_get(np);
+		of_node_put(np);
+	}
+
+	return ecc;
+}
+EXPORT_SYMBOL(of_mtk_ecc_get);
+
+int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
+{
+	enum mtk_ecc_operation op = config->op;
+	int ret;
+
+	ret = mutex_lock_interruptible(&ecc->lock);
+	if (ret) {
+		dev_err(ecc->dev, "interrupted when attempting to lock\n");
+		return ret;
+	}
+
+	mtk_ecc_wait_idle(ecc, op);
+	mtk_ecc_config(ecc, config);
+	writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
+
+	init_completion(&ecc->done);
+	writew(ECC_IRQ_EN, ecc->regs + ECC_IRQ_REG(op));
+
+	return 0;
+}
+EXPORT_SYMBOL(mtk_ecc_enable);
+
+void mtk_ecc_disable(struct mtk_ecc *ecc)
+{
+	enum mtk_ecc_operation op = ECC_ENCODE;
+
+	/* find out the running operation */
+	if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
+		op = ECC_DECODE;
+
+	/* disable it */
+	mtk_ecc_wait_idle(ecc, op);
+	writew(0, ecc->regs + ECC_IRQ_REG(op));
+	writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
+
+	mutex_unlock(&ecc->lock);
+}
+EXPORT_SYMBOL(mtk_ecc_disable);
+
+int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
+	if (!ret) {
+		dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
+				(op == ECC_ENCODE) ? "encoder" : "decoder");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(mtk_ecc_wait_done);
+
+int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
+			u8 *data, u32 bytes)
+{
+	dma_addr_t addr;
+	u32 *p, len, i;
+	int ret = 0;
+
+	addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
+	ret = dma_mapping_error(ecc->dev, addr);
+	if (ret) {
+		dev_err(ecc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	config->op = ECC_ENCODE;
+	config->addr = addr;
+	ret = mtk_ecc_enable(ecc, config);
+	if (ret) {
+		dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
+		return ret;
+	}
+
+	ret = mtk_ecc_wait_done(ecc, ECC_ENCODE);
+	if (ret)
+		goto timeout;
+
+	mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+
+	/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
+	len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
+	p = (u32 *) (data + bytes);
+
+	/* write the parity bytes generated by the ECC back to the OOB region */
+	for (i = 0; i < len; i++)
+		p[i] = readl(ecc->regs + ECC_ENCPAR(i));
+timeout:
+
+	dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
+	mtk_ecc_disable(ecc);
+
+	return ret;
+}
+EXPORT_SYMBOL(mtk_ecc_encode);
+
+void mtk_ecc_adjust_strength(u32 *p)
+{
+	u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
+			40, 44, 48, 52, 56, 60};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ecc); i++) {
+		if (*p <= ecc[i]) {
+			if (!i)
+				*p = ecc[i];
+			else if (*p != ecc[i])
+				*p = ecc[i - 1];
+			return;
+		}
+	}
+
+	*p = ecc[ARRAY_SIZE(ecc) - 1];
+}
+EXPORT_SYMBOL(mtk_ecc_adjust_strength);
+
+static int mtk_ecc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mtk_ecc *ecc;
+	struct resource *res;
+	int irq, ret;
+
+	ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
+	if (!ecc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	ecc->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ecc->regs)) {
+		dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs));
+		return PTR_ERR(ecc->regs);
+	}
+
+	ecc->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(ecc->clk)) {
+		dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
+		return PTR_ERR(ecc->clk);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "failed to get irq\n");
+		return -EINVAL;
+	}
+
+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "failed to set DMA mask\n");
+		return ret;
+	}
+
+	ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
+	if (ret) {
+		dev_err(dev, "failed to request irq\n");
+		return -EINVAL;
+	}
+
+	ecc->dev = dev;
+	mutex_init(&ecc->lock);
+	platform_set_drvdata(pdev, ecc);
+	dev_info(dev, "probed\n");
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_ecc_suspend(struct device *dev)
+{
+	struct mtk_ecc *ecc = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(ecc->clk);
+
+	return 0;
+}
+
+static int mtk_ecc_resume(struct device *dev)
+{
+	struct mtk_ecc *ecc = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(ecc->clk);
+	if (ret) {
+		dev_err(dev, "failed to enable clk\n");
+		return ret;
+	}
+
+	mtk_ecc_hw_init(ecc);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
+#endif
+
+static const struct of_device_id mtk_ecc_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-ecc" },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
+
+static struct platform_driver mtk_ecc_driver = {
+	.probe  = mtk_ecc_probe,
+	.driver = {
+		.name  = "mtk-ecc",
+		.of_match_table = of_match_ptr(mtk_ecc_dt_match),
+#ifdef CONFIG_PM_SLEEP
+		.pm = &mtk_ecc_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(mtk_ecc_driver);
+
+MODULE_AUTHOR("Xiaolei Li <xiaolei.li at mediatek.com>");
+MODULE_DESCRIPTION("MTK Nand ECC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/mtk_ecc.h
new file mode 100644
index 0000000..cbeba5c
--- /dev/null
+++ b/drivers/mtd/nand/mtk_ecc.h
@@ -0,0 +1,50 @@
+/*
+ * MTK SDG1 ECC controller
+ *
+ * Copyright (c) 2016 Mediatek
+ * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
+ *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
+#define __DRIVERS_MTD_NAND_MTK_ECC_H__
+
+#include <linux/types.h>
+
+#define ECC_PARITY_BITS		(14)
+
+enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
+enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE};
+
+struct device_node;
+struct mtk_ecc;
+
+struct mtk_ecc_stats {
+	u32 corrected;
+	u32 bitflips;
+	u32 failed;
+};
+
+struct mtk_ecc_config {
+	enum mtk_ecc_operation op;
+	enum mtk_ecc_mode mode;
+	dma_addr_t addr;
+	u32 strength;
+	u32 sectors;
+	u32 len;
+};
+
+int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32);
+void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
+int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
+int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
+void mtk_ecc_disable(struct mtk_ecc *);
+void mtk_ecc_adjust_strength(u32 *);
+
+struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
+void mtk_ecc_release(struct mtk_ecc *);
+
+#endif
diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
new file mode 100644
index 0000000..ad8863d
--- /dev/null
+++ b/drivers/mtd/nand/mtk_nand.c
@@ -0,0 +1,1509 @@
+/*
+ * MTK NAND Flash controller driver.
+ * Copyright (C) 2016 MediaTek Inc.
+ * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
+ *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include "mtk_ecc.h"
+
+/* NAND controller register definition */
+#define NFI_CNFG		(0x00)
+#define		CNFG_AHB		BIT(0)
+#define		CNFG_READ_EN		BIT(1)
+#define		CNFG_DMA_BURST_EN	BIT(2)
+#define		CNFG_BYTE_RW		BIT(6)
+#define		CNFG_HW_ECC_EN		BIT(8)
+#define		CNFG_AUTO_FMT_EN	BIT(9)
+#define		CNFG_OP_CUST		(6 << 12)
+#define NFI_PAGEFMT		(0x04)
+#define		PAGEFMT_FDM_ECC_SHIFT	(12)
+#define		PAGEFMT_FDM_SHIFT	(8)
+#define		PAGEFMT_SPARE_16	(0)
+#define		PAGEFMT_SPARE_26	(1)
+#define		PAGEFMT_SPARE_27	(2)
+#define		PAGEFMT_SPARE_28	(3)
+#define		PAGEFMT_SPARE_32	(4)
+#define		PAGEFMT_SPARE_36	(5)
+#define		PAGEFMT_SPARE_40	(6)
+#define		PAGEFMT_SPARE_44	(7)
+#define		PAGEFMT_SPARE_48	(8)
+#define		PAGEFMT_SPARE_49	(9)
+#define		PAGEFMT_SPARE_50	(0xa)
+#define		PAGEFMT_SPARE_51	(0xb)
+#define		PAGEFMT_SPARE_52	(0xc)
+#define		PAGEFMT_SPARE_62	(0xd)
+#define		PAGEFMT_SPARE_63	(0xe)
+#define		PAGEFMT_SPARE_64	(0xf)
+#define		PAGEFMT_SPARE_SHIFT	(4)
+#define		PAGEFMT_SEC_SEL_512	BIT(2)
+#define		PAGEFMT_512_2K		(0)
+#define		PAGEFMT_2K_4K		(1)
+#define		PAGEFMT_4K_8K		(2)
+#define		PAGEFMT_8K_16K		(3)
+/* NFI control */
+#define NFI_CON			(0x08)
+#define		CON_FIFO_FLUSH		BIT(0)
+#define		CON_NFI_RST		BIT(1)
+#define		CON_BRD			BIT(8)  /* burst  read */
+#define		CON_BWR			BIT(9)	/* burst  write */
+#define		CON_SEC_SHIFT		(12)
+/* Timming control register */
+#define NFI_ACCCON		(0x0C)
+#define NFI_INTR_EN		(0x10)
+#define		INTR_AHB_DONE_EN	BIT(6)
+#define NFI_INTR_STA		(0x14)
+#define NFI_CMD			(0x20)
+#define NFI_ADDRNOB		(0x30)
+#define NFI_COLADDR		(0x34)
+#define NFI_ROWADDR		(0x38)
+#define NFI_STRDATA		(0x40)
+#define		STAR_EN			(1)
+#define		STAR_DE			(0)
+#define NFI_CNRNB		(0x44)
+#define NFI_DATAW		(0x50)
+#define NFI_DATAR		(0x54)
+#define NFI_PIO_DIRDY		(0x58)
+#define		PIO_DI_RDY		(0x01)
+#define NFI_STA			(0x60)
+#define		STA_CMD			BIT(0)
+#define		STA_ADDR		BIT(1)
+#define		STA_BUSY		BIT(8)
+#define		STA_EMP_PAGE		BIT(12)
+#define		NFI_FSM_CUSTDATA	(0xe << 16)
+#define		NFI_FSM_MASK		(0xf << 16)
+#define NFI_ADDRCNTR		(0x70)
+#define		CNTR_MASK		GENMASK(16, 12)
+#define NFI_STRADDR		(0x80)
+#define NFI_BYTELEN		(0x84)
+#define NFI_CSEL		(0x90)
+#define NFI_FDML(x)		(0xA0 + (x) * sizeof(u32) * 2)
+#define NFI_FDMM(x)		(0xA4 + (x) * sizeof(u32) * 2)
+#define NFI_FDM_MAX_SIZE	(8)
+#define NFI_FDM_MIN_SIZE	(1)
+#define NFI_MASTER_STA		(0x224)
+#define		MASTER_STA_MASK		(0x0FFF)
+#define NFI_EMPTY_THRESH	(0x23C)
+
+#define MTK_NAME		"mtk-nand"
+#define KB(x)			((x) * 1024UL)
+#define MB(x)			(KB(x) * 1024UL)
+
+#define MTK_TIMEOUT		(500000)
+#define MTK_RESET_TIMEOUT	(1000000)
+#define MTK_MAX_SECTOR		(16)
+#define MTK_NAND_MAX_NSELS	(2)
+
+struct mtk_nfc_bad_mark_ctl {
+	void (*bm_swap)(struct mtd_info *, uint8_t *buf, int raw);
+	u32 sec;
+	u32 pos;
+};
+
+/*
+ * FDM: region used to store free OOB data
+ */
+struct mtk_nfc_fdm {
+	u32 reg_size;
+	u32 ecc_size;
+};
+
+struct mtk_nfc_nand_chip {
+	struct list_head node;
+	struct nand_chip nand;
+
+	struct mtk_nfc_bad_mark_ctl bad_mark;
+	struct mtk_nfc_fdm fdm;
+	u32 spare_per_sector;
+
+	int nsels;
+	u8 sels[0];
+	/* nothing after this field */
+};
+
+struct mtk_nfc_clk {
+	struct clk *nfi_clk;
+	struct clk *pad_clk;
+};
+
+struct mtk_nfc {
+	struct nand_hw_control controller;
+	struct mtk_ecc_config ecc_cfg;
+	struct mtk_nfc_clk clk;
+	struct mtk_ecc *ecc;
+
+	struct device *dev;
+	void __iomem *regs;
+
+	struct completion done;
+	struct list_head chips;
+
+	u8 *buffer;
+};
+
+static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand)
+{
+	return container_of(nand, struct mtk_nfc_nand_chip, nand);
+}
+
+static inline uint8_t *data_ptr(struct nand_chip *chip, const uint8_t *p, int i)
+{
+	return (uint8_t *) p + i * chip->ecc.size;
+}
+
+static inline uint8_t *oob_ptr(struct nand_chip *chip, int i)
+{
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	uint8_t *poi;
+
+	/* map the sector's FDM data to free oob:
+	 * the beginning of the oob area stores the FDM data of bad mark sectors
+	 */
+
+	if (i < mtk_nand->bad_mark.sec)
+		poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size;
+	else if (i == mtk_nand->bad_mark.sec)
+		poi = chip->oob_poi;
+	else
+		poi = chip->oob_poi + i * mtk_nand->fdm.reg_size;
+
+	return poi;
+}
+
+static inline int mtk_data_len(struct nand_chip *chip)
+{
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+
+	return chip->ecc.size + mtk_nand->spare_per_sector;
+}
+
+static inline uint8_t *mtk_data_ptr(struct nand_chip *chip,  int i)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+	return nfc->buffer + i * mtk_data_len(chip);
+}
+
+static inline uint8_t *mtk_oob_ptr(struct nand_chip *chip, int i)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+	return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size;
+}
+
+static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg)
+{
+	writel(val, nfc->regs + reg);
+}
+
+static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg)
+{
+	writew(val, nfc->regs + reg);
+}
+
+static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg)
+{
+	writeb(val, nfc->regs + reg);
+}
+
+static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg)
+{
+	return readl_relaxed(nfc->regs + reg);
+}
+
+static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg)
+{
+	return readw_relaxed(nfc->regs + reg);
+}
+
+static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg)
+{
+	return readb_relaxed(nfc->regs + reg);
+}
+
+static void mtk_nfc_hw_reset(struct mtk_nfc *nfc)
+{
+	struct device *dev = nfc->dev;
+	u32 val;
+	int ret;
+
+	/* reset all registers and force the NFI master to terminate */
+	nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
+
+	/* wait for the master to finish the last transaction */
+	ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val,
+			!(val & MASTER_STA_MASK), 50, MTK_RESET_TIMEOUT);
+	if (ret)
+		dev_warn(dev, "master active in reset [0x%x] = 0x%x\n",
+			NFI_MASTER_STA, val);
+
+	/* ensure any status register affected by the NFI master is reset */
+	nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
+	nfi_writew(nfc, STAR_DE, NFI_STRDATA);
+}
+
+static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command)
+{
+	struct device *dev = nfc->dev;
+	u32 val;
+	int ret;
+
+	nfi_writel(nfc, command, NFI_CMD);
+
+	ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
+					!(val & STA_CMD), 10,  MTK_TIMEOUT);
+	if (ret) {
+		dev_warn(dev, "nfi core timed out entering command mode\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr)
+{
+	struct device *dev = nfc->dev;
+	u32 val;
+	int ret;
+
+	nfi_writel(nfc, addr, NFI_COLADDR);
+	nfi_writel(nfc, 0, NFI_ROWADDR);
+	nfi_writew(nfc, 1, NFI_ADDRNOB);
+
+	ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
+					!(val & STA_ADDR), 10, MTK_TIMEOUT);
+	if (ret) {
+		dev_warn(dev, "nfi core timed out entering address mode\n");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	u32 fmt, spare;
+
+	if (!mtd->writesize)
+		return 0;
+
+	spare = mtk_nand->spare_per_sector;
+
+	switch (mtd->writesize) {
+	case 512:
+		fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
+		break;
+	case KB(2):
+		if (chip->ecc.size == 512)
+			fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
+		else
+			fmt = PAGEFMT_512_2K;
+		break;
+	case KB(4):
+		if (chip->ecc.size == 512)
+			fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
+		else
+			fmt = PAGEFMT_2K_4K;
+		break;
+	case KB(8):
+		if (chip->ecc.size == 512)
+			fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
+		else
+			fmt = PAGEFMT_4K_8K;
+		break;
+	case KB(16):
+		fmt = PAGEFMT_8K_16K;
+		break;
+	default:
+		dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize);
+		return -EINVAL;
+	}
+
+	/* the hardware will double the value for this eccsize, so we need to
+	 * halve it
+	 */
+	if (chip->ecc.size == 1024)
+		spare >>= 1;
+
+	switch (spare) {
+	case 16:
+		fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 26:
+		fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 27:
+		fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 28:
+		fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 32:
+		fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 36:
+		fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 40:
+		fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 44:
+		fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 48:
+		fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 49:
+		fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 50:
+		fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 51:
+		fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 52:
+		fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 62:
+		fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 63:
+		fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT);
+		break;
+	case 64:
+		fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT);
+		break;
+	default:
+		dev_err(nfc->dev, "invalid spare per sector %d\n", spare);
+		return -EINVAL;
+	}
+
+	fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT;
+	fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT;
+	nfi_writew(nfc, fmt, NFI_PAGEFMT);
+
+	nfc->ecc_cfg.strength = chip->ecc.strength;
+	nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size;
+
+	return 0;
+}
+
+static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct mtk_nfc *nfc = nand_get_controller_data(nand);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
+
+	if (chip < 0)
+		return;
+
+	mtk_nfc_hw_runtime_config(mtd);
+
+	nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL);
+}
+
+static int mtk_nfc_dev_ready(struct mtd_info *mtd)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
+		return 0;
+
+	return 1;
+}
+
+static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+
+	if (ctrl & NAND_ALE)
+		mtk_nfc_send_address(nfc, dat);
+	else if (ctrl & NAND_CLE) {
+		mtk_nfc_hw_reset(nfc);
+
+		nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
+		mtk_nfc_send_command(nfc, dat);
+	}
+}
+
+static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
+{
+	int rc;
+	u8 val;
+
+	rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val,
+					val & PIO_DI_RDY, 10, MTK_TIMEOUT);
+	if (rc < 0)
+		dev_err(nfc->dev, "data not ready\n");
+}
+
+static inline uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	u32 reg;
+
+	/* after each byte read, the NFI_STA reg is reset by the hardware */
+	reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
+	if (reg != NFI_FSM_CUSTDATA) {
+		reg = nfi_readw(nfc, NFI_CNFG);
+		reg |= CNFG_BYTE_RW | CNFG_READ_EN;
+		nfi_writew(nfc, reg, NFI_CNFG);
+
+		/* set to max sector to allow the HW to continue reading over
+		 * unaligned accesses
+		 */
+		reg = (MTK_MAX_SECTOR << CON_SEC_SHIFT) | CON_BRD;
+		nfi_writel(nfc, reg, NFI_CON);
+
+		/* trigger to fetch data */
+		nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+	}
+
+	mtk_nfc_wait_ioready(nfc);
+
+	return nfi_readb(nfc, NFI_DATAR);
+}
+
+static void mtk_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		buf[i] = mtk_nfc_read_byte(mtd);
+}
+
+static void mtk_nfc_write_byte(struct mtd_info *mtd, uint8_t byte)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
+	u32 reg;
+
+	reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
+
+	if (reg != NFI_FSM_CUSTDATA) {
+		reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
+		nfi_writew(nfc, reg, NFI_CNFG);
+
+		reg = MTK_MAX_SECTOR << CON_SEC_SHIFT | CON_BWR;
+		nfi_writel(nfc, reg, NFI_CON);
+
+		nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+	}
+
+	mtk_nfc_wait_ioready(nfc);
+	nfi_writeb(nfc, byte, NFI_DATAW);
+}
+
+static void mtk_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		mtk_nfc_write_byte(mtd, buf[i]);
+}
+
+static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	int size = chip->ecc.size + mtk_nand->fdm.reg_size;
+
+	nfc->ecc_cfg.mode = ECC_DMA_MODE;
+	nfc->ecc_cfg.op = ECC_ENCODE;
+	return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size);
+}
+
+static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, uint8_t *b, int c)
+{
+	/* nop */
+}
+
+static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, uint8_t *buf, int raw)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip);
+	u32 bad_pos = nand->bad_mark.pos;
+
+	if (raw)
+		bad_pos += nand->bad_mark.sec * mtk_data_len(chip);
+	else
+		bad_pos += nand->bad_mark.sec * chip->ecc.size;
+
+	swap(chip->oob_poi[0], buf[bad_pos]);
+}
+
+static int mtk_nfc_format_subpage(struct mtd_info *mtd, uint32_t offset,
+			uint32_t len, const uint8_t *buf)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	u32 start, end;
+	int i, ret;
+
+	start = offset / chip->ecc.size;
+	end = DIV_ROUND_UP(offset + len, chip->ecc.size);
+
+	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
+	for (i = 0; i < chip->ecc.steps; i++) {
+
+		memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
+			chip->ecc.size);
+
+		if (start > i || i >= end)
+			continue;
+
+		if (i == mtk_nand->bad_mark.sec)
+			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
+
+		memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
+
+		/* program the CRC back to the OOB */
+		ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i));
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void mtk_nfc_format_page(struct mtd_info *mtd, const uint8_t *buf)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	u32 i;
+
+	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
+	for (i = 0; i < chip->ecc.steps; i++) {
+		if (buf)
+			memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
+				chip->ecc.size);
+
+		if (i == mtk_nand->bad_mark.sec)
+			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
+
+		memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
+	}
+}
+
+static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start,
+					u32 sectors)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	u32 vall, valm;
+	u8 *oobptr;
+	int i, j;
+
+	for (i = 0; i < sectors; i++) {
+		oobptr = oob_ptr(chip, start + i);
+		vall = nfi_readl(nfc, NFI_FDML(i));
+		valm = nfi_readl(nfc, NFI_FDMM(i));
+
+		for (j = 0; j < fdm->reg_size; j++)
+			oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
+	}
+}
+
+static inline void mtk_nfc_write_fdm(struct nand_chip *chip)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	u32 vall, valm;
+	u8 *oobptr;
+	int i, j;
+
+	for (i = 0; i < chip->ecc.steps; i++) {
+		oobptr = oob_ptr(chip, i);
+		vall = 0;
+		valm = 0;
+		for (j = 0; j < 8; j++) {
+			if (j < 4)
+				vall |= (j < fdm->reg_size ? oobptr[j] : 0xff)
+						<< (j * 8);
+			else
+				valm |= (j < fdm->reg_size ? oobptr[j] : 0xff)
+						<< ((j - 4) * 8);
+		}
+		nfi_writel(nfc, vall, NFI_FDML(i));
+		nfi_writel(nfc, valm, NFI_FDMM(i));
+	}
+}
+
+static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, int page, int len)
+{
+
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct device *dev = nfc->dev;
+	dma_addr_t addr;
+	u32 reg;
+	int ret;
+
+	addr = dma_map_single(dev, (void *) buf, len, DMA_TO_DEVICE);
+	ret = dma_mapping_error(nfc->dev, addr);
+	if (ret) {
+		dev_err(nfc->dev, "dma mapping error\n");
+		return -EINVAL;
+	}
+
+	reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN;
+	nfi_writew(nfc, reg, NFI_CNFG);
+
+	nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON);
+	nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
+	nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
+
+	init_completion(&nfc->done);
+
+	reg = nfi_readl(nfc, NFI_CON) | CON_BWR;
+	nfi_writel(nfc, reg, NFI_CON);
+	nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+
+	ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
+	if (!ret) {
+		dev_err(dev, "program ahb done timeout\n");
+		nfi_writew(nfc, 0, NFI_INTR_EN);
+		ret = -ETIMEDOUT;
+		goto timeout;
+	}
+
+	ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg,
+			(reg & CNTR_MASK) >= chip->ecc.steps, 10, MTK_TIMEOUT);
+	if (ret)
+		dev_err(dev, "hwecc write timeout\n");
+
+timeout:
+
+	dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE);
+	nfi_writel(nfc, 0, NFI_CON);
+
+	return ret;
+}
+
+static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+			const uint8_t *buf, int page, int raw)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	size_t len;
+	const u8 *bufpoi;
+	u32 reg;
+	int ret;
+
+	if (!raw) {
+		/* OOB => FDM: from register,  ECC: from HW */
+		reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN;
+		nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG);
+
+		nfc->ecc_cfg.op = ECC_ENCODE;
+		nfc->ecc_cfg.mode = ECC_NFI_MODE;
+		ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
+		if (ret) {
+			/* clear NFI config */
+			reg = nfi_readw(nfc, NFI_CNFG);
+			reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+			nfi_writew(nfc, reg, NFI_CNFG);
+
+			return ret;
+		}
+
+		memcpy(nfc->buffer, buf, mtd->writesize);
+		mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw);
+		bufpoi = nfc->buffer;
+
+		/* write OOB into the FDM registers (OOB area in MTK NAND) */
+		mtk_nfc_write_fdm(chip);
+	} else
+		bufpoi = buf;
+
+	len = mtd->writesize + (raw ? mtd->oobsize : 0);
+	ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len);
+
+	if (!raw)
+		mtk_ecc_disable(nfc->ecc);
+
+	return ret;
+}
+
+static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, const uint8_t *buf, int oob_on, int page)
+{
+	return mtk_nfc_write_page(mtd, chip, buf, page, 0);
+}
+
+static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, int oob_on, int pg)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+
+	mtk_nfc_format_page(mtd, buf);
+	return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1);
+}
+
+static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
+		struct nand_chip *chip, uint32_t offset, uint32_t data_len,
+		const uint8_t *buf, int oob_on, int page)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	int ret;
+
+	ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf);
+	if (ret < 0)
+		return ret;
+
+	/* use the data in the private buffer (now with FDM and CRC) */
+	return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1);
+}
+
+static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+					int page)
+{
+	int ret;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	ret = mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page);
+	if (ret < 0)
+		return -EIO;
+
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	ret = chip->waitfunc(mtd, chip);
+
+	return ret & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_ecc_stats stats;
+	int rc, i;
+
+	rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
+	if (rc) {
+		memset(buf, 0xff, sectors * chip->ecc.size);
+		for (i = 0; i < sectors; i++)
+			memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size);
+		return 0;
+	}
+
+	mtk_ecc_get_stats(nfc->ecc, &stats, sectors);
+	mtd->ecc_stats.corrected += stats.corrected;
+	mtd->ecc_stats.failed += stats.failed;
+
+	return stats.bitflips;
+}
+
+static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+		uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+		int page, int raw)
+{
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	u32 spare = mtk_nand->spare_per_sector;
+	u32 column, sectors, start, end, reg;
+	dma_addr_t addr;
+	int bitflips;
+	size_t len;
+	u8 *buf;
+	int rc;
+
+	start = data_offs / chip->ecc.size;
+	end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
+
+	sectors = end - start;
+	column = start * (chip->ecc.size + spare);
+
+	len = sectors * chip->ecc.size + (raw ? sectors * spare : 0);
+	buf = bufpoi + start * chip->ecc.size;
+
+	if (column != 0)
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
+
+	addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
+	rc = dma_mapping_error(nfc->dev, addr);
+	if (rc) {
+		dev_err(nfc->dev, "dma mapping error\n");
+
+		return -EINVAL;
+	}
+
+	reg = nfi_readw(nfc, NFI_CNFG);
+	reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB;
+	if (!raw) {
+		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
+		nfi_writew(nfc, reg, NFI_CNFG);
+
+		nfc->ecc_cfg.mode = ECC_NFI_MODE;
+		nfc->ecc_cfg.sectors = sectors;
+		nfc->ecc_cfg.op = ECC_DECODE;
+		rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
+		if (rc) {
+			dev_err(nfc->dev, "ecc enable\n");
+			/* clear NFI_CNFG */
+			reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN |
+				CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
+			nfi_writew(nfc, reg, NFI_CNFG);
+			dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
+
+			return rc;
+		}
+	} else
+		nfi_writew(nfc, reg, NFI_CNFG);
+
+	nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON);
+	nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
+	nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
+
+	init_completion(&nfc->done);
+	reg = nfi_readl(nfc, NFI_CON) | CON_BRD;
+	nfi_writel(nfc, reg, NFI_CON);
+	nfi_writew(nfc, STAR_EN, NFI_STRDATA);
+
+	rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
+	if (!rc)
+		dev_warn(nfc->dev, "read ahb/dma done timeout\n");
+
+	rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg,
+				(reg & CNTR_MASK) >= sectors, 10, MTK_TIMEOUT);
+	if (rc < 0) {
+		dev_err(nfc->dev, "subpage done timeout\n");
+		bitflips = -EIO;
+	} else {
+		bitflips = 0;
+		if (!raw) {
+			rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE);
+			bitflips = rc < 0 ? -ETIMEDOUT :
+				mtk_nfc_update_ecc_stats(mtd, buf, sectors);
+			mtk_nfc_read_fdm(chip, start, sectors);
+		}
+	}
+
+	dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
+
+	if (raw)
+		goto done;
+
+	mtk_ecc_disable(nfc->ecc);
+
+	if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec)
+		mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw);
+done:
+	nfi_writel(nfc, 0, NFI_CON);
+
+	return bitflips;
+}
+
+static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, uint32_t off, uint32_t len, uint8_t *p, int pg)
+{
+	return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0);
+}
+
+static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd,
+	struct nand_chip *chip, uint8_t *p, int oob_on, int pg)
+{
+	return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0);
+}
+
+static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int oob_on, int page)
+{
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc *nfc = nand_get_controller_data(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	int i, ret;
+
+	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
+	ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer,
+					page, 1);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < chip->ecc.steps; i++) {
+		memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
+		if (i == mtk_nand->bad_mark.sec)
+			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
+
+		if (buf)
+			memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
+				chip->ecc.size);
+	}
+
+	return ret;
+}
+
+static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+				int page)
+{
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+	return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc)
+{
+	/* ACCON: access timing control register
+	 * -------------------------------------
+	 * 31:28: minimum required time for CS post pulling down after accessing
+	 *	the device
+	 * 27:22: minimum required time for CS pre pulling down before accessing
+	 *	the device
+	 * 21:16: minimum required time from NCEB low to NREB low
+	 * 15:12: minimum required time from NWEB high to NREB low.
+	 * 11:08: write enable hold time
+	 * 07:04: write wait states
+	 * 03:00: read wait states
+	 */
+	nfi_writel(nfc, 0x10804211, NFI_ACCCON);
+
+	/* CNRNB: nand ready/busy register
+	 * -------------------------------
+	 * 7:4: timeout register for polling the NAND busy/ready signal
+	 * 0  : poll the status of the busy/ready signal after [7:4]*16 cycles.
+	 */
+	nfi_writew(nfc, 0xf1, NFI_CNRNB);
+	nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
+
+	mtk_nfc_hw_reset(nfc);
+
+	nfi_readl(nfc, NFI_INTR_STA);
+	nfi_writel(nfc, 0, NFI_INTR_EN);
+}
+
+static irqreturn_t mtk_nfc_irq(int irq, void *id)
+{
+	struct mtk_nfc *nfc = id;
+	u16 sta, ien;
+
+	sta = nfi_readw(nfc, NFI_INTR_STA);
+	ien = nfi_readw(nfc, NFI_INTR_EN);
+
+	if (!(sta & ien))
+		return IRQ_NONE;
+
+	nfi_writew(nfc, ~sta & ien, NFI_INTR_EN);
+	complete(&nfc->done);
+
+	return IRQ_HANDLED;
+}
+
+static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
+{
+	int ret;
+
+	ret = clk_prepare_enable(clk->nfi_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable nfi clk\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(clk->pad_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable pad clk\n");
+		clk_disable_unprepare(clk->nfi_clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
+{
+	clk_disable_unprepare(clk->nfi_clk);
+	clk_disable_unprepare(clk->pad_clk);
+}
+
+static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section,
+				struct mtd_oob_region *oob_region)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
+	u32 eccsteps;
+
+	eccsteps = mtd->writesize / chip->ecc.size;
+
+	if (section >= eccsteps)
+		return -ERANGE;
+
+	oob_region->length = fdm->reg_size - fdm->ecc_size;
+	oob_region->offset = section * fdm->reg_size + fdm->ecc_size;
+
+	return 0;
+}
+
+static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
+				struct mtd_oob_region *oob_region)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
+	u32 eccsteps;
+
+	if (section)
+		return -ERANGE;
+
+	eccsteps = mtd->writesize / chip->ecc.size;
+	oob_region->offset = mtk_nand->fdm.reg_size * eccsteps;
+	oob_region->length = mtd->oobsize - oob_region->offset;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = {
+	.free = mtk_nfc_ooblayout_free,
+	.ecc = mtk_nfc_ooblayout_ecc,
+};
+
+static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
+	u32 ecc_bytes;
+
+	ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
+
+	fdm->reg_size = chip->spare_per_sector - ecc_bytes;
+	if (fdm->reg_size > NFI_FDM_MAX_SIZE)
+		fdm->reg_size = NFI_FDM_MAX_SIZE;
+
+	/* bad block mark storage */
+	fdm->ecc_size = 1;
+}
+
+static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
+				struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+
+	if (mtd->writesize == 512)
+		bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap;
+	else {
+		bm_ctl->bm_swap = mtk_nfc_bad_mark_swap;
+		bm_ctl->sec = mtd->writesize / mtk_data_len(nand);
+		bm_ctl->pos = mtd->writesize % mtk_data_len(nand);
+	}
+}
+
+static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44,
+			48, 49, 50, 51, 52, 62, 63, 64};
+	u32 eccsteps, i;
+
+	eccsteps = mtd->writesize / nand->ecc.size;
+	*sps = mtd->oobsize / eccsteps;
+
+	if (nand->ecc.size == 1024)
+		*sps >>= 1;
+
+	for (i = 0; i < ARRAY_SIZE(spare); i++) {
+		if (*sps <= spare[i]) {
+			if (!i)
+				*sps = spare[i];
+			else if (*sps != spare[i])
+				*sps = spare[i - 1];
+			break;
+		}
+	}
+
+	if (i >= ARRAY_SIZE(spare))
+		*sps = spare[ARRAY_SIZE(spare) - 1];
+
+	if (nand->ecc.size == 1024)
+		*sps <<= 1;
+}
+
+static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
+{
+	struct nand_chip *nand = mtd_to_nand(mtd);
+	u32 spare;
+	int free;
+
+	/* support only ecc hw mode */
+	if (nand->ecc.mode != NAND_ECC_HW) {
+		dev_err(dev, "ecc.mode not supported\n");
+		return -EINVAL;
+	}
+
+	/* if optional dt settings not present */
+	if (!nand->ecc.size || !nand->ecc.strength) {
+
+		/* use datasheet requirements */
+		nand->ecc.strength = nand->ecc_strength_ds;
+		nand->ecc.size = nand->ecc_step_ds;
+
+		/* align eccstrength and eccsize
+		 * this controller only supports 512 and 1024 sizes
+		 */
+		if (nand->ecc.size < 1024) {
+			if (mtd->writesize > 512) {
+				nand->ecc.size = 1024;
+				nand->ecc.strength <<= 1;
+			} else
+				nand->ecc.size = 512;
+		} else
+			nand->ecc.size = 1024;
+
+		mtk_nfc_set_spare_per_sector(&spare, mtd);
+
+		/* calculate oob bytes except ecc parity data */
+		free = ((nand->ecc.strength * ECC_PARITY_BITS) + 7) >> 3;
+		free = spare - free;
+
+		/* enhance ecc strength if oob left is bigger than max FDM size
+		 * or reduce ecc strength if oob size is not enough for ecc
+		 * parity data.
+		 */
+		if (free > NFI_FDM_MAX_SIZE) {
+			spare -= NFI_FDM_MAX_SIZE;
+			nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS;
+		} else if (free < 0) {
+			spare -= NFI_FDM_MIN_SIZE;
+			nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS;
+		}
+	}
+
+	mtk_ecc_adjust_strength(&nand->ecc.strength);
+
+	dev_info(dev, "eccsize %d eccstrength %d\n",
+		nand->ecc.size, nand->ecc.strength);
+
+	return 0;
+}
+
+static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
+				struct device_node *np)
+{
+	struct mtk_nfc_nand_chip *chip;
+	struct nand_chip *nand;
+	struct mtd_info *mtd;
+	int nsels, len;
+	u32 tmp;
+	int ret;
+	int i;
+
+	if (!of_get_property(np, "reg", &nsels))
+		return -ENODEV;
+
+	nsels /= sizeof(u32);
+	if (!nsels || nsels > MTK_NAND_MAX_NSELS) {
+		dev_err(dev, "invalid reg property size %d\n", nsels);
+		return -EINVAL;
+	}
+
+	chip = devm_kzalloc(dev,
+			sizeof(*chip) + nsels * sizeof(u8), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->nsels = nsels;
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(np, "reg", i, &tmp);
+		if (ret) {
+			dev_err(dev, "reg property failure : %d\n", ret);
+			return ret;
+		}
+		chip->sels[i] = tmp;
+	}
+
+	nand = &chip->nand;
+	nand->controller = &nfc->controller;
+
+	nand_set_flash_node(nand, np);
+	nand_set_controller_data(nand, nfc);
+
+	nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
+	nand->dev_ready = mtk_nfc_dev_ready;
+	nand->select_chip = mtk_nfc_select_chip;
+	nand->write_byte = mtk_nfc_write_byte;
+	nand->write_buf = mtk_nfc_write_buf;
+	nand->read_byte = mtk_nfc_read_byte;
+	nand->read_buf = mtk_nfc_read_buf;
+	nand->cmd_ctrl = mtk_nfc_cmd_ctrl;
+
+	/* set default mode in case dt entry is missing */
+	nand->ecc.mode = NAND_ECC_HW;
+
+	nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
+	nand->ecc.write_page_raw = mtk_nfc_write_page_raw;
+	nand->ecc.write_page = mtk_nfc_write_page_hwecc;
+	nand->ecc.write_oob_raw = mtk_nfc_write_oob_std;
+	nand->ecc.write_oob = mtk_nfc_write_oob_std;
+
+	nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
+	nand->ecc.read_page_raw = mtk_nfc_read_page_raw;
+	nand->ecc.read_page = mtk_nfc_read_page_hwecc;
+	nand->ecc.read_oob_raw = mtk_nfc_read_oob_std;
+	nand->ecc.read_oob = mtk_nfc_read_oob_std;
+
+	mtd = nand_to_mtd(nand);
+	mtd->owner = THIS_MODULE;
+	mtd->dev.parent = dev;
+	mtd->name = MTK_NAME;
+	mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops);
+
+	mtk_nfc_hw_init(nfc);
+
+	ret = nand_scan_ident(mtd, nsels, NULL);
+	if (ret)
+		return -ENODEV;
+
+	/* store bbt magic in page, cause OOB is not protected */
+	if (nand->bbt_options & NAND_BBT_USE_FLASH)
+		nand->bbt_options |= NAND_BBT_NO_OOB;
+
+	ret = mtk_nfc_ecc_init(dev, mtd);
+	if (ret)
+		return -EINVAL;
+
+	if (nand->options & NAND_BUSWIDTH_16) {
+		dev_err(dev, "16bits buswidth not supported");
+		return -EINVAL;
+	}
+
+	mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd);
+	mtk_nfc_set_fdm(&chip->fdm, mtd);
+	mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd);
+
+	len = mtd->writesize + mtd->oobsize;
+	nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
+	if (!nfc->buffer)
+		return  -ENOMEM;
+
+	ret = nand_scan_tail(mtd);
+	if (ret)
+		return -ENODEV;
+
+	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
+	if (ret) {
+		dev_err(dev, "mtd parse partition error\n");
+		nand_release(mtd);
+		return ret;
+	}
+
+	list_add_tail(&chip->node, &nfc->chips);
+
+	return 0;
+}
+
+static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int ret;
+
+	for_each_child_of_node(np, nand_np) {
+		ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np);
+		if (ret) {
+			of_node_put(nand_np);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int mtk_nfc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct mtk_nfc *nfc;
+	struct resource *res;
+	int ret, irq;
+
+	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+	if (!nfc)
+		return -ENOMEM;
+
+	spin_lock_init(&nfc->controller.lock);
+	init_waitqueue_head(&nfc->controller.wq);
+	INIT_LIST_HEAD(&nfc->chips);
+
+	/* probe defer if not ready */
+	nfc->ecc = of_mtk_ecc_get(np);
+	if (IS_ERR(nfc->ecc))
+		return PTR_ERR(nfc->ecc);
+	else if (!nfc->ecc)
+		return -ENODEV;
+
+	nfc->dev = dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	nfc->regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(nfc->regs)) {
+		ret = PTR_ERR(nfc->regs);
+		dev_err(dev, "no nfi base\n");
+		goto release_ecc;
+	}
+
+	nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
+	if (IS_ERR(nfc->clk.nfi_clk)) {
+		dev_err(dev, "no clk\n");
+		ret = PTR_ERR(nfc->clk.nfi_clk);
+		goto release_ecc;
+	}
+
+	nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk");
+	if (IS_ERR(nfc->clk.pad_clk)) {
+		dev_err(dev, "no pad clk\n");
+		ret = PTR_ERR(nfc->clk.pad_clk);
+		goto release_ecc;
+	}
+
+	ret = mtk_nfc_enable_clk(dev, &nfc->clk);
+	if (ret)
+		goto release_ecc;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "no nfi irq resource\n");
+		ret = -EINVAL;
+		goto clk_disable;
+	}
+
+	ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc);
+	if (ret) {
+		dev_err(dev, "failed to request nfi irq\n");
+		goto clk_disable;
+	}
+
+	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "failed to set dma mask\n");
+		goto clk_disable;
+	}
+
+	platform_set_drvdata(pdev, nfc);
+
+	ret = mtk_nfc_nand_chips_init(dev, nfc);
+	if (ret) {
+		dev_err(dev, "failed to init nand chips\n");
+		goto clk_disable;
+	}
+
+	return 0;
+
+clk_disable:
+	mtk_nfc_disable_clk(&nfc->clk);
+
+release_ecc:
+	mtk_ecc_release(nfc->ecc);
+
+	return ret;
+}
+
+static int mtk_nfc_remove(struct platform_device *pdev)
+{
+	struct mtk_nfc *nfc = platform_get_drvdata(pdev);
+	struct mtk_nfc_nand_chip *chip;
+
+	while (!list_empty(&nfc->chips)) {
+		chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip,
+					node);
+		nand_release(nand_to_mtd(&chip->nand));
+		list_del(&chip->node);
+	}
+
+	mtk_ecc_release(nfc->ecc);
+	mtk_nfc_disable_clk(&nfc->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_nfc_suspend(struct device *dev)
+{
+	struct mtk_nfc *nfc = dev_get_drvdata(dev);
+
+	mtk_nfc_disable_clk(&nfc->clk);
+
+	return 0;
+}
+
+static int mtk_nfc_resume(struct device *dev)
+{
+	struct mtk_nfc *nfc = dev_get_drvdata(dev);
+	struct mtk_nfc_nand_chip *chip;
+	struct nand_chip *nand;
+	struct mtd_info *mtd;
+	int ret;
+	u32 i;
+
+	udelay(200);
+
+	ret = mtk_nfc_enable_clk(dev, &nfc->clk);
+	if (ret)
+		return ret;
+
+	mtk_nfc_hw_init(nfc);
+
+	/* reset NAND chip if VCC was powered off */
+	list_for_each_entry(chip, &nfc->chips, node) {
+		nand = &chip->nand;
+		mtd = nand_to_mtd(nand);
+		for (i = 0; i < chip->nsels; i++) {
+			nand->select_chip(mtd, i);
+			nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+		}
+	}
+
+	return 0;
+}
+static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
+#endif
+
+static const struct of_device_id mtk_nfc_id_table[] = {
+	{ .compatible = "mediatek,mt2701-nfc" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
+
+static struct platform_driver mtk_nfc_driver = {
+	.probe  = mtk_nfc_probe,
+	.remove = mtk_nfc_remove,
+	.driver = {
+		.name  = MTK_NAME,
+		.of_match_table = mtk_nfc_id_table,
+#ifdef CONFIG_PM_SLEEP
+		.pm = &mtk_nfc_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(mtk_nfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Xiaolei Li <xiaolei.li at mediatek.com>");
+MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");
-- 
2.7.4



From boris.brezillon at free-electrons.com  Tue Jun 14 09:44:33 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Tue, 14 Jun 2016 18:44:33 +0200
Subject: [PATCH v5 2/2] mtd: mediatek: driver for MTK Smart Device
In-Reply-To: <1465919451-1891-3-git-send-email-jorge.ramirez-ortiz@linaro.org>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
 <1465919451-1891-3-git-send-email-jorge.ramirez-ortiz@linaro.org>
Message-ID: <20160614184433.2b399c8b@bbrezillon>

Hi Jorge,

On Tue, 14 Jun 2016 11:50:51 -0400
Jorge Ramirez-Ortiz <jorge.ramirez-ortiz at linaro.org> wrote:

> Add support for mediatek's SDG1 NFC nand controller embedded in SoC
> 2701
> 

Apart from a few coding style issues that I can fix when applying the
patch it looks good to me.

If I understand correctly, you don't have access to the hardware
anymore. Xiolei (or anyone else owning the proper hardware), can you add
your Tested-by?

Thanks,

Boris

> Signed-off-by: Jorge Ramirez-Ortiz <jorge.ramirez-ortiz at linaro.org>
> ---
>  drivers/mtd/nand/Kconfig    |    7 +
>  drivers/mtd/nand/Makefile   |    1 +
>  drivers/mtd/nand/mtk_ecc.c  |  528 +++++++++++++++
>  drivers/mtd/nand/mtk_ecc.h  |   50 ++
>  drivers/mtd/nand/mtk_nand.c | 1509 +++++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 2095 insertions(+)
>  create mode 100644 drivers/mtd/nand/mtk_ecc.c
>  create mode 100644 drivers/mtd/nand/mtk_ecc.h
>  create mode 100644 drivers/mtd/nand/mtk_nand.c
> 
> diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
> index f05e0e9..3c26e89 100644
> --- a/drivers/mtd/nand/Kconfig
> +++ b/drivers/mtd/nand/Kconfig
> @@ -563,4 +563,11 @@ config MTD_NAND_QCOM
>  	  Enables support for NAND flash chips on SoCs containing the EBI2 NAND
>  	  controller. This controller is found on IPQ806x SoC.
>  
> +config MTD_NAND_MTK
> +	tristate "Support for NAND controller on MTK SoCs"
> +	depends on HAS_DMA
> +	help
> +	  Enables support for NAND controller on MTK SoCs.
> +	  This controller is found on mt27xx, mt81xx, mt65xx SoCs.
> +
>  endif # MTD_NAND
> diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
> index f553353..cafde6f 100644
> --- a/drivers/mtd/nand/Makefile
> +++ b/drivers/mtd/nand/Makefile
> @@ -57,5 +57,6 @@ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
>  obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
>  obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
>  obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
> +obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_nand.o mtk_ecc.o
>  
>  nand-objs := nand_base.o nand_bbt.o nand_timings.o
> diff --git a/drivers/mtd/nand/mtk_ecc.c b/drivers/mtd/nand/mtk_ecc.c
> new file mode 100644
> index 0000000..2ab82ea
> --- /dev/null
> +++ b/drivers/mtd/nand/mtk_ecc.c
> @@ -0,0 +1,528 @@
> +/*
> + * MTK ECC controller driver.
> + * Copyright (C) 2016  MediaTek Inc.
> + * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
> + *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/iopoll.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/mutex.h>
> +
> +#include "mtk_ecc.h"
> +
> +#define ECC_IDLE_MASK		BIT(0)
> +#define ECC_IRQ_EN		BIT(0)
> +#define ECC_OP_ENABLE		(1)
> +#define ECC_OP_DISABLE		(0)
> +
> +#define ECC_ENCCON		(0x00)
> +#define ECC_ENCCNFG		(0x04)
> +#define		ECC_CNFG_4BIT		(0)
> +#define		ECC_CNFG_6BIT		(1)
> +#define		ECC_CNFG_8BIT		(2)
> +#define		ECC_CNFG_10BIT		(3)
> +#define		ECC_CNFG_12BIT		(4)
> +#define		ECC_CNFG_14BIT		(5)
> +#define		ECC_CNFG_16BIT		(6)
> +#define		ECC_CNFG_18BIT		(7)
> +#define		ECC_CNFG_20BIT		(8)
> +#define		ECC_CNFG_22BIT		(9)
> +#define		ECC_CNFG_24BIT		(0xa)
> +#define		ECC_CNFG_28BIT		(0xb)
> +#define		ECC_CNFG_32BIT		(0xc)
> +#define		ECC_CNFG_36BIT		(0xd)
> +#define		ECC_CNFG_40BIT		(0xe)
> +#define		ECC_CNFG_44BIT		(0xf)
> +#define		ECC_CNFG_48BIT		(0x10)
> +#define		ECC_CNFG_52BIT		(0x11)
> +#define		ECC_CNFG_56BIT		(0x12)
> +#define		ECC_CNFG_60BIT		(0x13)
> +#define		ECC_MODE_SHIFT		(5)
> +#define		ECC_MS_SHIFT		(16)
> +#define ECC_ENCDIADDR		(0x08)
> +#define ECC_ENCIDLE		(0x0C)
> +#define ECC_ENCPAR(x)		(0x10 + (x) * sizeof(u32))
> +#define ECC_ENCIRQ_EN		(0x80)
> +#define ECC_ENCIRQ_STA		(0x84)
> +#define ECC_DECCON		(0x100)
> +#define ECC_DECCNFG		(0x104)
> +#define		DEC_EMPTY_EN		BIT(31)
> +#define		DEC_CNFG_CORRECT	(0x3 << 12)
> +#define ECC_DECIDLE		(0x10C)
> +#define ECC_DECENUM0		(0x114)
> +#define		ERR_MASK		(0x3f)
> +#define ECC_DECDONE		(0x124)
> +#define ECC_DECIRQ_EN		(0x200)
> +#define ECC_DECIRQ_STA		(0x204)
> +
> +#define ECC_TIMEOUT		(500000)
> +
> +#define ECC_IDLE_REG(op)	((op) == ECC_ENCODE ? ECC_ENCIDLE : ECC_DECIDLE)
> +#define ECC_CTL_REG(op)		((op) == ECC_ENCODE ? ECC_ENCCON : ECC_DECCON)
> +#define ECC_IRQ_REG(op)		((op) == ECC_ENCODE ? \
> +					ECC_ENCIRQ_EN : ECC_DECIRQ_EN)
> +
> +struct mtk_ecc {
> +	struct device *dev;
> +	void __iomem *regs;
> +	struct clk *clk;
> +
> +	struct completion done;
> +	struct mutex lock;
> +	u32 sectors;
> +};
> +
> +static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
> +					enum mtk_ecc_operation op)
> +{
> +	struct device *dev = ecc->dev;
> +	u32 val;
> +	int ret;
> +
> +	ret = readl_poll_timeout_atomic(ecc->regs + ECC_IDLE_REG(op), val,
> +					val & ECC_IDLE_MASK,
> +					10, ECC_TIMEOUT);
> +	if (ret)
> +		dev_warn(dev, "%s NOT idle\n",
> +			op == ECC_ENCODE ? "encoder" : "decoder");
> +}
> +
> +static irqreturn_t mtk_ecc_irq(int irq, void *id)
> +{
> +	struct mtk_ecc *ecc = id;
> +	enum mtk_ecc_operation op;
> +	u32 dec, enc;
> +
> +	dec = readw(ecc->regs + ECC_DECIRQ_STA) & ECC_IRQ_EN;
> +	if (dec) {
> +		op = ECC_DECODE;
> +		dec = readw(ecc->regs + ECC_DECDONE);
> +		if (dec & ecc->sectors) {
> +			ecc->sectors = 0;
> +			complete(&ecc->done);
> +		} else
> +			return IRQ_HANDLED;
> +	} else {
> +		enc = readl(ecc->regs + ECC_ENCIRQ_STA) & ECC_IRQ_EN;
> +		if (enc) {
> +			op = ECC_ENCODE;
> +			complete(&ecc->done);
> +		} else
> +			return IRQ_NONE;
> +	}
> +
> +	writel(0, ecc->regs + ECC_IRQ_REG(op));
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void mtk_ecc_config(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
> +{
> +	u32 ecc_bit = ECC_CNFG_4BIT, dec_sz, enc_sz;
> +	u32 reg;
> +
> +	switch (config->strength) {
> +	case 4:
> +		ecc_bit = ECC_CNFG_4BIT;
> +		break;
> +	case 6:
> +		ecc_bit = ECC_CNFG_6BIT;
> +		break;
> +	case 8:
> +		ecc_bit = ECC_CNFG_8BIT;
> +		break;
> +	case 10:
> +		ecc_bit = ECC_CNFG_10BIT;
> +		break;
> +	case 12:
> +		ecc_bit = ECC_CNFG_12BIT;
> +		break;
> +	case 14:
> +		ecc_bit = ECC_CNFG_14BIT;
> +		break;
> +	case 16:
> +		ecc_bit = ECC_CNFG_16BIT;
> +		break;
> +	case 18:
> +		ecc_bit = ECC_CNFG_18BIT;
> +		break;
> +	case 20:
> +		ecc_bit = ECC_CNFG_20BIT;
> +		break;
> +	case 22:
> +		ecc_bit = ECC_CNFG_22BIT;
> +		break;
> +	case 24:
> +		ecc_bit = ECC_CNFG_24BIT;
> +		break;
> +	case 28:
> +		ecc_bit = ECC_CNFG_28BIT;
> +		break;
> +	case 32:
> +		ecc_bit = ECC_CNFG_32BIT;
> +		break;
> +	case 36:
> +		ecc_bit = ECC_CNFG_36BIT;
> +		break;
> +	case 40:
> +		ecc_bit = ECC_CNFG_40BIT;
> +		break;
> +	case 44:
> +		ecc_bit = ECC_CNFG_44BIT;
> +		break;
> +	case 48:
> +		ecc_bit = ECC_CNFG_48BIT;
> +		break;
> +	case 52:
> +		ecc_bit = ECC_CNFG_52BIT;
> +		break;
> +	case 56:
> +		ecc_bit = ECC_CNFG_56BIT;
> +		break;
> +	case 60:
> +		ecc_bit = ECC_CNFG_60BIT;
> +		break;
> +	default:
> +		dev_err(ecc->dev, "invalid strength %d, default to 4 bits\n",
> +			config->strength);
> +	}
> +
> +	if (config->op == ECC_ENCODE) {
> +		/* configure ECC encoder (in bits) */
> +		enc_sz = config->len << 3;
> +
> +		reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
> +		reg |= (enc_sz << ECC_MS_SHIFT);
> +		writel(reg, ecc->regs + ECC_ENCCNFG);
> +
> +		if (config->mode != ECC_NFI_MODE)
> +			writel(lower_32_bits(config->addr),
> +				ecc->regs + ECC_ENCDIADDR);
> +
> +	} else {
> +		/* configure ECC decoder (in bits) */
> +		dec_sz = (config->len << 3) +
> +					config->strength * ECC_PARITY_BITS;
> +
> +		reg = ecc_bit | (config->mode << ECC_MODE_SHIFT);
> +		reg |= (dec_sz << ECC_MS_SHIFT) | DEC_CNFG_CORRECT;
> +		reg |= DEC_EMPTY_EN;
> +		writel(reg, ecc->regs + ECC_DECCNFG);
> +
> +		if (config->sectors)
> +			ecc->sectors = 1 << (config->sectors - 1);
> +	}
> +}
> +
> +void mtk_ecc_get_stats(struct mtk_ecc *ecc, struct mtk_ecc_stats *stats,
> +			int sectors)
> +{
> +	u32 offset, i, err;
> +	u32 bitflips = 0;
> +
> +	stats->corrected = 0;
> +	stats->failed = 0;
> +
> +	for (i = 0; i < sectors; i++) {
> +		offset = (i >> 2) << 2;
> +		err = readl(ecc->regs + ECC_DECENUM0 + offset);
> +		err = err >> ((i % 4) * 8);
> +		err &= ERR_MASK;
> +		if (err == ERR_MASK) {
> +			/* uncorrectable errors */
> +			stats->failed++;
> +			continue;
> +		}
> +
> +		stats->corrected += err;
> +		bitflips = max_t(u32, bitflips, err);
> +	}
> +
> +	stats->bitflips = bitflips;
> +}
> +EXPORT_SYMBOL(mtk_ecc_get_stats);
> +
> +void mtk_ecc_release(struct mtk_ecc *ecc)
> +{
> +	clk_disable_unprepare(ecc->clk);
> +	put_device(ecc->dev);
> +}
> +EXPORT_SYMBOL(mtk_ecc_release);
> +
> +static void mtk_ecc_hw_init(struct mtk_ecc *ecc)
> +{
> +	mtk_ecc_wait_idle(ecc, ECC_ENCODE);
> +	writew(ECC_OP_DISABLE, ecc->regs + ECC_ENCCON);
> +
> +	mtk_ecc_wait_idle(ecc, ECC_DECODE);
> +	writel(ECC_OP_DISABLE, ecc->regs + ECC_DECCON);
> +}
> +
> +static struct mtk_ecc *mtk_ecc_get(struct device_node *np)
> +{
> +	struct platform_device *pdev;
> +	struct mtk_ecc *ecc;
> +
> +	pdev = of_find_device_by_node(np);
> +	if (!pdev || !platform_get_drvdata(pdev))
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	get_device(&pdev->dev);
> +	ecc = platform_get_drvdata(pdev);
> +	clk_prepare_enable(ecc->clk);
> +	mtk_ecc_hw_init(ecc);
> +
> +	return ecc;
> +}
> +
> +struct mtk_ecc *of_mtk_ecc_get(struct device_node *of_node)
> +{
> +	struct mtk_ecc *ecc = NULL;
> +	struct device_node *np;
> +
> +	np = of_parse_phandle(of_node, "ecc-engine", 0);
> +	if (np) {
> +		ecc = mtk_ecc_get(np);
> +		of_node_put(np);
> +	}
> +
> +	return ecc;
> +}
> +EXPORT_SYMBOL(of_mtk_ecc_get);
> +
> +int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
> +{
> +	enum mtk_ecc_operation op = config->op;
> +	int ret;
> +
> +	ret = mutex_lock_interruptible(&ecc->lock);
> +	if (ret) {
> +		dev_err(ecc->dev, "interrupted when attempting to lock\n");
> +		return ret;
> +	}
> +
> +	mtk_ecc_wait_idle(ecc, op);
> +	mtk_ecc_config(ecc, config);
> +	writew(ECC_OP_ENABLE, ecc->regs + ECC_CTL_REG(op));
> +
> +	init_completion(&ecc->done);
> +	writew(ECC_IRQ_EN, ecc->regs + ECC_IRQ_REG(op));
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mtk_ecc_enable);
> +
> +void mtk_ecc_disable(struct mtk_ecc *ecc)
> +{
> +	enum mtk_ecc_operation op = ECC_ENCODE;
> +
> +	/* find out the running operation */
> +	if (readw(ecc->regs + ECC_CTL_REG(op)) != ECC_OP_ENABLE)
> +		op = ECC_DECODE;
> +
> +	/* disable it */
> +	mtk_ecc_wait_idle(ecc, op);
> +	writew(0, ecc->regs + ECC_IRQ_REG(op));
> +	writew(ECC_OP_DISABLE, ecc->regs + ECC_CTL_REG(op));
> +
> +	mutex_unlock(&ecc->lock);
> +}
> +EXPORT_SYMBOL(mtk_ecc_disable);
> +
> +int mtk_ecc_wait_done(struct mtk_ecc *ecc, enum mtk_ecc_operation op)
> +{
> +	int ret;
> +
> +	ret = wait_for_completion_timeout(&ecc->done, msecs_to_jiffies(500));
> +	if (!ret) {
> +		dev_err(ecc->dev, "%s timeout - interrupt did not arrive)\n",
> +				(op == ECC_ENCODE) ? "encoder" : "decoder");
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(mtk_ecc_wait_done);
> +
> +int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
> +			u8 *data, u32 bytes)
> +{
> +	dma_addr_t addr;
> +	u32 *p, len, i;
> +	int ret = 0;
> +
> +	addr = dma_map_single(ecc->dev, data, bytes, DMA_TO_DEVICE);
> +	ret = dma_mapping_error(ecc->dev, addr);
> +	if (ret) {
> +		dev_err(ecc->dev, "dma mapping error\n");
> +		return -EINVAL;
> +	}
> +
> +	config->op = ECC_ENCODE;
> +	config->addr = addr;
> +	ret = mtk_ecc_enable(ecc, config);
> +	if (ret) {
> +		dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
> +		return ret;
> +	}
> +
> +	ret = mtk_ecc_wait_done(ecc, ECC_ENCODE);
> +	if (ret)
> +		goto timeout;
> +
> +	mtk_ecc_wait_idle(ecc, ECC_ENCODE);
> +
> +	/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
> +	len = (config->strength * ECC_PARITY_BITS + 7) >> 3;
> +	p = (u32 *) (data + bytes);
> +
> +	/* write the parity bytes generated by the ECC back to the OOB region */
> +	for (i = 0; i < len; i++)
> +		p[i] = readl(ecc->regs + ECC_ENCPAR(i));
> +timeout:
> +
> +	dma_unmap_single(ecc->dev, addr, bytes, DMA_TO_DEVICE);
> +	mtk_ecc_disable(ecc);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(mtk_ecc_encode);
> +
> +void mtk_ecc_adjust_strength(u32 *p)
> +{
> +	u32 ecc[] = {4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36,
> +			40, 44, 48, 52, 56, 60};
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(ecc); i++) {
> +		if (*p <= ecc[i]) {
> +			if (!i)
> +				*p = ecc[i];
> +			else if (*p != ecc[i])
> +				*p = ecc[i - 1];
> +			return;
> +		}
> +	}
> +
> +	*p = ecc[ARRAY_SIZE(ecc) - 1];
> +}
> +EXPORT_SYMBOL(mtk_ecc_adjust_strength);
> +
> +static int mtk_ecc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct mtk_ecc *ecc;
> +	struct resource *res;
> +	int irq, ret;
> +
> +	ecc = devm_kzalloc(dev, sizeof(*ecc), GFP_KERNEL);
> +	if (!ecc)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	ecc->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(ecc->regs)) {
> +		dev_err(dev, "failed to map regs: %ld\n", PTR_ERR(ecc->regs));
> +		return PTR_ERR(ecc->regs);
> +	}
> +
> +	ecc->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(ecc->clk)) {
> +		dev_err(dev, "failed to get clock: %ld\n", PTR_ERR(ecc->clk));
> +		return PTR_ERR(ecc->clk);
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "failed to get irq\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
> +	if (ret) {
> +		dev_err(dev, "failed to set DMA mask\n");
> +		return ret;
> +	}
> +
> +	ret = devm_request_irq(dev, irq, mtk_ecc_irq, 0x0, "mtk-ecc", ecc);
> +	if (ret) {
> +		dev_err(dev, "failed to request irq\n");
> +		return -EINVAL;
> +	}
> +
> +	ecc->dev = dev;
> +	mutex_init(&ecc->lock);
> +	platform_set_drvdata(pdev, ecc);
> +	dev_info(dev, "probed\n");
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_ecc_suspend(struct device *dev)
> +{
> +	struct mtk_ecc *ecc = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(ecc->clk);
> +
> +	return 0;
> +}
> +
> +static int mtk_ecc_resume(struct device *dev)
> +{
> +	struct mtk_ecc *ecc = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(ecc->clk);
> +	if (ret) {
> +		dev_err(dev, "failed to enable clk\n");
> +		return ret;
> +	}
> +
> +	mtk_ecc_hw_init(ecc);
> +
> +	return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(mtk_ecc_pm_ops, mtk_ecc_suspend, mtk_ecc_resume);
> +#endif
> +
> +static const struct of_device_id mtk_ecc_dt_match[] = {
> +	{ .compatible = "mediatek,mt2701-ecc" },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, mtk_ecc_dt_match);
> +
> +static struct platform_driver mtk_ecc_driver = {
> +	.probe  = mtk_ecc_probe,
> +	.driver = {
> +		.name  = "mtk-ecc",
> +		.of_match_table = of_match_ptr(mtk_ecc_dt_match),
> +#ifdef CONFIG_PM_SLEEP
> +		.pm = &mtk_ecc_pm_ops,
> +#endif
> +	},
> +};
> +
> +module_platform_driver(mtk_ecc_driver);
> +
> +MODULE_AUTHOR("Xiaolei Li <xiaolei.li at mediatek.com>");
> +MODULE_DESCRIPTION("MTK Nand ECC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/mtd/nand/mtk_ecc.h b/drivers/mtd/nand/mtk_ecc.h
> new file mode 100644
> index 0000000..cbeba5c
> --- /dev/null
> +++ b/drivers/mtd/nand/mtk_ecc.h
> @@ -0,0 +1,50 @@
> +/*
> + * MTK SDG1 ECC controller
> + *
> + * Copyright (c) 2016 Mediatek
> + * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
> + *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published
> + * by the Free Software Foundation.
> + */
> +
> +#ifndef __DRIVERS_MTD_NAND_MTK_ECC_H__
> +#define __DRIVERS_MTD_NAND_MTK_ECC_H__
> +
> +#include <linux/types.h>
> +
> +#define ECC_PARITY_BITS		(14)
> +
> +enum mtk_ecc_mode {ECC_DMA_MODE = 0, ECC_NFI_MODE = 1};
> +enum mtk_ecc_operation {ECC_ENCODE, ECC_DECODE};
> +
> +struct device_node;
> +struct mtk_ecc;
> +
> +struct mtk_ecc_stats {
> +	u32 corrected;
> +	u32 bitflips;
> +	u32 failed;
> +};
> +
> +struct mtk_ecc_config {
> +	enum mtk_ecc_operation op;
> +	enum mtk_ecc_mode mode;
> +	dma_addr_t addr;
> +	u32 strength;
> +	u32 sectors;
> +	u32 len;
> +};
> +
> +int mtk_ecc_encode(struct mtk_ecc *, struct mtk_ecc_config *, u8 *, u32);
> +void mtk_ecc_get_stats(struct mtk_ecc *, struct mtk_ecc_stats *, int);
> +int mtk_ecc_wait_done(struct mtk_ecc *, enum mtk_ecc_operation);
> +int mtk_ecc_enable(struct mtk_ecc *, struct mtk_ecc_config *);
> +void mtk_ecc_disable(struct mtk_ecc *);
> +void mtk_ecc_adjust_strength(u32 *);
> +
> +struct mtk_ecc *of_mtk_ecc_get(struct device_node *);
> +void mtk_ecc_release(struct mtk_ecc *);
> +
> +#endif
> diff --git a/drivers/mtd/nand/mtk_nand.c b/drivers/mtd/nand/mtk_nand.c
> new file mode 100644
> index 0000000..ad8863d
> --- /dev/null
> +++ b/drivers/mtd/nand/mtk_nand.c
> @@ -0,0 +1,1509 @@
> +/*
> + * MTK NAND Flash controller driver.
> + * Copyright (C) 2016 MediaTek Inc.
> + * Authors:	Xiaolei Li		<xiaolei.li at mediatek.com>
> + *		Jorge Ramirez-Ortiz	<jorge.ramirez-ortiz at linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/clk.h>
> +#include <linux/mtd/nand.h>
> +#include <linux/mtd/mtd.h>
> +#include <linux/module.h>
> +#include <linux/iopoll.h>
> +#include <linux/of.h>
> +#include "mtk_ecc.h"
> +
> +/* NAND controller register definition */
> +#define NFI_CNFG		(0x00)
> +#define		CNFG_AHB		BIT(0)
> +#define		CNFG_READ_EN		BIT(1)
> +#define		CNFG_DMA_BURST_EN	BIT(2)
> +#define		CNFG_BYTE_RW		BIT(6)
> +#define		CNFG_HW_ECC_EN		BIT(8)
> +#define		CNFG_AUTO_FMT_EN	BIT(9)
> +#define		CNFG_OP_CUST		(6 << 12)
> +#define NFI_PAGEFMT		(0x04)
> +#define		PAGEFMT_FDM_ECC_SHIFT	(12)
> +#define		PAGEFMT_FDM_SHIFT	(8)
> +#define		PAGEFMT_SPARE_16	(0)
> +#define		PAGEFMT_SPARE_26	(1)
> +#define		PAGEFMT_SPARE_27	(2)
> +#define		PAGEFMT_SPARE_28	(3)
> +#define		PAGEFMT_SPARE_32	(4)
> +#define		PAGEFMT_SPARE_36	(5)
> +#define		PAGEFMT_SPARE_40	(6)
> +#define		PAGEFMT_SPARE_44	(7)
> +#define		PAGEFMT_SPARE_48	(8)
> +#define		PAGEFMT_SPARE_49	(9)
> +#define		PAGEFMT_SPARE_50	(0xa)
> +#define		PAGEFMT_SPARE_51	(0xb)
> +#define		PAGEFMT_SPARE_52	(0xc)
> +#define		PAGEFMT_SPARE_62	(0xd)
> +#define		PAGEFMT_SPARE_63	(0xe)
> +#define		PAGEFMT_SPARE_64	(0xf)
> +#define		PAGEFMT_SPARE_SHIFT	(4)
> +#define		PAGEFMT_SEC_SEL_512	BIT(2)
> +#define		PAGEFMT_512_2K		(0)
> +#define		PAGEFMT_2K_4K		(1)
> +#define		PAGEFMT_4K_8K		(2)
> +#define		PAGEFMT_8K_16K		(3)
> +/* NFI control */
> +#define NFI_CON			(0x08)
> +#define		CON_FIFO_FLUSH		BIT(0)
> +#define		CON_NFI_RST		BIT(1)
> +#define		CON_BRD			BIT(8)  /* burst  read */
> +#define		CON_BWR			BIT(9)	/* burst  write */
> +#define		CON_SEC_SHIFT		(12)
> +/* Timming control register */
> +#define NFI_ACCCON		(0x0C)
> +#define NFI_INTR_EN		(0x10)
> +#define		INTR_AHB_DONE_EN	BIT(6)
> +#define NFI_INTR_STA		(0x14)
> +#define NFI_CMD			(0x20)
> +#define NFI_ADDRNOB		(0x30)
> +#define NFI_COLADDR		(0x34)
> +#define NFI_ROWADDR		(0x38)
> +#define NFI_STRDATA		(0x40)
> +#define		STAR_EN			(1)
> +#define		STAR_DE			(0)
> +#define NFI_CNRNB		(0x44)
> +#define NFI_DATAW		(0x50)
> +#define NFI_DATAR		(0x54)
> +#define NFI_PIO_DIRDY		(0x58)
> +#define		PIO_DI_RDY		(0x01)
> +#define NFI_STA			(0x60)
> +#define		STA_CMD			BIT(0)
> +#define		STA_ADDR		BIT(1)
> +#define		STA_BUSY		BIT(8)
> +#define		STA_EMP_PAGE		BIT(12)
> +#define		NFI_FSM_CUSTDATA	(0xe << 16)
> +#define		NFI_FSM_MASK		(0xf << 16)
> +#define NFI_ADDRCNTR		(0x70)
> +#define		CNTR_MASK		GENMASK(16, 12)
> +#define NFI_STRADDR		(0x80)
> +#define NFI_BYTELEN		(0x84)
> +#define NFI_CSEL		(0x90)
> +#define NFI_FDML(x)		(0xA0 + (x) * sizeof(u32) * 2)
> +#define NFI_FDMM(x)		(0xA4 + (x) * sizeof(u32) * 2)
> +#define NFI_FDM_MAX_SIZE	(8)
> +#define NFI_FDM_MIN_SIZE	(1)
> +#define NFI_MASTER_STA		(0x224)
> +#define		MASTER_STA_MASK		(0x0FFF)
> +#define NFI_EMPTY_THRESH	(0x23C)
> +
> +#define MTK_NAME		"mtk-nand"
> +#define KB(x)			((x) * 1024UL)
> +#define MB(x)			(KB(x) * 1024UL)
> +
> +#define MTK_TIMEOUT		(500000)
> +#define MTK_RESET_TIMEOUT	(1000000)
> +#define MTK_MAX_SECTOR		(16)
> +#define MTK_NAND_MAX_NSELS	(2)
> +
> +struct mtk_nfc_bad_mark_ctl {
> +	void (*bm_swap)(struct mtd_info *, uint8_t *buf, int raw);
> +	u32 sec;
> +	u32 pos;
> +};
> +
> +/*
> + * FDM: region used to store free OOB data
> + */
> +struct mtk_nfc_fdm {
> +	u32 reg_size;
> +	u32 ecc_size;
> +};
> +
> +struct mtk_nfc_nand_chip {
> +	struct list_head node;
> +	struct nand_chip nand;
> +
> +	struct mtk_nfc_bad_mark_ctl bad_mark;
> +	struct mtk_nfc_fdm fdm;
> +	u32 spare_per_sector;
> +
> +	int nsels;
> +	u8 sels[0];
> +	/* nothing after this field */
> +};
> +
> +struct mtk_nfc_clk {
> +	struct clk *nfi_clk;
> +	struct clk *pad_clk;
> +};
> +
> +struct mtk_nfc {
> +	struct nand_hw_control controller;
> +	struct mtk_ecc_config ecc_cfg;
> +	struct mtk_nfc_clk clk;
> +	struct mtk_ecc *ecc;
> +
> +	struct device *dev;
> +	void __iomem *regs;
> +
> +	struct completion done;
> +	struct list_head chips;
> +
> +	u8 *buffer;
> +};
> +
> +static inline struct mtk_nfc_nand_chip *to_mtk_nand(struct nand_chip *nand)
> +{
> +	return container_of(nand, struct mtk_nfc_nand_chip, nand);
> +}
> +
> +static inline uint8_t *data_ptr(struct nand_chip *chip, const uint8_t *p, int i)
> +{
> +	return (uint8_t *) p + i * chip->ecc.size;
> +}
> +
> +static inline uint8_t *oob_ptr(struct nand_chip *chip, int i)
> +{
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	uint8_t *poi;
> +
> +	/* map the sector's FDM data to free oob:
> +	 * the beginning of the oob area stores the FDM data of bad mark sectors
> +	 */
> +
> +	if (i < mtk_nand->bad_mark.sec)
> +		poi = chip->oob_poi + (i + 1) * mtk_nand->fdm.reg_size;
> +	else if (i == mtk_nand->bad_mark.sec)
> +		poi = chip->oob_poi;
> +	else
> +		poi = chip->oob_poi + i * mtk_nand->fdm.reg_size;
> +
> +	return poi;
> +}
> +
> +static inline int mtk_data_len(struct nand_chip *chip)
> +{
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +
> +	return chip->ecc.size + mtk_nand->spare_per_sector;
> +}
> +
> +static inline uint8_t *mtk_data_ptr(struct nand_chip *chip,  int i)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +
> +	return nfc->buffer + i * mtk_data_len(chip);
> +}
> +
> +static inline uint8_t *mtk_oob_ptr(struct nand_chip *chip, int i)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +
> +	return nfc->buffer + i * mtk_data_len(chip) + chip->ecc.size;
> +}
> +
> +static inline void nfi_writel(struct mtk_nfc *nfc, u32 val, u32 reg)
> +{
> +	writel(val, nfc->regs + reg);
> +}
> +
> +static inline void nfi_writew(struct mtk_nfc *nfc, u16 val, u32 reg)
> +{
> +	writew(val, nfc->regs + reg);
> +}
> +
> +static inline void nfi_writeb(struct mtk_nfc *nfc, u8 val, u32 reg)
> +{
> +	writeb(val, nfc->regs + reg);
> +}
> +
> +static inline u32 nfi_readl(struct mtk_nfc *nfc, u32 reg)
> +{
> +	return readl_relaxed(nfc->regs + reg);
> +}
> +
> +static inline u16 nfi_readw(struct mtk_nfc *nfc, u32 reg)
> +{
> +	return readw_relaxed(nfc->regs + reg);
> +}
> +
> +static inline u8 nfi_readb(struct mtk_nfc *nfc, u32 reg)
> +{
> +	return readb_relaxed(nfc->regs + reg);
> +}
> +
> +static void mtk_nfc_hw_reset(struct mtk_nfc *nfc)
> +{
> +	struct device *dev = nfc->dev;
> +	u32 val;
> +	int ret;
> +
> +	/* reset all registers and force the NFI master to terminate */
> +	nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
> +
> +	/* wait for the master to finish the last transaction */
> +	ret = readl_poll_timeout(nfc->regs + NFI_MASTER_STA, val,
> +			!(val & MASTER_STA_MASK), 50, MTK_RESET_TIMEOUT);
> +	if (ret)
> +		dev_warn(dev, "master active in reset [0x%x] = 0x%x\n",
> +			NFI_MASTER_STA, val);
> +
> +	/* ensure any status register affected by the NFI master is reset */
> +	nfi_writel(nfc, CON_FIFO_FLUSH | CON_NFI_RST, NFI_CON);
> +	nfi_writew(nfc, STAR_DE, NFI_STRDATA);
> +}
> +
> +static int mtk_nfc_send_command(struct mtk_nfc *nfc, u8 command)
> +{
> +	struct device *dev = nfc->dev;
> +	u32 val;
> +	int ret;
> +
> +	nfi_writel(nfc, command, NFI_CMD);
> +
> +	ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
> +					!(val & STA_CMD), 10,  MTK_TIMEOUT);
> +	if (ret) {
> +		dev_warn(dev, "nfi core timed out entering command mode\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_send_address(struct mtk_nfc *nfc, int addr)
> +{
> +	struct device *dev = nfc->dev;
> +	u32 val;
> +	int ret;
> +
> +	nfi_writel(nfc, addr, NFI_COLADDR);
> +	nfi_writel(nfc, 0, NFI_ROWADDR);
> +	nfi_writew(nfc, 1, NFI_ADDRNOB);
> +
> +	ret = readl_poll_timeout_atomic(nfc->regs + NFI_STA, val,
> +					!(val & STA_ADDR), 10, MTK_TIMEOUT);
> +	if (ret) {
> +		dev_warn(dev, "nfi core timed out entering address mode\n");
> +		return -EIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_hw_runtime_config(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	u32 fmt, spare;
> +
> +	if (!mtd->writesize)
> +		return 0;
> +
> +	spare = mtk_nand->spare_per_sector;
> +
> +	switch (mtd->writesize) {
> +	case 512:
> +		fmt = PAGEFMT_512_2K | PAGEFMT_SEC_SEL_512;
> +		break;
> +	case KB(2):
> +		if (chip->ecc.size == 512)
> +			fmt = PAGEFMT_2K_4K | PAGEFMT_SEC_SEL_512;
> +		else
> +			fmt = PAGEFMT_512_2K;
> +		break;
> +	case KB(4):
> +		if (chip->ecc.size == 512)
> +			fmt = PAGEFMT_4K_8K | PAGEFMT_SEC_SEL_512;
> +		else
> +			fmt = PAGEFMT_2K_4K;
> +		break;
> +	case KB(8):
> +		if (chip->ecc.size == 512)
> +			fmt = PAGEFMT_8K_16K | PAGEFMT_SEC_SEL_512;
> +		else
> +			fmt = PAGEFMT_4K_8K;
> +		break;
> +	case KB(16):
> +		fmt = PAGEFMT_8K_16K;
> +		break;
> +	default:
> +		dev_err(nfc->dev, "invalid page len: %d\n", mtd->writesize);
> +		return -EINVAL;
> +	}
> +
> +	/* the hardware will double the value for this eccsize, so we need to
> +	 * halve it
> +	 */
> +	if (chip->ecc.size == 1024)
> +		spare >>= 1;
> +
> +	switch (spare) {
> +	case 16:
> +		fmt |= (PAGEFMT_SPARE_16 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 26:
> +		fmt |= (PAGEFMT_SPARE_26 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 27:
> +		fmt |= (PAGEFMT_SPARE_27 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 28:
> +		fmt |= (PAGEFMT_SPARE_28 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 32:
> +		fmt |= (PAGEFMT_SPARE_32 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 36:
> +		fmt |= (PAGEFMT_SPARE_36 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 40:
> +		fmt |= (PAGEFMT_SPARE_40 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 44:
> +		fmt |= (PAGEFMT_SPARE_44 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 48:
> +		fmt |= (PAGEFMT_SPARE_48 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 49:
> +		fmt |= (PAGEFMT_SPARE_49 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 50:
> +		fmt |= (PAGEFMT_SPARE_50 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 51:
> +		fmt |= (PAGEFMT_SPARE_51 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 52:
> +		fmt |= (PAGEFMT_SPARE_52 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 62:
> +		fmt |= (PAGEFMT_SPARE_62 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 63:
> +		fmt |= (PAGEFMT_SPARE_63 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	case 64:
> +		fmt |= (PAGEFMT_SPARE_64 << PAGEFMT_SPARE_SHIFT);
> +		break;
> +	default:
> +		dev_err(nfc->dev, "invalid spare per sector %d\n", spare);
> +		return -EINVAL;
> +	}
> +
> +	fmt |= mtk_nand->fdm.reg_size << PAGEFMT_FDM_SHIFT;
> +	fmt |= mtk_nand->fdm.ecc_size << PAGEFMT_FDM_ECC_SHIFT;
> +	nfi_writew(nfc, fmt, NFI_PAGEFMT);
> +
> +	nfc->ecc_cfg.strength = chip->ecc.strength;
> +	nfc->ecc_cfg.len = chip->ecc.size + mtk_nand->fdm.ecc_size;
> +
> +	return 0;
> +}
> +
> +static void mtk_nfc_select_chip(struct mtd_info *mtd, int chip)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct mtk_nfc *nfc = nand_get_controller_data(nand);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(nand);
> +
> +	if (chip < 0)
> +		return;
> +
> +	mtk_nfc_hw_runtime_config(mtd);
> +
> +	nfi_writel(nfc, mtk_nand->sels[chip], NFI_CSEL);
> +}
> +
> +static int mtk_nfc_dev_ready(struct mtd_info *mtd)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
> +
> +	if (nfi_readl(nfc, NFI_STA) & STA_BUSY)
> +		return 0;
> +
> +	return 1;
> +}
> +
> +static void mtk_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
> +
> +	if (ctrl & NAND_ALE)
> +		mtk_nfc_send_address(nfc, dat);
> +	else if (ctrl & NAND_CLE) {
> +		mtk_nfc_hw_reset(nfc);
> +
> +		nfi_writew(nfc, CNFG_OP_CUST, NFI_CNFG);
> +		mtk_nfc_send_command(nfc, dat);
> +	}
> +}
> +
> +static inline void mtk_nfc_wait_ioready(struct mtk_nfc *nfc)
> +{
> +	int rc;
> +	u8 val;
> +
> +	rc = readb_poll_timeout_atomic(nfc->regs + NFI_PIO_DIRDY, val,
> +					val & PIO_DI_RDY, 10, MTK_TIMEOUT);
> +	if (rc < 0)
> +		dev_err(nfc->dev, "data not ready\n");
> +}
> +
> +static inline uint8_t mtk_nfc_read_byte(struct mtd_info *mtd)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	u32 reg;
> +
> +	/* after each byte read, the NFI_STA reg is reset by the hardware */
> +	reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
> +	if (reg != NFI_FSM_CUSTDATA) {
> +		reg = nfi_readw(nfc, NFI_CNFG);
> +		reg |= CNFG_BYTE_RW | CNFG_READ_EN;
> +		nfi_writew(nfc, reg, NFI_CNFG);
> +
> +		/* set to max sector to allow the HW to continue reading over
> +		 * unaligned accesses
> +		 */
> +		reg = (MTK_MAX_SECTOR << CON_SEC_SHIFT) | CON_BRD;
> +		nfi_writel(nfc, reg, NFI_CON);
> +
> +		/* trigger to fetch data */
> +		nfi_writew(nfc, STAR_EN, NFI_STRDATA);
> +	}
> +
> +	mtk_nfc_wait_ioready(nfc);
> +
> +	return nfi_readb(nfc, NFI_DATAR);
> +}
> +
> +static void mtk_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
> +{
> +	int i;
> +
> +	for (i = 0; i < len; i++)
> +		buf[i] = mtk_nfc_read_byte(mtd);
> +}
> +
> +static void mtk_nfc_write_byte(struct mtd_info *mtd, uint8_t byte)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(mtd_to_nand(mtd));
> +	u32 reg;
> +
> +	reg = nfi_readl(nfc, NFI_STA) & NFI_FSM_MASK;
> +
> +	if (reg != NFI_FSM_CUSTDATA) {
> +		reg = nfi_readw(nfc, NFI_CNFG) | CNFG_BYTE_RW;
> +		nfi_writew(nfc, reg, NFI_CNFG);
> +
> +		reg = MTK_MAX_SECTOR << CON_SEC_SHIFT | CON_BWR;
> +		nfi_writel(nfc, reg, NFI_CON);
> +
> +		nfi_writew(nfc, STAR_EN, NFI_STRDATA);
> +	}
> +
> +	mtk_nfc_wait_ioready(nfc);
> +	nfi_writeb(nfc, byte, NFI_DATAW);
> +}
> +
> +static void mtk_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
> +{
> +	int i;
> +
> +	for (i = 0; i < len; i++)
> +		mtk_nfc_write_byte(mtd, buf[i]);
> +}
> +
> +static int mtk_nfc_sector_encode(struct nand_chip *chip, u8 *data)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	int size = chip->ecc.size + mtk_nand->fdm.reg_size;
> +
> +	nfc->ecc_cfg.mode = ECC_DMA_MODE;
> +	nfc->ecc_cfg.op = ECC_ENCODE;
> +	return mtk_ecc_encode(nfc->ecc, &nfc->ecc_cfg, data, size);
> +}
> +
> +static void mtk_nfc_no_bad_mark_swap(struct mtd_info *a, uint8_t *b, int c)
> +{
> +	/* nop */
> +}
> +
> +static void mtk_nfc_bad_mark_swap(struct mtd_info *mtd, uint8_t *buf, int raw)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *nand = to_mtk_nand(chip);
> +	u32 bad_pos = nand->bad_mark.pos;
> +
> +	if (raw)
> +		bad_pos += nand->bad_mark.sec * mtk_data_len(chip);
> +	else
> +		bad_pos += nand->bad_mark.sec * chip->ecc.size;
> +
> +	swap(chip->oob_poi[0], buf[bad_pos]);
> +}
> +
> +static int mtk_nfc_format_subpage(struct mtd_info *mtd, uint32_t offset,
> +			uint32_t len, const uint8_t *buf)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	u32 start, end;
> +	int i, ret;
> +
> +	start = offset / chip->ecc.size;
> +	end = DIV_ROUND_UP(offset + len, chip->ecc.size);
> +
> +	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
> +	for (i = 0; i < chip->ecc.steps; i++) {
> +
> +		memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
> +			chip->ecc.size);
> +
> +		if (start > i || i >= end)
> +			continue;
> +
> +		if (i == mtk_nand->bad_mark.sec)
> +			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
> +
> +		memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
> +
> +		/* program the CRC back to the OOB */
> +		ret = mtk_nfc_sector_encode(chip, mtk_data_ptr(chip, i));
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_nfc_format_page(struct mtd_info *mtd, const uint8_t *buf)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	u32 i;
> +
> +	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
> +	for (i = 0; i < chip->ecc.steps; i++) {
> +		if (buf)
> +			memcpy(mtk_data_ptr(chip, i), data_ptr(chip, buf, i),
> +				chip->ecc.size);
> +
> +		if (i == mtk_nand->bad_mark.sec)
> +			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
> +
> +		memcpy(mtk_oob_ptr(chip, i), oob_ptr(chip, i), fdm->reg_size);
> +	}
> +}
> +
> +static inline void mtk_nfc_read_fdm(struct nand_chip *chip, u32 start,
> +					u32 sectors)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	u32 vall, valm;
> +	u8 *oobptr;
> +	int i, j;
> +
> +	for (i = 0; i < sectors; i++) {
> +		oobptr = oob_ptr(chip, start + i);
> +		vall = nfi_readl(nfc, NFI_FDML(i));
> +		valm = nfi_readl(nfc, NFI_FDMM(i));
> +
> +		for (j = 0; j < fdm->reg_size; j++)
> +			oobptr[j] = (j >= 4 ? valm : vall) >> ((j % 4) * 8);
> +	}
> +}
> +
> +static inline void mtk_nfc_write_fdm(struct nand_chip *chip)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	u32 vall, valm;
> +	u8 *oobptr;
> +	int i, j;
> +
> +	for (i = 0; i < chip->ecc.steps; i++) {
> +		oobptr = oob_ptr(chip, i);
> +		vall = 0;
> +		valm = 0;
> +		for (j = 0; j < 8; j++) {
> +			if (j < 4)
> +				vall |= (j < fdm->reg_size ? oobptr[j] : 0xff)
> +						<< (j * 8);
> +			else
> +				valm |= (j < fdm->reg_size ? oobptr[j] : 0xff)
> +						<< ((j - 4) * 8);
> +		}
> +		nfi_writel(nfc, vall, NFI_FDML(i));
> +		nfi_writel(nfc, valm, NFI_FDMM(i));
> +	}
> +}
> +
> +static int mtk_nfc_do_write_page(struct mtd_info *mtd, struct nand_chip *chip,
> +				const uint8_t *buf, int page, int len)
> +{
> +
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct device *dev = nfc->dev;
> +	dma_addr_t addr;
> +	u32 reg;
> +	int ret;
> +
> +	addr = dma_map_single(dev, (void *) buf, len, DMA_TO_DEVICE);
> +	ret = dma_mapping_error(nfc->dev, addr);
> +	if (ret) {
> +		dev_err(nfc->dev, "dma mapping error\n");
> +		return -EINVAL;
> +	}
> +
> +	reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AHB | CNFG_DMA_BURST_EN;
> +	nfi_writew(nfc, reg, NFI_CNFG);
> +
> +	nfi_writel(nfc, chip->ecc.steps << CON_SEC_SHIFT, NFI_CON);
> +	nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
> +	nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
> +
> +	init_completion(&nfc->done);
> +
> +	reg = nfi_readl(nfc, NFI_CON) | CON_BWR;
> +	nfi_writel(nfc, reg, NFI_CON);
> +	nfi_writew(nfc, STAR_EN, NFI_STRDATA);
> +
> +	ret = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
> +	if (!ret) {
> +		dev_err(dev, "program ahb done timeout\n");
> +		nfi_writew(nfc, 0, NFI_INTR_EN);
> +		ret = -ETIMEDOUT;
> +		goto timeout;
> +	}
> +
> +	ret = readl_poll_timeout_atomic(nfc->regs + NFI_ADDRCNTR, reg,
> +			(reg & CNTR_MASK) >= chip->ecc.steps, 10, MTK_TIMEOUT);
> +	if (ret)
> +		dev_err(dev, "hwecc write timeout\n");
> +
> +timeout:
> +
> +	dma_unmap_single(nfc->dev, addr, len, DMA_TO_DEVICE);
> +	nfi_writel(nfc, 0, NFI_CON);
> +
> +	return ret;
> +}
> +
> +static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
> +			const uint8_t *buf, int page, int raw)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	size_t len;
> +	const u8 *bufpoi;
> +	u32 reg;
> +	int ret;
> +
> +	if (!raw) {
> +		/* OOB => FDM: from register,  ECC: from HW */
> +		reg = nfi_readw(nfc, NFI_CNFG) | CNFG_AUTO_FMT_EN;
> +		nfi_writew(nfc, reg | CNFG_HW_ECC_EN, NFI_CNFG);
> +
> +		nfc->ecc_cfg.op = ECC_ENCODE;
> +		nfc->ecc_cfg.mode = ECC_NFI_MODE;
> +		ret = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
> +		if (ret) {
> +			/* clear NFI config */
> +			reg = nfi_readw(nfc, NFI_CNFG);
> +			reg &= ~(CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
> +			nfi_writew(nfc, reg, NFI_CNFG);
> +
> +			return ret;
> +		}
> +
> +		memcpy(nfc->buffer, buf, mtd->writesize);
> +		mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, raw);
> +		bufpoi = nfc->buffer;
> +
> +		/* write OOB into the FDM registers (OOB area in MTK NAND) */
> +		mtk_nfc_write_fdm(chip);
> +	} else
> +		bufpoi = buf;
> +
> +	len = mtd->writesize + (raw ? mtd->oobsize : 0);
> +	ret = mtk_nfc_do_write_page(mtd, chip, bufpoi, page, len);
> +
> +	if (!raw)
> +		mtk_ecc_disable(nfc->ecc);
> +
> +	return ret;
> +}
> +
> +static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
> +	struct nand_chip *chip, const uint8_t *buf, int oob_on, int page)
> +{
> +	return mtk_nfc_write_page(mtd, chip, buf, page, 0);
> +}
> +
> +static int mtk_nfc_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
> +				const uint8_t *buf, int oob_on, int pg)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +
> +	mtk_nfc_format_page(mtd, buf);
> +	return mtk_nfc_write_page(mtd, chip, nfc->buffer, pg, 1);
> +}
> +
> +static int mtk_nfc_write_subpage_hwecc(struct mtd_info *mtd,
> +		struct nand_chip *chip, uint32_t offset, uint32_t data_len,
> +		const uint8_t *buf, int oob_on, int page)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	int ret;
> +
> +	ret = mtk_nfc_format_subpage(mtd, offset, data_len, buf);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* use the data in the private buffer (now with FDM and CRC) */
> +	return mtk_nfc_write_page(mtd, chip, nfc->buffer, page, 1);
> +}
> +
> +static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
> +					int page)
> +{
> +	int ret;
> +
> +	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
> +
> +	ret = mtk_nfc_write_page_raw(mtd, chip, NULL, 1, page);
> +	if (ret < 0)
> +		return -EIO;
> +
> +	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
> +	ret = chip->waitfunc(mtd, chip);
> +
> +	return ret & NAND_STATUS_FAIL ? -EIO : 0;
> +}
> +
> +static int mtk_nfc_update_ecc_stats(struct mtd_info *mtd, u8 *buf, u32 sectors)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_ecc_stats stats;
> +	int rc, i;
> +
> +	rc = nfi_readl(nfc, NFI_STA) & STA_EMP_PAGE;
> +	if (rc) {
> +		memset(buf, 0xff, sectors * chip->ecc.size);
> +		for (i = 0; i < sectors; i++)
> +			memset(oob_ptr(chip, i), 0xff, mtk_nand->fdm.reg_size);
> +		return 0;
> +	}
> +
> +	mtk_ecc_get_stats(nfc->ecc, &stats, sectors);
> +	mtd->ecc_stats.corrected += stats.corrected;
> +	mtd->ecc_stats.failed += stats.failed;
> +
> +	return stats.bitflips;
> +}
> +
> +static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
> +		uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
> +		int page, int raw)
> +{
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	u32 spare = mtk_nand->spare_per_sector;
> +	u32 column, sectors, start, end, reg;
> +	dma_addr_t addr;
> +	int bitflips;
> +	size_t len;
> +	u8 *buf;
> +	int rc;
> +
> +	start = data_offs / chip->ecc.size;
> +	end = DIV_ROUND_UP(data_offs + readlen, chip->ecc.size);
> +
> +	sectors = end - start;
> +	column = start * (chip->ecc.size + spare);
> +
> +	len = sectors * chip->ecc.size + (raw ? sectors * spare : 0);
> +	buf = bufpoi + start * chip->ecc.size;
> +
> +	if (column != 0)
> +		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1);
> +
> +	addr = dma_map_single(nfc->dev, buf, len, DMA_FROM_DEVICE);
> +	rc = dma_mapping_error(nfc->dev, addr);
> +	if (rc) {
> +		dev_err(nfc->dev, "dma mapping error\n");
> +
> +		return -EINVAL;
> +	}
> +
> +	reg = nfi_readw(nfc, NFI_CNFG);
> +	reg |= CNFG_READ_EN | CNFG_DMA_BURST_EN | CNFG_AHB;
> +	if (!raw) {
> +		reg |= CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN;
> +		nfi_writew(nfc, reg, NFI_CNFG);
> +
> +		nfc->ecc_cfg.mode = ECC_NFI_MODE;
> +		nfc->ecc_cfg.sectors = sectors;
> +		nfc->ecc_cfg.op = ECC_DECODE;
> +		rc = mtk_ecc_enable(nfc->ecc, &nfc->ecc_cfg);
> +		if (rc) {
> +			dev_err(nfc->dev, "ecc enable\n");
> +			/* clear NFI_CNFG */
> +			reg &= ~(CNFG_DMA_BURST_EN | CNFG_AHB | CNFG_READ_EN |
> +				CNFG_AUTO_FMT_EN | CNFG_HW_ECC_EN);
> +			nfi_writew(nfc, reg, NFI_CNFG);
> +			dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
> +
> +			return rc;
> +		}
> +	} else
> +		nfi_writew(nfc, reg, NFI_CNFG);
> +
> +	nfi_writel(nfc, sectors << CON_SEC_SHIFT, NFI_CON);
> +	nfi_writew(nfc, INTR_AHB_DONE_EN, NFI_INTR_EN);
> +	nfi_writel(nfc, lower_32_bits(addr), NFI_STRADDR);
> +
> +	init_completion(&nfc->done);
> +	reg = nfi_readl(nfc, NFI_CON) | CON_BRD;
> +	nfi_writel(nfc, reg, NFI_CON);
> +	nfi_writew(nfc, STAR_EN, NFI_STRDATA);
> +
> +	rc = wait_for_completion_timeout(&nfc->done, msecs_to_jiffies(500));
> +	if (!rc)
> +		dev_warn(nfc->dev, "read ahb/dma done timeout\n");
> +
> +	rc = readl_poll_timeout_atomic(nfc->regs + NFI_BYTELEN, reg,
> +				(reg & CNTR_MASK) >= sectors, 10, MTK_TIMEOUT);
> +	if (rc < 0) {
> +		dev_err(nfc->dev, "subpage done timeout\n");
> +		bitflips = -EIO;
> +	} else {
> +		bitflips = 0;
> +		if (!raw) {
> +			rc = mtk_ecc_wait_done(nfc->ecc, ECC_DECODE);
> +			bitflips = rc < 0 ? -ETIMEDOUT :
> +				mtk_nfc_update_ecc_stats(mtd, buf, sectors);
> +			mtk_nfc_read_fdm(chip, start, sectors);
> +		}
> +	}
> +
> +	dma_unmap_single(nfc->dev, addr, len, DMA_FROM_DEVICE);
> +
> +	if (raw)
> +		goto done;
> +
> +	mtk_ecc_disable(nfc->ecc);
> +
> +	if (clamp(mtk_nand->bad_mark.sec, start, end) == mtk_nand->bad_mark.sec)
> +		mtk_nand->bad_mark.bm_swap(mtd, bufpoi, raw);
> +done:
> +	nfi_writel(nfc, 0, NFI_CON);
> +
> +	return bitflips;
> +}
> +
> +static int mtk_nfc_read_subpage_hwecc(struct mtd_info *mtd,
> +	struct nand_chip *chip, uint32_t off, uint32_t len, uint8_t *p, int pg)
> +{
> +	return mtk_nfc_read_subpage(mtd, chip, off, len, p, pg, 0);
> +}
> +
> +static int mtk_nfc_read_page_hwecc(struct mtd_info *mtd,
> +	struct nand_chip *chip, uint8_t *p, int oob_on, int pg)
> +{
> +	return mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, p, pg, 0);
> +}
> +
> +static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
> +				uint8_t *buf, int oob_on, int page)
> +{
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc *nfc = nand_get_controller_data(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	int i, ret;
> +
> +	memset(nfc->buffer, 0xff, mtd->writesize + mtd->oobsize);
> +	ret = mtk_nfc_read_subpage(mtd, chip, 0, mtd->writesize, nfc->buffer,
> +					page, 1);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0; i < chip->ecc.steps; i++) {
> +		memcpy(oob_ptr(chip, i), mtk_oob_ptr(chip, i), fdm->reg_size);
> +		if (i == mtk_nand->bad_mark.sec)
> +			mtk_nand->bad_mark.bm_swap(mtd, nfc->buffer, 1);
> +
> +		if (buf)
> +			memcpy(data_ptr(chip, buf, i), mtk_data_ptr(chip, i),
> +				chip->ecc.size);
> +	}
> +
> +	return ret;
> +}
> +
> +static int mtk_nfc_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
> +				int page)
> +{
> +	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
> +
> +	return mtk_nfc_read_page_raw(mtd, chip, NULL, 1, page);
> +}
> +
> +static inline void mtk_nfc_hw_init(struct mtk_nfc *nfc)
> +{
> +	/* ACCON: access timing control register
> +	 * -------------------------------------
> +	 * 31:28: minimum required time for CS post pulling down after accessing
> +	 *	the device
> +	 * 27:22: minimum required time for CS pre pulling down before accessing
> +	 *	the device
> +	 * 21:16: minimum required time from NCEB low to NREB low
> +	 * 15:12: minimum required time from NWEB high to NREB low.
> +	 * 11:08: write enable hold time
> +	 * 07:04: write wait states
> +	 * 03:00: read wait states
> +	 */
> +	nfi_writel(nfc, 0x10804211, NFI_ACCCON);
> +
> +	/* CNRNB: nand ready/busy register
> +	 * -------------------------------
> +	 * 7:4: timeout register for polling the NAND busy/ready signal
> +	 * 0  : poll the status of the busy/ready signal after [7:4]*16 cycles.
> +	 */
> +	nfi_writew(nfc, 0xf1, NFI_CNRNB);
> +	nfi_writew(nfc, PAGEFMT_8K_16K, NFI_PAGEFMT);
> +
> +	mtk_nfc_hw_reset(nfc);
> +
> +	nfi_readl(nfc, NFI_INTR_STA);
> +	nfi_writel(nfc, 0, NFI_INTR_EN);
> +}
> +
> +static irqreturn_t mtk_nfc_irq(int irq, void *id)
> +{
> +	struct mtk_nfc *nfc = id;
> +	u16 sta, ien;
> +
> +	sta = nfi_readw(nfc, NFI_INTR_STA);
> +	ien = nfi_readw(nfc, NFI_INTR_EN);
> +
> +	if (!(sta & ien))
> +		return IRQ_NONE;
> +
> +	nfi_writew(nfc, ~sta & ien, NFI_INTR_EN);
> +	complete(&nfc->done);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int mtk_nfc_enable_clk(struct device *dev, struct mtk_nfc_clk *clk)
> +{
> +	int ret;
> +
> +	ret = clk_prepare_enable(clk->nfi_clk);
> +	if (ret) {
> +		dev_err(dev, "failed to enable nfi clk\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(clk->pad_clk);
> +	if (ret) {
> +		dev_err(dev, "failed to enable pad clk\n");
> +		clk_disable_unprepare(clk->nfi_clk);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mtk_nfc_disable_clk(struct mtk_nfc_clk *clk)
> +{
> +	clk_disable_unprepare(clk->nfi_clk);
> +	clk_disable_unprepare(clk->pad_clk);
> +}
> +
> +static int mtk_nfc_ooblayout_free(struct mtd_info *mtd, int section,
> +				struct mtd_oob_region *oob_region)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	struct mtk_nfc_fdm *fdm = &mtk_nand->fdm;
> +	u32 eccsteps;
> +
> +	eccsteps = mtd->writesize / chip->ecc.size;
> +
> +	if (section >= eccsteps)
> +		return -ERANGE;
> +
> +	oob_region->length = fdm->reg_size - fdm->ecc_size;
> +	oob_region->offset = section * fdm->reg_size + fdm->ecc_size;
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_ooblayout_ecc(struct mtd_info *mtd, int section,
> +				struct mtd_oob_region *oob_region)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *mtk_nand = to_mtk_nand(chip);
> +	u32 eccsteps;
> +
> +	if (section)
> +		return -ERANGE;
> +
> +	eccsteps = mtd->writesize / chip->ecc.size;
> +	oob_region->offset = mtk_nand->fdm.reg_size * eccsteps;
> +	oob_region->length = mtd->oobsize - oob_region->offset;
> +
> +	return 0;
> +}
> +
> +static const struct mtd_ooblayout_ops mtk_nfc_ooblayout_ops = {
> +	.free = mtk_nfc_ooblayout_free,
> +	.ecc = mtk_nfc_ooblayout_ecc,
> +};
> +
> +static void mtk_nfc_set_fdm(struct mtk_nfc_fdm *fdm, struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	struct mtk_nfc_nand_chip *chip = to_mtk_nand(nand);
> +	u32 ecc_bytes;
> +
> +	ecc_bytes = DIV_ROUND_UP(nand->ecc.strength * ECC_PARITY_BITS, 8);
> +
> +	fdm->reg_size = chip->spare_per_sector - ecc_bytes;
> +	if (fdm->reg_size > NFI_FDM_MAX_SIZE)
> +		fdm->reg_size = NFI_FDM_MAX_SIZE;
> +
> +	/* bad block mark storage */
> +	fdm->ecc_size = 1;
> +}
> +
> +static void mtk_nfc_set_bad_mark_ctl(struct mtk_nfc_bad_mark_ctl *bm_ctl,
> +				struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +
> +	if (mtd->writesize == 512)
> +		bm_ctl->bm_swap = mtk_nfc_no_bad_mark_swap;
> +	else {
> +		bm_ctl->bm_swap = mtk_nfc_bad_mark_swap;
> +		bm_ctl->sec = mtd->writesize / mtk_data_len(nand);
> +		bm_ctl->pos = mtd->writesize % mtk_data_len(nand);
> +	}
> +}
> +
> +static void mtk_nfc_set_spare_per_sector(u32 *sps, struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 spare[] = {16, 26, 27, 28, 32, 36, 40, 44,
> +			48, 49, 50, 51, 52, 62, 63, 64};
> +	u32 eccsteps, i;
> +
> +	eccsteps = mtd->writesize / nand->ecc.size;
> +	*sps = mtd->oobsize / eccsteps;
> +
> +	if (nand->ecc.size == 1024)
> +		*sps >>= 1;
> +
> +	for (i = 0; i < ARRAY_SIZE(spare); i++) {
> +		if (*sps <= spare[i]) {
> +			if (!i)
> +				*sps = spare[i];
> +			else if (*sps != spare[i])
> +				*sps = spare[i - 1];
> +			break;
> +		}
> +	}
> +
> +	if (i >= ARRAY_SIZE(spare))
> +		*sps = spare[ARRAY_SIZE(spare) - 1];
> +
> +	if (nand->ecc.size == 1024)
> +		*sps <<= 1;
> +}
> +
> +static int mtk_nfc_ecc_init(struct device *dev, struct mtd_info *mtd)
> +{
> +	struct nand_chip *nand = mtd_to_nand(mtd);
> +	u32 spare;
> +	int free;
> +
> +	/* support only ecc hw mode */
> +	if (nand->ecc.mode != NAND_ECC_HW) {
> +		dev_err(dev, "ecc.mode not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	/* if optional dt settings not present */
> +	if (!nand->ecc.size || !nand->ecc.strength) {
> +
> +		/* use datasheet requirements */
> +		nand->ecc.strength = nand->ecc_strength_ds;
> +		nand->ecc.size = nand->ecc_step_ds;
> +
> +		/* align eccstrength and eccsize
> +		 * this controller only supports 512 and 1024 sizes
> +		 */
> +		if (nand->ecc.size < 1024) {
> +			if (mtd->writesize > 512) {
> +				nand->ecc.size = 1024;
> +				nand->ecc.strength <<= 1;
> +			} else
> +				nand->ecc.size = 512;
> +		} else
> +			nand->ecc.size = 1024;
> +
> +		mtk_nfc_set_spare_per_sector(&spare, mtd);
> +
> +		/* calculate oob bytes except ecc parity data */
> +		free = ((nand->ecc.strength * ECC_PARITY_BITS) + 7) >> 3;
> +		free = spare - free;
> +
> +		/* enhance ecc strength if oob left is bigger than max FDM size
> +		 * or reduce ecc strength if oob size is not enough for ecc
> +		 * parity data.
> +		 */
> +		if (free > NFI_FDM_MAX_SIZE) {
> +			spare -= NFI_FDM_MAX_SIZE;
> +			nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS;
> +		} else if (free < 0) {
> +			spare -= NFI_FDM_MIN_SIZE;
> +			nand->ecc.strength = (spare << 3) / ECC_PARITY_BITS;
> +		}
> +	}
> +
> +	mtk_ecc_adjust_strength(&nand->ecc.strength);
> +
> +	dev_info(dev, "eccsize %d eccstrength %d\n",
> +		nand->ecc.size, nand->ecc.strength);
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
> +				struct device_node *np)
> +{
> +	struct mtk_nfc_nand_chip *chip;
> +	struct nand_chip *nand;
> +	struct mtd_info *mtd;
> +	int nsels, len;
> +	u32 tmp;
> +	int ret;
> +	int i;
> +
> +	if (!of_get_property(np, "reg", &nsels))
> +		return -ENODEV;
> +
> +	nsels /= sizeof(u32);
> +	if (!nsels || nsels > MTK_NAND_MAX_NSELS) {
> +		dev_err(dev, "invalid reg property size %d\n", nsels);
> +		return -EINVAL;
> +	}
> +
> +	chip = devm_kzalloc(dev,
> +			sizeof(*chip) + nsels * sizeof(u8), GFP_KERNEL);
> +	if (!chip)
> +		return -ENOMEM;
> +
> +	chip->nsels = nsels;
> +	for (i = 0; i < nsels; i++) {
> +		ret = of_property_read_u32_index(np, "reg", i, &tmp);
> +		if (ret) {
> +			dev_err(dev, "reg property failure : %d\n", ret);
> +			return ret;
> +		}
> +		chip->sels[i] = tmp;
> +	}
> +
> +	nand = &chip->nand;
> +	nand->controller = &nfc->controller;
> +
> +	nand_set_flash_node(nand, np);
> +	nand_set_controller_data(nand, nfc);
> +
> +	nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
> +	nand->dev_ready = mtk_nfc_dev_ready;
> +	nand->select_chip = mtk_nfc_select_chip;
> +	nand->write_byte = mtk_nfc_write_byte;
> +	nand->write_buf = mtk_nfc_write_buf;
> +	nand->read_byte = mtk_nfc_read_byte;
> +	nand->read_buf = mtk_nfc_read_buf;
> +	nand->cmd_ctrl = mtk_nfc_cmd_ctrl;
> +
> +	/* set default mode in case dt entry is missing */
> +	nand->ecc.mode = NAND_ECC_HW;
> +
> +	nand->ecc.write_subpage = mtk_nfc_write_subpage_hwecc;
> +	nand->ecc.write_page_raw = mtk_nfc_write_page_raw;
> +	nand->ecc.write_page = mtk_nfc_write_page_hwecc;
> +	nand->ecc.write_oob_raw = mtk_nfc_write_oob_std;
> +	nand->ecc.write_oob = mtk_nfc_write_oob_std;
> +
> +	nand->ecc.read_subpage = mtk_nfc_read_subpage_hwecc;
> +	nand->ecc.read_page_raw = mtk_nfc_read_page_raw;
> +	nand->ecc.read_page = mtk_nfc_read_page_hwecc;
> +	nand->ecc.read_oob_raw = mtk_nfc_read_oob_std;
> +	nand->ecc.read_oob = mtk_nfc_read_oob_std;
> +
> +	mtd = nand_to_mtd(nand);
> +	mtd->owner = THIS_MODULE;
> +	mtd->dev.parent = dev;
> +	mtd->name = MTK_NAME;
> +	mtd_set_ooblayout(mtd, &mtk_nfc_ooblayout_ops);
> +
> +	mtk_nfc_hw_init(nfc);
> +
> +	ret = nand_scan_ident(mtd, nsels, NULL);
> +	if (ret)
> +		return -ENODEV;
> +
> +	/* store bbt magic in page, cause OOB is not protected */
> +	if (nand->bbt_options & NAND_BBT_USE_FLASH)
> +		nand->bbt_options |= NAND_BBT_NO_OOB;
> +
> +	ret = mtk_nfc_ecc_init(dev, mtd);
> +	if (ret)
> +		return -EINVAL;
> +
> +	if (nand->options & NAND_BUSWIDTH_16) {
> +		dev_err(dev, "16bits buswidth not supported");
> +		return -EINVAL;
> +	}
> +
> +	mtk_nfc_set_spare_per_sector(&chip->spare_per_sector, mtd);
> +	mtk_nfc_set_fdm(&chip->fdm, mtd);
> +	mtk_nfc_set_bad_mark_ctl(&chip->bad_mark, mtd);
> +
> +	len = mtd->writesize + mtd->oobsize;
> +	nfc->buffer = devm_kzalloc(dev, len, GFP_KERNEL);
> +	if (!nfc->buffer)
> +		return  -ENOMEM;
> +
> +	ret = nand_scan_tail(mtd);
> +	if (ret)
> +		return -ENODEV;
> +
> +	ret = mtd_device_parse_register(mtd, NULL, NULL, NULL, 0);
> +	if (ret) {
> +		dev_err(dev, "mtd parse partition error\n");
> +		nand_release(mtd);
> +		return ret;
> +	}
> +
> +	list_add_tail(&chip->node, &nfc->chips);
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_nand_chips_init(struct device *dev, struct mtk_nfc *nfc)
> +{
> +	struct device_node *np = dev->of_node;
> +	struct device_node *nand_np;
> +	int ret;
> +
> +	for_each_child_of_node(np, nand_np) {
> +		ret = mtk_nfc_nand_chip_init(dev, nfc, nand_np);
> +		if (ret) {
> +			of_node_put(nand_np);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct mtk_nfc *nfc;
> +	struct resource *res;
> +	int ret, irq;
> +
> +	nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
> +	if (!nfc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&nfc->controller.lock);
> +	init_waitqueue_head(&nfc->controller.wq);
> +	INIT_LIST_HEAD(&nfc->chips);
> +
> +	/* probe defer if not ready */
> +	nfc->ecc = of_mtk_ecc_get(np);
> +	if (IS_ERR(nfc->ecc))
> +		return PTR_ERR(nfc->ecc);
> +	else if (!nfc->ecc)
> +		return -ENODEV;
> +
> +	nfc->dev = dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	nfc->regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(nfc->regs)) {
> +		ret = PTR_ERR(nfc->regs);
> +		dev_err(dev, "no nfi base\n");
> +		goto release_ecc;
> +	}
> +
> +	nfc->clk.nfi_clk = devm_clk_get(dev, "nfi_clk");
> +	if (IS_ERR(nfc->clk.nfi_clk)) {
> +		dev_err(dev, "no clk\n");
> +		ret = PTR_ERR(nfc->clk.nfi_clk);
> +		goto release_ecc;
> +	}
> +
> +	nfc->clk.pad_clk = devm_clk_get(dev, "pad_clk");
> +	if (IS_ERR(nfc->clk.pad_clk)) {
> +		dev_err(dev, "no pad clk\n");
> +		ret = PTR_ERR(nfc->clk.pad_clk);
> +		goto release_ecc;
> +	}
> +
> +	ret = mtk_nfc_enable_clk(dev, &nfc->clk);
> +	if (ret)
> +		goto release_ecc;
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq < 0) {
> +		dev_err(dev, "no nfi irq resource\n");
> +		ret = -EINVAL;
> +		goto clk_disable;
> +	}
> +
> +	ret = devm_request_irq(dev, irq, mtk_nfc_irq, 0x0, "mtk-nand", nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to request nfi irq\n");
> +		goto clk_disable;
> +	}
> +
> +	ret = dma_set_mask(dev, DMA_BIT_MASK(32));
> +	if (ret) {
> +		dev_err(dev, "failed to set dma mask\n");
> +		goto clk_disable;
> +	}
> +
> +	platform_set_drvdata(pdev, nfc);
> +
> +	ret = mtk_nfc_nand_chips_init(dev, nfc);
> +	if (ret) {
> +		dev_err(dev, "failed to init nand chips\n");
> +		goto clk_disable;
> +	}
> +
> +	return 0;
> +
> +clk_disable:
> +	mtk_nfc_disable_clk(&nfc->clk);
> +
> +release_ecc:
> +	mtk_ecc_release(nfc->ecc);
> +
> +	return ret;
> +}
> +
> +static int mtk_nfc_remove(struct platform_device *pdev)
> +{
> +	struct mtk_nfc *nfc = platform_get_drvdata(pdev);
> +	struct mtk_nfc_nand_chip *chip;
> +
> +	while (!list_empty(&nfc->chips)) {
> +		chip = list_first_entry(&nfc->chips, struct mtk_nfc_nand_chip,
> +					node);
> +		nand_release(nand_to_mtd(&chip->nand));
> +		list_del(&chip->node);
> +	}
> +
> +	mtk_ecc_release(nfc->ecc);
> +	mtk_nfc_disable_clk(&nfc->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_nfc_suspend(struct device *dev)
> +{
> +	struct mtk_nfc *nfc = dev_get_drvdata(dev);
> +
> +	mtk_nfc_disable_clk(&nfc->clk);
> +
> +	return 0;
> +}
> +
> +static int mtk_nfc_resume(struct device *dev)
> +{
> +	struct mtk_nfc *nfc = dev_get_drvdata(dev);
> +	struct mtk_nfc_nand_chip *chip;
> +	struct nand_chip *nand;
> +	struct mtd_info *mtd;
> +	int ret;
> +	u32 i;
> +
> +	udelay(200);
> +
> +	ret = mtk_nfc_enable_clk(dev, &nfc->clk);
> +	if (ret)
> +		return ret;
> +
> +	mtk_nfc_hw_init(nfc);
> +
> +	/* reset NAND chip if VCC was powered off */
> +	list_for_each_entry(chip, &nfc->chips, node) {
> +		nand = &chip->nand;
> +		mtd = nand_to_mtd(nand);
> +		for (i = 0; i < chip->nsels; i++) {
> +			nand->select_chip(mtd, i);
> +			nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
> +		}
> +	}
> +
> +	return 0;
> +}
> +static SIMPLE_DEV_PM_OPS(mtk_nfc_pm_ops, mtk_nfc_suspend, mtk_nfc_resume);
> +#endif
> +
> +static const struct of_device_id mtk_nfc_id_table[] = {
> +	{ .compatible = "mediatek,mt2701-nfc" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, mtk_nfc_id_table);
> +
> +static struct platform_driver mtk_nfc_driver = {
> +	.probe  = mtk_nfc_probe,
> +	.remove = mtk_nfc_remove,
> +	.driver = {
> +		.name  = MTK_NAME,
> +		.of_match_table = mtk_nfc_id_table,
> +#ifdef CONFIG_PM_SLEEP
> +		.pm = &mtk_nfc_pm_ops,
> +#endif
> +	},
> +};
> +
> +module_platform_driver(mtk_nfc_driver);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Xiaolei Li <xiaolei.li at mediatek.com>");
> +MODULE_DESCRIPTION("MTK Nand Flash Controller Driver");



-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com


From jorge.ramirez-ortiz at linaro.org  Tue Jun 14 10:35:25 2016
From: jorge.ramirez-ortiz at linaro.org (Jorge Ramirez)
Date: Tue, 14 Jun 2016 13:35:25 -0400
Subject: [PATCH v5 2/2] mtd: mediatek: driver for MTK Smart Device
In-Reply-To: <20160614184433.2b399c8b@bbrezillon>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
 <1465919451-1891-3-git-send-email-jorge.ramirez-ortiz@linaro.org>
 <20160614184433.2b399c8b@bbrezillon>
Message-ID: <5760405D.3090101@linaro.org>

On 06/14/2016 12:44 PM, Boris Brezillon wrote:
> Apart from a few coding style issues that I can fix when applying the
> patch it looks good to me.

thanks Boris for the all the reviews.
and apologies for having taking this long for posting v5 (the US 
immigration system has been fun to deal with..actually there are not 9 
but 10 circles of hell)

> If I understand correctly, you don't have access to the hardware
> anymore. Xiolei (or anyone else owning the proper hardware), can you add
> your Tested-by?
>
> Thanks,
>
> Boris



From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:15 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:15 +0800
Subject: Add MediaTek USB3 DRD Driver
Message-ID: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>

>From 48552e96e4e33f8830cb6a59154fe148425532fd Mon Sep 17 00:00:00 2001
From: Chunfeng Yun <chunfeng.yun at mediatek.com>
Date: Wed, 15 Jun 2016 10:58:10 +0800
Subject: [PATCH v4,0/5] Add MediaTek USB3 DRD Driver

These patches introduce the MediaTek USB3 dual-role controller
driver.

The driver can be configured as Dual-Role Device (DRD),
Peripheral Only and Host Only (xHCI) modes. It works well
with Mass Storage, RNDIS and g_zero on FS/HS and SS. And it is
tested on MT8173 platform which only contains USB2.0 device IP,
and on MT6290 platform which contains USB3.0 device IP.

Change in v4:
1. fix build errors on non-mediatek platforms
2. provide manual dual-role switch via debugfs instead of sysfs

Change in v3:
1. fix some typo error
2. rename mtu3.txt to mt8173-mtu3.txt

Change in v2:
1. modify binding docs according to suggestions
2. modify some comments and remove some dummy blank lines
3. fix memory leakage

Chunfeng Yun (5):
  dt-bindings: mt8173-xhci: support host side of dual-role mode
  dt-bindings: mt8173-mtu3: add devicetree bindings
  usb: xhci-mtk: make IPPC register optional
  usb: Add MediaTek USB3 DRD Driver
  arm64: dts: mediatek: add USB3 DRD driver

 .../devicetree/bindings/usb/mt8173-mtu3.txt        |   86 ++
 .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts        |   46 +-
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |   28 +-
 drivers/usb/Kconfig                                |    2 +
 drivers/usb/Makefile                               |    1 +
 drivers/usb/host/xhci-mtk.c                        |   32 +-
 drivers/usb/mtu3/Kconfig                           |   48 ++
 drivers/usb/mtu3/Makefile                          |   21 +
 drivers/usb/mtu3/mtu3.h                            |  424 ++++++++++
 drivers/usb/mtu3/mtu3_core.c                       |  879 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_dr.c                         |  375 +++++++++
 drivers/usb/mtu3/mtu3_dr.h                         |  108 +++
 drivers/usb/mtu3/mtu3_gadget.c                     |  731 ++++++++++++++++
 drivers/usb/mtu3/mtu3_gadget_ep0.c                 |  879 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_host.c                       |  294 +++++++
 drivers/usb/mtu3/mtu3_hw_regs.h                    |  474 +++++++++++
 drivers/usb/mtu3/mtu3_plat.c                       |  490 +++++++++++
 drivers/usb/mtu3/mtu3_qmu.c                        |  599 +++++++++++++
 drivers/usb/mtu3/mtu3_qmu.h                        |   43 +
 20 files changed, 5594 insertions(+), 14 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
 create mode 100644 drivers/usb/mtu3/Kconfig
 create mode 100644 drivers/usb/mtu3/Makefile
 create mode 100644 drivers/usb/mtu3/mtu3.h
 create mode 100644 drivers/usb/mtu3/mtu3_core.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.h
 create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
 create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
 create mode 100644 drivers/usb/mtu3/mtu3_host.c
 create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
 create mode 100644 drivers/usb/mtu3/mtu3_plat.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.h

--
1.7.9.5




From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:16 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:16 +0800
Subject: [PATCH v4,
 1/5] dt-bindings: mt8173-xhci: support host side of dual-role mode
In-Reply-To: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465960040-768-2-git-send-email-chunfeng.yun@mediatek.com>

Some resources, such as IPPC register etc, shared with device
driver are moved into common glue layer when xHCI driver is the
host side of dual-role mode and they should be changed as optional
properties if they are required ones before. For clarity, add
a new part of binding to support host side of dual-role mode.

Additionally add optional properties of pinctrl for host only mode

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
Acked-by: Rob Herring <robh at kernel.org>
---
 .../devicetree/bindings/usb/mt8173-xhci.txt        |   48 ++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
index b3a7ffa..bec2dff 100644
--- a/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/mt8173-xhci.txt
@@ -2,6 +2,14 @@ MT8173 xHCI
 
 The device node for Mediatek SOC USB3.0 host controller
 
+There are two scenarios: the first one only supports xHCI driver;
+the second one supports dual-role mode, and the host is based on xHCI
+driver. Take account of backward compatibility, we divide bindings
+into two parts.
+
+1st: only supports xHCI driver
+------------------------------------------------------------------------
+
 Required properties:
  - compatible : should contain "mediatek,mt8173-xhci"
  - reg : specifies physical base address and size of the registers,
@@ -27,6 +35,9 @@ Optional properties:
 	control register, it depends on "mediatek,wakeup-src".
  - vbus-supply : reference to the VBUS regulator;
  - usb3-lpm-capable : supports USB3.0 LPM
+ - pinctrl-names : a pinctrl state named "default" must be defined
+ - pinctrl-0 : pin control group
+	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
 
 Example:
 usb30: usb at 11270000 {
@@ -49,3 +60,40 @@ usb30: usb at 11270000 {
 	mediatek,syscon-wakeup = <&pericfg>;
 	mediatek,wakeup-src = <1>;
 };
+
+2nd: dual-role mode with xHCI driver
+------------------------------------------------------------------------
+
+In the case, xhci is added as subnode to mtu3. An example and the DT binding
+details of mtu3 can be found in:
+Documentation/devicetree/bindings/usb/mtu3.txt
+
+Required properties:
+ - compatible : should contain "mediatek,mt8173-xhci"
+ - reg : specifies physical base address and size of the registers,
+	should be only for xHCI IP registers
+ - interrupts : interrupt used by the host controller
+ - power-domains : a phandle to USB power domain node to control USB's
+	mtcmos
+ - vusb33-supply : regulator of USB avdd3.3v
+
+ - clocks : a list of phandle + clock-specifier pairs, one for each
+	entry in clock-names
+ - clock-names : must be
+	"sys_ck": for clock of xHCI MAC
+
+Optional properties:
+ - vbus-supply : reference to the VBUS regulator;
+ - usb3-lpm-capable : supports USB3.0 LPM
+
+Example:
+usb30: usb at 11270000 {
+	compatible = "mediatek,mt8173-xhci";
+	reg = <0 0x11270000 0 0x1000>;
+	interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+	clocks = <&topckgen CLK_TOP_USB30_SEL>;
+	clock-names = "sys_ck";
+	vusb33-supply = <&mt6397_vusb_reg>;
+	usb3-lpm-capable;
+};
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:17 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:17 +0800
Subject: [PATCH v4,2/5] dt-bindings: mt8173-mtu3: add devicetree bindings
In-Reply-To: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465960040-768-3-git-send-email-chunfeng.yun@mediatek.com>

add a DT binding doc for MediaTek USB3 DRD driver

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 .../devicetree/bindings/usb/mt8173-mtu3.txt        |   86 ++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt

diff --git a/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
new file mode 100644
index 0000000..12f3c2a
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/mt8173-mtu3.txt
@@ -0,0 +1,86 @@
+The device node for Mediatek USB3.0 DRD controller
+
+Required properties:
+ - compatible : should be "mediatek,mt8173-mtu3"
+ - reg : specifies physical base address and size of the registers
+ - reg-names: should be "mac" for device IP and "ippc" for IP port control
+ - interrupts : interrupt used by the device IP
+ - power-domains : a phandle to USB power domain node to control USB's
+	mtcmos
+ - vusb33-supply : regulator of USB avdd3.3v
+ - clocks : a list of phandle + clock-specifier pairs, one for each
+	entry in clock-names
+ - clock-names : must contain "sys_ck" for clock of controller;
+	"wakeup_deb_p0" and "wakeup_deb_p1" are optional, they are
+	depends on "mediatek,enable-wakeup"
+ - phys : a list of phandle + phy specifier pairs
+ - dr_mode : should be one of "host", "peripheral" or "otg",
+	refer to usb/generic.txt
+
+Optional properties:
+ - #address-cells, #size-cells : should be '2' if the device has sub-nodes
+	with 'reg' property
+ - ranges : allows valid 1:1 translation between child's address space and
+	parent's address space
+ - extcon : external connector for vbus and idpin changes detection, needed
+	when supports dual-role mode.
+ - vbus-supply : reference to the VBUS regulator, needed when supports
+	dual-role mode.
+ - pinctl-names : a pinctrl state named "default" must be defined,
+	"id_float" and "id_ground" are optinal which depends on
+	"mediatek,enable-manual-drd"
+ - pinctrl-0 : pin control group
+	See: Documentation/devicetree/bindings/pinctrl/pinctrl-binding.txt
+
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+	"full-speed"; refer to usb/generic.txt
+ - enable-manual-drd : supports manual dual-role switch via debugfs; usually
+	used when receptacle is TYPE-A and also wants to support dual-role
+	mode.
+ - mediatek,enable-wakeup : supports ip sleep wakeup used by host mode
+ - mediatek,syscon-wakeup : phandle to syscon used to access USB wakeup
+	control register, it depends on "mediatek,enable-wakeup".
+
+Sub-nodes:
+The xhci should be added as subnode to mtu3 as shown in the following example
+if host mode is enabled. The DT binding details of xhci can be found in:
+Documentation/devicetree/bindings/usb/mt8173-xhci.txt
+
+Example:
+ssusb: usb at 11271000 {
+	compatible = "mediatek,mt8173-mtu3";
+	reg = <0 0x11271000 0 0x3000>,
+	      <0 0x11280700 0 0x0100>;
+	reg-names = "mac", "ippc";
+	interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
+	phys = <&phy_port0 PHY_TYPE_USB3>,
+	       <&phy_port1 PHY_TYPE_USB2>;
+	power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+	clocks = <&topckgen CLK_TOP_USB30_SEL>,
+		 <&pericfg CLK_PERI_USB0>,
+		 <&pericfg CLK_PERI_USB1>;
+	clock-names = "sys_ck",
+		      "wakeup_deb_p0",
+		      "wakeup_deb_p1";
+	vusb33-supply = <&mt6397_vusb_reg>;
+	vbus-supply = <&usb_p0_vbus>;
+	extcon = <&extcon_usb>;
+	dr_mode = "otg";
+	mediatek,enable-wakeup;
+	mediatek,syscon-wakeup = <&pericfg>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+	ranges;
+	status = "disabled";
+
+	usb_host: xhci at 11270000 {
+		compatible = "mediatek,mt8173-xhci";
+		reg = <0 0x11270000 0 0x1000>;
+		interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+		power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+		clocks = <&topckgen CLK_TOP_USB30_SEL>;
+		clock-names = "sys_ck";
+		vusb33-supply = <&mt6397_vusb_reg>;
+		status = "disabled";
+	};
+};
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:18 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:18 +0800
Subject: [PATCH v4,3/5] usb: xhci-mtk: make IPPC register optional
In-Reply-To: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465960040-768-4-git-send-email-chunfeng.yun@mediatek.com>

Make IPPC register optional to support host side of dual-role mode,
due to it is moved into common glue layer for simplification.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/host/xhci-mtk.c |   32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c
index 79959f1..dc86832 100644
--- a/drivers/usb/host/xhci-mtk.c
+++ b/drivers/usb/host/xhci-mtk.c
@@ -94,6 +94,9 @@ static int xhci_mtk_host_enable(struct xhci_hcd_mtk *mtk)
 	int ret;
 	int i;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* power on host ip */
 	value = readl(&ippc->ip_pw_ctr1);
 	value &= ~CTRL1_IP_HOST_PDN;
@@ -139,6 +142,9 @@ static int xhci_mtk_host_disable(struct xhci_hcd_mtk *mtk)
 	int ret;
 	int i;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* power down all u3 ports */
 	for (i = 0; i < mtk->num_u3_ports; i++) {
 		value = readl(&ippc->u3_ctrl_p[i]);
@@ -173,6 +179,9 @@ static int xhci_mtk_ssusb_config(struct xhci_hcd_mtk *mtk)
 	struct mu3c_ippc_regs __iomem *ippc = mtk->ippc_regs;
 	u32 value;
 
+	if (ippc == NULL)
+		return 0;
+
 	/* reset whole ip */
 	value = readl(&ippc->ip_pw_ctr0);
 	value |= CTRL0_IP_SW_RST;
@@ -475,6 +484,7 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci)
 /* called during probe() after chip reset completes */
 static int xhci_mtk_setup(struct usb_hcd *hcd)
 {
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 	struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd);
 	int ret;
 
@@ -482,12 +492,21 @@ static int xhci_mtk_setup(struct usb_hcd *hcd)
 		ret = xhci_mtk_ssusb_config(mtk);
 		if (ret)
 			return ret;
+	}
+
+	ret = xhci_gen_setup(hcd, xhci_mtk_quirks);
+	if (ret)
+		return ret;
+
+	if (usb_hcd_is_primary_hcd(hcd)) {
+		mtk->num_u3_ports = xhci->num_usb3_ports;
+		mtk->num_u2_ports = xhci->num_usb2_ports;
 		ret = xhci_mtk_sch_init(mtk);
 		if (ret)
 			return ret;
 	}
 
-	return xhci_gen_setup(hcd, xhci_mtk_quirks);
+	return ret;
 }
 
 static int xhci_mtk_probe(struct platform_device *pdev)
@@ -595,11 +614,14 @@ static int xhci_mtk_probe(struct platform_device *pdev)
 	hcd->rsrc_start = res->start;
 	hcd->rsrc_len = resource_size(res);
 
+	mtk->ippc_regs = NULL;
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-	mtk->ippc_regs = devm_ioremap_resource(dev, res);
-	if (IS_ERR(mtk->ippc_regs)) {
-		ret = PTR_ERR(mtk->ippc_regs);
-		goto put_usb2_hcd;
+	if (res) {	/* ippc register is optional */
+		mtk->ippc_regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(mtk->ippc_regs)) {
+			ret = PTR_ERR(mtk->ippc_regs);
+			goto put_usb2_hcd;
+		}
 	}
 
 	for (phy_num = 0; phy_num < mtk->num_phys; phy_num++) {
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:20 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:20 +0800
Subject: [PATCH v4,5/5] arm64: dts: mediatek: add USB3 DRD driver
In-Reply-To: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465960040-768-6-git-send-email-chunfeng.yun@mediatek.com>

USB3 DRD driver is added for MT8173-EVB, and xHCI driver
becomes its subnode

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8173-evb.dts |   46 +++++++++++++++++++++++++--
 arch/arm64/boot/dts/mediatek/mt8173.dtsi    |   28 ++++++++++++----
 2 files changed, 65 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
index 7453a47..682dfd7 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8173-evb.dts
@@ -34,6 +34,11 @@
 
 	chosen { };
 
+	extcon_usb: extcon_iddig {
+		compatible = "linux,extcon-usb-gpio";
+		id-gpio = <&pio 16 GPIO_ACTIVE_HIGH>;
+	};
+
 	usb_p1_vbus: regulator at 0 {
 		compatible = "regulator-fixed";
 		regulator-name = "usb_vbus";
@@ -42,6 +47,16 @@
 		gpio = <&pio 130 GPIO_ACTIVE_HIGH>;
 		enable-active-high;
 	};
+
+	usb_p0_vbus: regulator at 1 {
+		compatible = "regulator-fixed";
+		regulator-name = "vbus";
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+		gpio = <&pio 9 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
+
 };
 
 &i2c1 {
@@ -205,6 +220,20 @@
 			bias-pull-down = <MTK_PUPD_SET_R1R0_10>;
 		};
 	};
+
+	usb_id_pins_float: usb_iddig_pull_up {
+		pins_iddig {
+			pinmux = <MT8173_PIN_16_IDDIG__FUNC_IDDIG>;
+			bias-pull-up;
+		};
+	};
+
+	usb_id_pins_ground: usb_iddig_pull_down {
+		pins_iddig {
+			pinmux = <MT8173_PIN_16_IDDIG__FUNC_IDDIG>;
+			bias-pull-down;
+		};
+	};
 };
 
 &pwm0 {
@@ -431,12 +460,25 @@
 	status = "okay";
 };
 
+&ssusb {
+	vusb33-supply = <&mt6397_vusb_reg>;
+	vbus-supply = <&usb_p0_vbus>;
+	extcon = <&extcon_usb>;
+	dr_mode = "otg";
+	mediatek,enable-wakeup;
+	pinctrl-names = "default", "id_float", "id_ground";
+	pinctrl-0 = <&usb_id_pins_float>;
+	pinctrl-1 = <&usb_id_pins_float>;
+	pinctrl-2 = <&usb_id_pins_ground>;
+	status = "okay";
+};
+
 &uart0 {
 	status = "okay";
 };
 
-&usb30 {
+&usb_host {
 	vusb33-supply = <&mt6397_vusb_reg>;
 	vbus-supply = <&usb_p1_vbus>;
-	mediatek,wakeup-src = <1>;
+	status = "okay";
 };
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 05f89c4..723ab6e 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -608,11 +608,14 @@
 			status = "disabled";
 		};
 
-		usb30: usb at 11270000 {
-			compatible = "mediatek,mt8173-xhci";
-			reg = <0 0x11270000 0 0x1000>,
+		ssusb: usb at 11271000 {
+			compatible = "mediatek,mt8173-mtu3";
+			reg = <0 0x11271000 0 0x3000>,
 			      <0 0x11280700 0 0x0100>;
-			interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+			reg-names = "mac", "ippc";
+			interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_LOW>;
+			phys = <&phy_port0 PHY_TYPE_USB3>,
+			       <&phy_port1 PHY_TYPE_USB2>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
 			clocks = <&topckgen CLK_TOP_USB30_SEL>,
 				 <&pericfg CLK_PERI_USB0>,
@@ -620,10 +623,21 @@
 			clock-names = "sys_ck",
 				      "wakeup_deb_p0",
 				      "wakeup_deb_p1";
-			phys = <&phy_port0 PHY_TYPE_USB3>,
-			       <&phy_port1 PHY_TYPE_USB2>;
 			mediatek,syscon-wakeup = <&pericfg>;
-			status = "okay";
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+			status = "disabled";
+
+			usb_host: xhci at 11270000 {
+				compatible = "mediatek,mt8173-xhci";
+				reg = <0 0x11270000 0 0x1000>;
+				interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_LOW>;
+				power-domains = <&scpsys MT8173_POWER_DOMAIN_USB>;
+				clocks = <&topckgen CLK_TOP_USB30_SEL>;
+				clock-names = "sys_ck";
+				status = "disabled";
+			};
 		};
 
 		u3phy: usb-phy at 11290000 {
-- 
1.7.9.5



From chunfeng.yun at mediatek.com  Tue Jun 14 20:07:19 2016
From: chunfeng.yun at mediatek.com (Chunfeng Yun)
Date: Wed, 15 Jun 2016 11:07:19 +0800
Subject: [PATCH v4,4/5] usb: Add MediaTek USB3 DRD Driver
In-Reply-To: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <1465960040-768-5-git-send-email-chunfeng.yun@mediatek.com>

This patch adds support for the MediaTek USB3 controller
integrated into MT8173. It can be configured as Dual-Role
Device (DRD), Peripheral Only and Host Only (xHCI) modes.

Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
---
 drivers/usb/Kconfig                |    2 +
 drivers/usb/Makefile               |    1 +
 drivers/usb/mtu3/Kconfig           |   48 ++
 drivers/usb/mtu3/Makefile          |   21 +
 drivers/usb/mtu3/mtu3.h            |  424 +++++++++++++++++
 drivers/usb/mtu3/mtu3_core.c       |  879 ++++++++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_dr.c         |  375 +++++++++++++++
 drivers/usb/mtu3/mtu3_dr.h         |  108 +++++
 drivers/usb/mtu3/mtu3_gadget.c     |  731 ++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_gadget_ep0.c |  879 ++++++++++++++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_host.c       |  294 ++++++++++++
 drivers/usb/mtu3/mtu3_hw_regs.h    |  474 +++++++++++++++++++
 drivers/usb/mtu3/mtu3_plat.c       |  490 ++++++++++++++++++++
 drivers/usb/mtu3/mtu3_qmu.c        |  599 ++++++++++++++++++++++++
 drivers/usb/mtu3/mtu3_qmu.h        |   43 ++
 15 files changed, 5368 insertions(+)
 create mode 100644 drivers/usb/mtu3/Kconfig
 create mode 100644 drivers/usb/mtu3/Makefile
 create mode 100644 drivers/usb/mtu3/mtu3.h
 create mode 100644 drivers/usb/mtu3/mtu3_core.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.c
 create mode 100644 drivers/usb/mtu3/mtu3_dr.h
 create mode 100644 drivers/usb/mtu3/mtu3_gadget.c
 create mode 100644 drivers/usb/mtu3/mtu3_gadget_ep0.c
 create mode 100644 drivers/usb/mtu3/mtu3_host.c
 create mode 100644 drivers/usb/mtu3/mtu3_hw_regs.h
 create mode 100644 drivers/usb/mtu3/mtu3_plat.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.c
 create mode 100644 drivers/usb/mtu3/mtu3_qmu.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 8689dcb..9ca0bf0 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -95,6 +95,8 @@ source "drivers/usb/usbip/Kconfig"
 
 endif
 
+source "drivers/usb/mtu3/Kconfig"
+
 source "drivers/usb/musb/Kconfig"
 
 source "drivers/usb/dwc3/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..7791af6 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
 obj-$(CONFIG_USB_MON)		+= mon/
+obj-$(CONFIG_USB_MTU3)		+= mtu3/
 
 obj-$(CONFIG_PCI)		+= host/
 obj-$(CONFIG_USB_EHCI_HCD)	+= host/
diff --git a/drivers/usb/mtu3/Kconfig b/drivers/usb/mtu3/Kconfig
new file mode 100644
index 0000000..929ff51
--- /dev/null
+++ b/drivers/usb/mtu3/Kconfig
@@ -0,0 +1,48 @@
+# For MTK USB3.0 IP
+
+config USB_MTU3
+	tristate "MediaTek USB3 Dual Role controller"
+	depends on EXTCON && (USB || USB_GADGET) && HAS_DMA
+	depends on ARCH_MEDIATEK || COMPILE_TEST
+	select USB_XHCI_MTK if USB_SUPPORT && USB_XHCI_HCD
+	help
+	  Say Y or M here if your system runs on MediaTek SoCs with
+	  Dual Role SuperSpeed USB controller. You can select usb
+	  mode as peripheral role or host role, or both.
+
+	  If you don't know what this is, please say N.
+
+	  Choose M here to compile this driver as a module, and it
+	  will be called mtu3.ko.
+
+
+if USB_MTU3
+choice
+	bool "MTU3 Mode Selection"
+	default USB_MTU3_DUAL_ROLE if (USB && USB_GADGET)
+	default USB_MTU3_HOST if (USB && !USB_GADGET)
+	default USB_MTU3_GADGET if (!USB && USB_GADGET)
+
+config USB_MTU3_HOST
+	bool "Host only mode"
+	depends on USB=y || USB=USB_MTU3
+	help
+	  Select this when you want to use MTU3 in host mode only,
+	  thereby the gadget feature will be regressed.
+
+config USB_MTU3_GADGET
+	bool "Gadget only mode"
+	depends on USB_GADGET=y || USB_GADGET=USB_MTU3
+	help
+	  Select this when you want to use MTU3 in gadget mode only,
+	  thereby the host feature will be regressed.
+
+config USB_MTU3_DUAL_ROLE
+	bool "Dual Role mode"
+	depends on ((USB=y || USB=USB_MTU3) && (USB_GADGET=y || USB_GADGET=USB_MTU3))
+	help
+	  This is the default mode of working of MTU3 controller where
+	  both host and gadget features are enabled.
+
+endchoice
+endif
diff --git a/drivers/usb/mtu3/Makefile b/drivers/usb/mtu3/Makefile
new file mode 100644
index 0000000..4307ab9
--- /dev/null
+++ b/drivers/usb/mtu3/Makefile
@@ -0,0 +1,21 @@
+
+#ifeq ($(CONFIG_USB_DEBUG),y)
+	ccflags-y	+= -DDEBUG
+#endif
+
+obj-$(CONFIG_USB_MTU3)	+= mtu3.o
+
+mtu3-y	:= mtu3_plat.o
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_HOST) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+	mtu3-y	+= mtu3_host.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_MTU3_GADGET) $(CONFIG_USB_MTU3_DUAL_ROLE)),)
+	mtu3-y	+= mtu3_core.o mtu3_gadget_ep0.o mtu3_gadget.o mtu3_qmu.o
+endif
+
+ifneq ($(CONFIG_USB_MTU3_DUAL_ROLE),)
+	mtu3-y	+= mtu3_dr.o
+endif
+
diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h
new file mode 100644
index 0000000..f57b45e
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3.h
@@ -0,0 +1,424 @@
+/*
+ * mtu3.h - MediaTek USB3 DRD header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 __MTU3_H__
+#define __MTU3_H__
+
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/extcon.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/phy/phy.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/workqueue.h>
+
+struct mtu3;
+struct mtu3_ep;
+struct mtu3_request;
+
+#include "mtu3_hw_regs.h"
+#include "mtu3_qmu.h"
+
+#define	MU3D_EP_TXCR0(epnum)	(U3D_TX1CSR0 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_TXCR1(epnum)	(U3D_TX1CSR1 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_TXCR2(epnum)	(U3D_TX1CSR2 + (((epnum) - 1) * 0x10))
+
+#define	MU3D_EP_RXCR0(epnum)	(U3D_RX1CSR0 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR1(epnum)	(U3D_RX1CSR1 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR2(epnum)	(U3D_RX1CSR2 + (((epnum) - 1) * 0x10))
+#define	MU3D_EP_RXCR3(epnum)	(U3D_RX1CSR3 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_RQCSR(epnum)	(U3D_RXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQSAR(epnum)	(U3D_RXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQCPR(epnum)	(U3D_RXQCPR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_RQLDPR(epnum)	(U3D_RXQLDPR1 + (((epnum) - 1) * 0x10))
+
+#define USB_QMU_TQCSR(epnum)	(U3D_TXQCSR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQSAR(epnum)	(U3D_TXQSAR1 + (((epnum) - 1) * 0x10))
+#define USB_QMU_TQCPR(epnum)	(U3D_TXQCPR1 + (((epnum) - 1) * 0x10))
+
+#define SSUSB_U3_CTRL(p)	(U3D_SSUSB_U3_CTRL_0P + ((p) * 0x08))
+#define SSUSB_U2_CTRL(p)	(U3D_SSUSB_U2_CTRL_0P + ((p) * 0x08))
+
+#define MTU3_DRIVER_NAME	"mtu3"
+#define	DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+#define MTU3_EP_ENABLED		BIT(0)
+#define MTU3_EP_STALL		BIT(1)
+#define MTU3_EP_WEDGE		BIT(2)
+#define MTU3_EP_BUSY		BIT(3)
+
+#define MTU3_U3_IP_SLOT_DEFAULT 2
+#define MTU3_U2_IP_SLOT_DEFAULT 1
+
+/**
+ * Normally the device works on HS or SS, to simplify fifo management,
+ * devide fifo into some 512B parts, use bitmap to manage it; And
+ * 128 bits size of bitmap is large enough, that means it can manage
+ * up to 64KB fifo size.
+ * NOTE: MTU3_EP_FIFO_UNIT should be power of two
+ */
+#define MTU3_EP_FIFO_UNIT		(1 << 9)
+#define MTU3_FIFO_BIT_SIZE		128
+#define MTU3_U2_IP_EP0_FIFO_SIZE	64
+
+/**
+ * Maximum size of ep0 response buffer for ch9 requests,
+ * the set_sel request uses 6 so far, get_status is 2
+ */
+#define EP0_RESPONSE_BUF  6
+
+/* device operated link and speed got from DEVICE_CONF register */
+enum mtu3_speed {
+	MTU3_SPEED_INACTIVE = 0,
+	MTU3_SPEED_FULL = 1,
+	MTU3_SPEED_HIGH = 3,
+	MTU3_SPEED_SUPER = 4,
+};
+
+/**
+ * @MU3D_EP0_STATE_SETUP: waits for SETUP or received a SETUP
+ *		without data stage.
+ * @MU3D_EP0_STATE_TX: IN data stage
+ * @MU3D_EP0_STATE_RX: OUT data stage
+ * @MU3D_EP0_STATE_TX_END: the last IN data is transferred, and
+ *		waits for its completion ineterrupt
+ * @MU3D_EP0_STATE_STALL: ep0 is in stall status, will be auto-cleared
+ *		after receives a SETUP.
+ */
+enum mtu3_g_ep0_state {
+	MU3D_EP0_STATE_SETUP = 1,
+	MU3D_EP0_STATE_TX,
+	MU3D_EP0_STATE_RX,
+	MU3D_EP0_STATE_TX_END,
+	MU3D_EP0_STATE_STALL,
+};
+
+/**
+ * @base: the base address of fifo
+ * @limit: the bitmap size in bits
+ * @bitmap: fifo bitmap in unit of @MTU3_EP_FIFO_UNIT
+ */
+struct mtu3_fifo_info {
+	u32 base;
+	u32 limit;
+	DECLARE_BITMAP(bitmap, MTU3_FIFO_BIT_SIZE);
+};
+
+/**
+ * General Purpose Descriptor (GPD):
+ *	The format of TX GPD is a little different from RX one.
+ *	And the size of GPD is 16 bytes.
+ *
+ * @flag:
+ *	bit0: Hardware Own (HWO);
+ *	bit1: Buffer Descriptor Present (BDP), always 0, BD is not supported;
+ *	bit2: Bypass (BPS), 1: HW skips this GPD if HWO = 1;
+ *	bit7: Interrupt On Completion (IOC);
+ * @chksum: This is used to validate the contents of this GPD;
+ *	If TXQ_CS_EN / RXQ_CS_EN bit is set, an interrupt is issued
+ *	when checksum validation fails;
+ *	Checksum value is calculated over the 16 bytes of the GPD by default;
+ * @data_buf_len: (RX ONLY) This value indicates the length of
+ *	the assigned data buffer
+ * @next_gpd: Physical address of the next GPD
+ * @buffer: Physical address of the data buffer
+ * @buf_len:
+ *	(TX): This value indicates the length of the assigned data buffer
+ *	(RX): The total length of data received
+ * @ext_len: reserved
+ * @ext_flag:
+ *	bit5: (TX ONLY) Zero Length Packet (ZLP),
+ */
+struct qmu_gpd {
+	u8 flag;
+	u8 chksum;
+	u16 data_buf_len;
+	u32 next_gpd;
+	u32 buffer;
+	u16 buf_len;
+	u8 ext_len;
+	u8 ext_flag;
+} __packed;
+
+/**
+* dma: physical base address of GPD segment
+* start: virtual base address of GPD segment
+* end: the last gpd element
+* enqueue: the first empty gpd to use
+* dequeue: the first completed gpd serviced by ISR
+* NOTE: the ring elements of the gpd should be >= 2
+*/
+struct mtu3_gpd_ring {
+	dma_addr_t dma;
+	struct qmu_gpd *start;
+	struct qmu_gpd *end;
+	struct qmu_gpd *enqueue;
+	struct qmu_gpd *dequeue;
+};
+
+/**
+* @vbus: vbus 5V used by host mode
+* @edev: external connector used to detect vbus and iddig changes
+* @vbus_nb: notifier for vbus detection
+* @vbus_nb: notifier for iddig(idpin) detection
+* @extcon_reg_dwork: delay work for extcon notifier register, waiting for
+*		xHCI driver initialized, it's necessary for system bootup
+*		as device.
+* @is_u3_drd: whether port0 supports usb3.0 dual-role device or not
+* @id_*: used to maually switch between host and device modes by inpin
+* @manual_drd_enabled: it's true when receptacle is TYPE-A, and supports dual
+*		role devices by an interface to switch host/device modes
+*		depending on user input.
+*/
+struct otg_switch_mtk {
+	struct regulator *vbus;
+	struct extcon_dev *edev;
+	struct notifier_block vbus_nb;
+	struct notifier_block id_nb;
+	struct delayed_work extcon_reg_dwork;
+	bool is_u3_drd;
+	/* dual-role switch by sysfs interface */
+	struct pinctrl *id_pinctrl;
+	struct pinctrl_state *id_float;
+	struct pinctrl_state *id_ground;
+	bool manual_drd_enabled;
+};
+
+/**
+ * @mac_base: register base address of mu3d device MAC, exclude xhci's
+ * @ippc_base: register base address of ip port controller interface
+ * @vusb33: usb3.3V shared by device/host ip
+ * @sys_clk: system clock of usb, shared by device/host ip
+ * @dr_mode: supports which driver mode:
+ *		host only, device only or dual-role mode
+ * @u2_ports: number of usb2.0 host ports
+ * @u3_ports: number of usb3.0 host ports
+ * @dbgfs_root: only used when supports manual dual-role switch via debugfs
+ * @wakeup_en: it's true when supports remote wakeup in host mode
+ * @wk_deb_p0: port0's wakeup debounce clock
+ * @wk_deb_p1: it's optional, and depends on port1 is supported or not
+ */
+struct ssusb_mtk {
+	struct device *dev;
+	struct mtu3 *u3d;
+	void __iomem *mac_base;
+	void __iomem *ippc_base;
+	struct phy **phys;
+	int num_phys;
+	/* common power & clock */
+	struct regulator *vusb33;
+	struct clk *sys_clk;
+	/* otg */
+	struct otg_switch_mtk otg_switch;
+	enum usb_dr_mode dr_mode;
+	bool is_host;
+	int u2_ports;
+	int u3_ports;
+	struct dentry *dbgfs_root;
+	/* usb wakeup for host mode */
+	bool wakeup_en;
+	struct clk *wk_deb_p0;
+	struct clk *wk_deb_p1;
+	struct regmap *pericfg;
+};
+
+/**
+ * @fifo_size: it is (@slot + 1) * @fifo_seg_size
+ * @fifo_seg_size: it is roundup_pow_of_two(@maxp)
+ */
+struct mtu3_ep {
+	struct usb_ep ep;
+	char name[12];
+	struct mtu3 *mtu;
+	u8 epnum;
+	u8 type;
+	u8 is_in;
+	u16 maxp;
+	int slot;
+	u32 fifo_size;
+	u32 fifo_addr;
+	u32 fifo_seg_size;
+	struct mtu3_fifo_info *fifo;
+
+	struct list_head req_list;
+	struct mtu3_gpd_ring gpd_ring;
+	const struct usb_ss_ep_comp_descriptor *comp_desc;
+	const struct usb_endpoint_descriptor *desc;
+
+	int flags;
+	u8 wedged;
+	u8 busy;
+};
+
+struct mtu3_request {
+	struct usb_request request;
+	struct list_head list;
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	struct qmu_gpd *gpd;
+	int epnum;
+};
+
+static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev)
+{
+	return dev_get_drvdata(dev);
+}
+
+/**
+ * struct mtu3 - device driver instance data.
+ * @slot: MTU3_U2_IP_SLOT_DEFAULT for U2 IP only,
+ *		MTU3_U3_IP_SLOT_DEFAULT for U3 IP
+ * @may_wakeup: means device's remote wakeup is enabled
+ * @is_self_powered: is reported in device status and the config descriptor
+ * @ep0_req: dummy request used while handling standard USB requests
+ *		for GET_STATUS and SET_SEL
+ * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests
+ */
+struct mtu3 {
+	spinlock_t lock;
+	struct ssusb_mtk *ssusb;
+	struct device *dev;
+	void __iomem *mac_base;
+	void __iomem *ippc_base;
+	int irq;
+
+	struct mtu3_fifo_info tx_fifo;
+	struct mtu3_fifo_info rx_fifo;
+
+	struct mtu3_ep *ep_array;
+	struct mtu3_ep *in_eps;
+	struct mtu3_ep *out_eps;
+	struct mtu3_ep *ep0;
+	int num_eps;
+	int slot;
+	int active_ep;
+
+	struct dma_pool	*qmu_gpd_pool;
+	struct tasklet_struct qmu_done;
+	u32 qmu_done_intr;
+
+	enum mtu3_g_ep0_state ep0_state;
+	struct usb_gadget g;	/* the gadget */
+	struct usb_gadget_driver *gadget_driver;
+	struct mtu3_request ep0_req;
+	u8 setup_buf[EP0_RESPONSE_BUF];
+	u32 max_speed;
+
+	unsigned is_active:1;
+	unsigned may_wakeup:1;
+	unsigned is_self_powered:1;
+	unsigned test_mode:1;
+	unsigned softconnect:1;
+	unsigned u1_enable:1;
+	unsigned u2_enable:1;
+	unsigned is_u3_ip:1;
+
+	u8 address;
+	u8 test_mode_nr;
+	u32 hw_version;
+};
+
+static inline struct mtu3 *gadget_to_mtu3(struct usb_gadget *g)
+{
+	return container_of(g, struct mtu3, g);
+}
+
+static inline int is_first_entry(const struct list_head *list,
+	const struct list_head *head)
+{
+	return list_is_last(head, list);
+}
+
+static inline struct mtu3_request *to_mtu3_request(struct usb_request *req)
+{
+	return req ? container_of(req, struct mtu3_request, request) : NULL;
+}
+
+static inline struct mtu3_ep *to_mtu3_ep(struct usb_ep *ep)
+{
+	return ep ? container_of(ep, struct mtu3_ep, ep) : NULL;
+}
+
+static inline struct mtu3_request *next_request(struct mtu3_ep *mep)
+{
+	struct list_head *queue = &mep->req_list;
+
+	if (list_empty(queue))
+		return NULL;
+
+	return list_first_entry(queue, struct mtu3_request, list);
+}
+
+static inline void mtu3_writel(void __iomem *base, u32 offset, u32 data)
+{
+	writel(data, base + offset);
+}
+
+static inline u32 mtu3_readl(void __iomem *base, u32 offset)
+{
+	return readl(base + offset);
+}
+
+static inline void mtu3_setbits(void __iomem *base, u32 offset, u32 bits)
+{
+	void __iomem *addr = base + offset;
+	u32 tmp = readl(addr);
+
+	writel((tmp | (bits)), addr);
+}
+
+static inline void mtu3_clrbits(void __iomem *base, u32 offset, u32 bits)
+{
+	void __iomem *addr = base + offset;
+	u32 tmp = readl(addr);
+
+	writel((tmp & ~(bits)), addr);
+}
+
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks);
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req);
+void mtu3_req_complete(struct mtu3_ep *mep,
+		struct usb_request *req, int status);
+
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+		int interval, int burst, int mult);
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep);
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set);
+void mtu3_ep0_setup(struct mtu3 *mtu);
+void mtu3_start(struct mtu3 *mtu);
+void mtu3_stop(struct mtu3 *mtu);
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on);
+
+int mtu3_gadget_setup(struct mtu3 *mtu);
+void mtu3_gadget_cleanup(struct mtu3 *mtu);
+void mtu3_gadget_reset(struct mtu3 *mtu);
+void mtu3_gadget_suspend(struct mtu3 *mtu);
+void mtu3_gadget_resume(struct mtu3 *mtu);
+void mtu3_gadget_disconnect(struct mtu3 *mtu);
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu);
+extern const struct usb_ep_ops mtu3_ep0_ops;
+
+#endif
diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c
new file mode 100644
index 0000000..756824f
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_core.c
@@ -0,0 +1,879 @@
+/*
+ * mtu3_core.c - hardware access layer and gadget init/exit of
+ *                     MediaTek usb3 Dual-Role Controller Driver
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+
+static int ep_fifo_alloc(struct mtu3_ep *mep, u32 seg_size)
+{
+	struct mtu3_fifo_info *fifo = mep->fifo;
+	u32 free_bits = DIV_ROUND_UP(seg_size, MTU3_EP_FIFO_UNIT);
+	u32 start_bit;
+
+	free_bits = roundup_pow_of_two(free_bits);
+	if (free_bits > fifo->limit)
+		return -EINVAL;
+
+	mep->fifo_seg_size = free_bits * MTU3_EP_FIFO_UNIT;
+	free_bits = free_bits * (mep->slot + 1);
+	start_bit = bitmap_find_next_zero_area(fifo->bitmap,
+			fifo->limit, 0, free_bits, 0);
+	if (start_bit >= fifo->limit)
+		return -EOVERFLOW;
+
+	bitmap_set(fifo->bitmap, start_bit, free_bits);
+	mep->fifo_size = free_bits * MTU3_EP_FIFO_UNIT;
+	mep->fifo_addr = fifo->base + MTU3_EP_FIFO_UNIT * start_bit;
+
+	dev_dbg(mep->mtu->dev, "%s fifo:%#x/%#x, start_bit: %d\n",
+		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+
+	return mep->fifo_addr;
+}
+
+static void ep_fifo_free(struct mtu3_ep *mep)
+{
+	struct mtu3_fifo_info *fifo = mep->fifo;
+	u32 addr = mep->fifo_addr;
+	u32 bits = mep->fifo_size / MTU3_EP_FIFO_UNIT;
+	u32 start_bit;
+
+	if (unlikely(addr < fifo->base || bits > fifo->limit))
+		return;
+
+	start_bit = (addr - fifo->base) / MTU3_EP_FIFO_UNIT;
+	bitmap_clear(fifo->bitmap, start_bit, bits);
+	mep->fifo_size = 0;
+	mep->fifo_seg_size = 0;
+
+	dev_dbg(mep->mtu->dev, "%s size:%#x/%#x, start_bit: %d\n",
+		__func__, mep->fifo_seg_size, mep->fifo_size, start_bit);
+}
+
+/* enable/disable U3D SS function */
+static void mtu3_ss_func_set(struct mtu3 *mtu, bool enable)
+{
+	/* If usb3_en==0, LTSSM will go to SS.Disable state */
+	if (enable)
+		mtu3_setbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+	else
+		mtu3_clrbits(mtu->mac_base, U3D_USB3_CONFIG, USB3_EN);
+
+	dev_dbg(mtu->dev, "USB3_EN = %d\n", !!enable);
+}
+
+/* set/clear U3D HS device soft connect */
+static void mtu3_hs_softconn_set(struct mtu3 *mtu, bool enable)
+{
+	if (enable) {
+		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+			SOFT_CONN | SUSPENDM_ENABLE);
+	} else {
+		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT,
+			SOFT_CONN | SUSPENDM_ENABLE);
+	}
+	dev_dbg(mtu->dev, "SOFTCONN = %d\n", !!enable);
+}
+
+/* only port0 of U2/U3 supports device mode */
+static int mtu3_device_enable(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+	u32 check_clk = 0;
+
+	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+	if (mtu->is_u3_ip) {
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+		mtu3_clrbits(ibase, SSUSB_U3_CTRL(0),
+			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN |
+			SSUSB_U3_PORT_HOST_SEL));
+	}
+	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0),
+		(SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN |
+		SSUSB_U2_PORT_HOST_SEL));
+
+	mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+
+	return ssusb_check_clocks(mtu->ssusb, check_clk);
+}
+
+static void mtu3_device_disable(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+
+	if (mtu->is_u3_ip)
+		mtu3_setbits(ibase, SSUSB_U3_CTRL(0),
+			(SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN));
+
+	mtu3_setbits(ibase, SSUSB_U2_CTRL(0),
+		SSUSB_U2_PORT_DIS | SSUSB_U2_PORT_PDN);
+
+	mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL);
+
+	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+/* reset U3D all dev module. */
+static void mtu3_device_reset(struct mtu3 *mtu)
+{
+	void __iomem *ibase = mtu->ippc_base;
+
+	mtu3_setbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+	udelay(1);
+	mtu3_clrbits(ibase, U3D_SSUSB_DEV_RST_CTRL, SSUSB_DEV_SW_RST);
+}
+
+/* disable all interrupts */
+static void mtu3_intr_disable(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	/* Disable level 1 interrupts */
+	mtu3_writel(mbase, U3D_LV1IECR, ~0x0);
+
+	/* Disable endpoint interrupts */
+	mtu3_writel(mbase, U3D_EPIECR, ~0x0);
+}
+
+static void mtu3_intr_status_clear(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	/* Clear EP0 and Tx/Rx EPn interrupts status */
+	mtu3_writel(mbase, U3D_EPISR, ~0x0);
+
+	/* Clear U2 USB common interrupt status */
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR, ~0x0);
+
+	/* Clear U3 LTSSM interrupt status */
+	mtu3_writel(mbase, U3D_LTSSM_INTR, ~0x0);
+
+	/* Clear speed change interrupt status */
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR, ~0x0);
+}
+
+/* enable system global interrupt */
+static void mtu3_intr_enable(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 value;
+
+	/*Enable level 1 interrupt (BMU, QMU, MAC3, DMA, MAC2, EPCTL) */
+	value = BMU_INTR | QMU_INTR | MAC3_INTR | MAC2_INTR | EP_CTRL_INTR;
+	mtu3_writel(mbase, U3D_LV1IESR, value);
+
+	/* Enable U2 common USB interrupt */
+	value = SUSPEND_INTR | RESUME_INTR | RESET_INTR;
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value);
+
+	if (mtu->is_u3_ip) {
+		/* Enable U3 LTSSM interrupt */
+		value = HOT_RST_INTR | WARM_RST_INTR | VBUS_RISE_INTR |
+		    VBUS_FALL_INTR | ENTER_U3_INTR | EXIT_U3_INTR;
+		mtu3_writel(mbase, U3D_LTSSM_INTR_ENABLE, value);
+	}
+
+	/* Enable QMU interrupt. */
+	value = TXQ_CSERR_INT | TXQ_LENERR_INT | RXQ_CSERR_INT |
+			RXQ_LENERR_INT | RXQ_ZLPERR_INT;
+	mtu3_writel(mbase, U3D_QIESR1, value);
+
+	/* Enable speed change interrupt */
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR_ENABLE, SSUSB_DEV_SPEED_CHG_INTR);
+}
+
+/* set/clear the stall and toggle bits for non-ep0 */
+void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	u8 epnum = mep->epnum;
+	u32 csr;
+
+	if (mep->is_in) {	/* TX */
+		csr = mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)) & TX_W1C_BITS;
+		if (set)
+			csr |= TX_SENDSTALL;
+		else
+			csr = (csr & (~TX_SENDSTALL)) | TX_SENTSTALL;
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr);
+
+	} else {	/* RX */
+		csr = mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)) & RX_W1C_BITS;
+		if (set)
+			csr |= RX_SENDSTALL;
+		else
+			csr = (csr & (~RX_SENDSTALL)) | RX_SENTSTALL;
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr);
+
+	}
+
+	if (!set) {
+		mtu3_setbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
+		mtu3_clrbits(mbase, U3D_EP_RST, EP_RST(mep->is_in, epnum));
+		mep->flags &= ~MTU3_EP_STALL;
+	} else {
+		mep->flags |= MTU3_EP_STALL;
+	}
+
+	dev_dbg(mtu->dev, "%s: %s\n", mep->name,
+		set ? "SEND STALL" : "CLEAR STALL, with EP RESET");
+}
+
+void mtu3_dev_on_off(struct mtu3 *mtu, int is_on)
+{
+	if (mtu->is_u3_ip && (mtu->max_speed == USB_SPEED_SUPER))
+		mtu3_ss_func_set(mtu, is_on);
+	else
+		mtu3_hs_softconn_set(mtu, is_on);
+
+	dev_info(mtu->dev, "gadget (%s) pullup D%s\n",
+		usb_speed_string(mtu->max_speed), is_on ? "+" : "-");
+}
+
+void mtu3_start(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	dev_dbg(mtu->dev, "%s devctl 0x%02x\n", __func__,
+		(u8)mtu3_readl(mbase, U3D_DEVICE_CONTROL));
+
+	mtu3_clrbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+
+	/*
+	 * When disable U2 port, USB2_CSR's register will be reset to
+	 * default value after re-enable it again(HS is enabled by default).
+	 * So if force mac to work as FS, disable HS function.
+	 */
+	if (mtu->max_speed == USB_SPEED_FULL)
+		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+
+	/* Initialize the default interrupts */
+	mtu3_intr_enable(mtu);
+
+	mtu->is_active = 1;
+
+	if (mtu->softconnect)
+		mtu3_dev_on_off(mtu, 1);
+
+}
+
+void mtu3_stop(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	mtu3_intr_disable(mtu);
+	mtu3_intr_status_clear(mtu);
+
+	if (mtu->softconnect)
+		mtu3_dev_on_off(mtu, 0);
+
+	mtu->is_active = 0;
+	mtu3_setbits(mtu->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN);
+}
+
+int mtu3_config_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+			int interval, int burst, int mult)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 csr0, csr1, csr2;
+	u32 fifosz = 0;
+	u32 max_pkt;
+	int fifo_addr;
+
+	fifo_addr = ep_fifo_alloc(mep, mep->maxp);
+	if (fifo_addr < 0) {
+		dev_err(mtu->dev, "alloc ep fifo failed(%d)\n", fifosz);
+		return -ENOMEM;
+	}
+	fifosz = ilog2(mep->fifo_seg_size);
+	dev_dbg(mtu->dev, "%s fifosz: %x(%x/%x)\n", __func__, fifosz,
+		mep->fifo_seg_size, mep->fifo_size);
+
+	if (mep->is_in) {
+		csr0 = TX_TXMAXPKTSZ(mep->maxp);
+		csr0 |= TX_DMAREQEN;
+
+		max_pkt = (burst + 1) * (mult + 1) - 1;
+		csr1 = TX_SS_BURST(burst) | TX_SLOT(mep->slot);
+		csr1 |= TX_MAX_PKT(max_pkt) | TX_MULT(mult);
+
+		csr2 = TX_FIFOADDR(fifo_addr >> 4);
+		csr2 |= TX_FIFOSEGSIZE(fifosz);
+
+		switch (mep->type) {
+		case USB_ENDPOINT_XFER_BULK:
+			csr1 |= TX_TYPE(TYPE_BULK);
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			csr1 |= TX_TYPE(TYPE_ISO);
+			csr2 |= TX_BINTERVAL(interval);
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			csr1 |= TX_TYPE(TYPE_INT);
+			csr2 |= TX_BINTERVAL(interval);
+			break;
+		}
+
+		/* Enable QMU Done interrupt */
+		mtu3_setbits(mbase, U3D_QIESR0, QMU_TX_DONE_INT(epnum));
+
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), csr0);
+		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), csr1);
+		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), csr2);
+
+		dev_dbg(mtu->dev, "U3D_TX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+			epnum, mtu3_readl(mbase, MU3D_EP_TXCR0(epnum)),
+			mtu3_readl(mbase, MU3D_EP_TXCR1(epnum)),
+			mtu3_readl(mbase, MU3D_EP_TXCR2(epnum)));
+
+	} else {
+
+		csr0 = RX_RXMAXPKTSZ(mep->maxp);
+		csr0 |= RX_DMAREQEN;
+
+		max_pkt = (burst + 1) * (mult + 1) - 1;
+		csr1 = RX_SS_BURST(burst) | RX_SLOT(mep->slot);
+		csr1 |= RX_MAX_PKT(max_pkt) | RX_MULT(mult);
+
+		csr2 = RX_FIFOADDR(fifo_addr >> 4);
+		csr2 |= RX_FIFOSEGSIZE(fifosz);
+
+		switch (mep->type) {
+		case USB_ENDPOINT_XFER_BULK:
+			csr1 |= RX_TYPE(TYPE_BULK);
+			break;
+		case USB_ENDPOINT_XFER_ISOC:
+			csr1 |= RX_TYPE(TYPE_ISO);
+			csr2 |= RX_BINTERVAL(interval);
+			break;
+		case USB_ENDPOINT_XFER_INT:
+			csr1 |= RX_TYPE(TYPE_INT);
+			csr2 |= RX_BINTERVAL(interval);
+			break;
+		}
+
+		/*Enable QMU Done interrupt */
+		mtu3_setbits(mbase, U3D_QIESR0, QMU_RX_DONE_INT(epnum));
+
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), csr0);
+		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), csr1);
+		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), csr2);
+
+		dev_dbg(mtu->dev, "U3D_RX%d CSR0:%#x, CSR1:%#x, CSR2:%#x\n",
+			epnum, mtu3_readl(mbase, MU3D_EP_RXCR0(epnum)),
+			mtu3_readl(mbase, MU3D_EP_RXCR1(epnum)),
+			mtu3_readl(mbase, MU3D_EP_RXCR2(epnum)));
+
+	}
+
+	dev_dbg(mtu->dev, "csr0:%#x, csr1:%#x, csr2:%#x\n", csr0, csr1, csr2);
+	dev_dbg(mtu->dev, "%s: %s, fifo-addr:%#x, fifo-size:%#x(%#x/%#x)\n",
+		__func__, mep->name, mep->fifo_addr, mep->fifo_size, fifosz,
+		mep->fifo_seg_size);
+
+	return 0;
+}
+
+void mtu3_deconfig_ep(struct mtu3 *mtu, struct mtu3_ep *mep)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+
+	if (mep->is_in) {
+		mtu3_writel(mbase, MU3D_EP_TXCR0(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_TXCR1(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_TXCR2(epnum), 0);
+		mtu3_setbits(mbase, U3D_QIECR0, QMU_TX_DONE_INT(epnum));
+	} else {
+		mtu3_writel(mbase, MU3D_EP_RXCR0(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_RXCR1(epnum), 0);
+		mtu3_writel(mbase, MU3D_EP_RXCR2(epnum), 0);
+		mtu3_setbits(mbase, U3D_QIECR0, QMU_RX_DONE_INT(epnum));
+	}
+
+	ep_fifo_free(mep);
+
+	dev_dbg(mtu->dev, "%s: %s\n", __func__, mep->name);
+}
+
+/*
+ * Two scenarios:
+ * 1. when device IP supports SS, the fifo of EP0, TX EPs, RX EPs
+ *	are separated;
+ * 2. when only supports HS, the fifo is shared for all eps, and
+ *	the capability registers of @EPNTXFFSZ or @EPNRXFFSZ indicate
+ *	the total fifo size of non-ep0, and ep0's is 64B, so the total fifo
+ *	size is 64B + @EPNTXFFSZ;
+ *	Because the first 64B should be reserved for EP0, so non-ep0's fifo
+ *	starts from offset 64, and are divided into two equal parts for
+ *	TX or RX EPs for simplification.
+ */
+static void get_ep_fifo_config(struct mtu3 *mtu)
+{
+	struct mtu3_fifo_info *tx_fifo;
+	struct mtu3_fifo_info *rx_fifo;
+	u32 fifosize;
+
+	if (mtu->is_u3_ip) {
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+		tx_fifo = &mtu->tx_fifo;
+		tx_fifo->base = 0;
+		tx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNRXFFSZ);
+		rx_fifo = &mtu->rx_fifo;
+		rx_fifo->base = 0;
+		rx_fifo->limit = fifosize / MTU3_EP_FIFO_UNIT;
+		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+		mtu->slot = MTU3_U3_IP_SLOT_DEFAULT;
+	} else {
+		fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ);
+		tx_fifo = &mtu->tx_fifo;
+		tx_fifo->base = MTU3_U2_IP_EP0_FIFO_SIZE;
+		tx_fifo->limit = (fifosize / MTU3_EP_FIFO_UNIT) >> 1;
+		bitmap_zero(tx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+
+		rx_fifo = &mtu->rx_fifo;
+		rx_fifo->base =
+			tx_fifo->base + tx_fifo->limit * MTU3_EP_FIFO_UNIT;
+		rx_fifo->limit = tx_fifo->limit;
+		bitmap_zero(rx_fifo->bitmap, MTU3_FIFO_BIT_SIZE);
+		mtu->slot = MTU3_U2_IP_SLOT_DEFAULT;
+	}
+
+	dev_dbg(mtu->dev, "%s, TX: base-%d, limit-%d; RX: base-%d, limit-%d\n",
+		__func__, tx_fifo->base, tx_fifo->limit,
+		rx_fifo->base, rx_fifo->limit);
+}
+
+void mtu3_ep0_setup(struct mtu3 *mtu)
+{
+	u32 maxpacket = mtu->g.ep0->maxpacket;
+	u32 csr;
+
+	dev_dbg(mtu->dev, "%s maxpacket: %d\n", __func__, maxpacket);
+
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR);
+	csr &= ~EP0_MAXPKTSZ_MSK;
+	csr |= EP0_MAXPKTSZ(maxpacket);
+	csr &= EP0_W1C_BITS;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+	/* Enable EP0 interrupt */
+	mtu3_writel(mtu->mac_base, U3D_EPIESR, EP0ISR);
+}
+
+static int mtu3_mem_alloc(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_ep *ep_array;
+	int in_ep_num, out_ep_num;
+	u32 cap_epinfo, cap_dev;
+	int ret;
+	int i;
+
+	mtu->hw_version = mtu3_readl(mtu->ippc_base, U3D_SSUSB_HW_ID);
+
+	cap_dev = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP);
+	mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(cap_dev);
+
+	cap_epinfo = mtu3_readl(mbase, U3D_CAP_EPINFO);
+	in_ep_num = CAP_TX_EP_NUM(cap_epinfo);
+	out_ep_num = CAP_RX_EP_NUM(cap_epinfo);
+
+	dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version,
+		mtu->is_u3_ip ? "U3" : "U2");
+	dev_info(mtu->dev, "fifosz/epnum: Tx=%#x/%d, Rx=%#x/%d\n",
+		 mtu3_readl(mbase, U3D_CAP_EPNTXFFSZ), in_ep_num,
+		 mtu3_readl(mbase, U3D_CAP_EPNRXFFSZ), out_ep_num);
+
+	/* one for ep0, another is reserved */
+	mtu->num_eps = min(in_ep_num, out_ep_num) + 1;
+	ep_array = kcalloc(mtu->num_eps * 2, sizeof(*ep_array), GFP_KERNEL);
+	if (ep_array == NULL)
+		return -ENOMEM;
+
+	mtu->ep_array = ep_array;
+	mtu->in_eps = ep_array;
+	mtu->out_eps = &ep_array[mtu->num_eps];
+	/* ep0 use in_eps[0], out_eps[0] is reserved */
+	mtu->ep0 = mtu->in_eps;
+
+	mtu->ep0->mtu = mtu;
+	mtu->ep0->epnum = 0;
+
+	for (i = 1; i < mtu->num_eps; i++) {
+		struct mtu3_ep *mep = mtu->in_eps + i;
+
+		mep->fifo = &mtu->tx_fifo;
+		mep = mtu->out_eps + i;
+		mep->fifo = &mtu->rx_fifo;
+	}
+
+	get_ep_fifo_config(mtu);
+
+	ret = mtu3_qmu_init(mtu);
+	if (ret)
+		kfree(mtu->ep_array);
+
+	return ret;
+}
+
+static void mtu3_mem_free(struct mtu3 *mtu)
+{
+	mtu3_qmu_exit(mtu);
+	kfree(mtu->ep_array);
+}
+
+static void mtu3_set_speed(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+
+	if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH))
+		mtu->max_speed = USB_SPEED_HIGH;
+
+	if (mtu->max_speed == USB_SPEED_FULL) {
+		/* disable U3 SS function */
+		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+		/* disable HS function */
+		mtu3_clrbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+	} else if (mtu->max_speed == USB_SPEED_HIGH) {
+		mtu3_clrbits(mbase, U3D_USB3_CONFIG, USB3_EN);
+		/* HS/FS detected by HW */
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT, HS_ENABLE);
+	}
+	dev_info(mtu->dev, "max_speed: %s\n",
+		usb_speed_string(mtu->max_speed));
+}
+
+static void mtu3_regs_init(struct mtu3 *mtu)
+{
+
+	void __iomem *mbase = mtu->mac_base;
+
+	/* be sure interrupts are disabled before registration of ISR */
+	mtu3_intr_disable(mtu);
+	mtu3_intr_status_clear(mtu);
+
+	if (mtu->is_u3_ip) {
+		/* disable LGO_U1/U2 by default */
+		mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL,
+				SW_U1_ACCEPT_ENABLE | SW_U2_ACCEPT_ENABLE |
+				SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE);
+		/* device responses to u3_exit from host automatically */
+		mtu3_clrbits(mbase, U3D_LTSSM_CTRL, SOFT_U3_EXIT_EN);
+	}
+
+	mtu3_set_speed(mtu);
+
+	/* delay about 0.1us from detecting reset to send chirp-K */
+	mtu3_clrbits(mbase, U3D_LINK_RESET_INFO, WTCHRP_MSK);
+
+	/* U2/U3 detected by HW */
+	mtu3_writel(mbase, U3D_DEVICE_CONF, 0);
+
+	/* enable QMU 16B checksum */
+	mtu3_setbits(mbase, U3D_QCR0, QMU_CS16B_EN);
+
+	/* vbus detected by HW */
+	mtu3_clrbits(mbase, U3D_MISC_CTRL, VBUS_FRC_EN | VBUS_ON);
+}
+
+static irqreturn_t mtu3_link_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	enum usb_device_speed udev_speed;
+	u32 maxpkt = 64;
+	u32 link;
+	u32 speed;
+
+	link = mtu3_readl(mbase, U3D_DEV_LINK_INTR);
+	link &= mtu3_readl(mbase, U3D_DEV_LINK_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_DEV_LINK_INTR, link); /* W1C */
+	dev_dbg(mtu->dev, "=== LINK[%x] ===\n", link);
+
+	if (!(link & SSUSB_DEV_SPEED_CHG_INTR))
+		return IRQ_NONE;
+
+	speed = SSUSB_DEV_SPEED(mtu3_readl(mbase, U3D_DEVICE_CONF));
+
+	switch (speed) {
+	case MTU3_SPEED_FULL:
+		udev_speed = USB_SPEED_FULL;
+		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+				LPM_BESL_STALL | LPM_BESLD_STALL);
+		break;
+	case MTU3_SPEED_HIGH:
+		udev_speed = USB_SPEED_HIGH;
+		/*BESLCK = 4 < BESLCK_U3 = 10 < BESLDCK = 15 */
+		mtu3_writel(mbase, U3D_USB20_LPM_PARAMETER, LPM_BESLDCK(0xf)
+				| LPM_BESLCK(4) | LPM_BESLCK_U3(0xa));
+		mtu3_setbits(mbase, U3D_POWER_MANAGEMENT,
+				LPM_BESL_STALL | LPM_BESLD_STALL);
+		break;
+	case MTU3_SPEED_SUPER:
+		udev_speed = USB_SPEED_SUPER;
+		maxpkt = 512;
+		break;
+	default:
+		udev_speed = USB_SPEED_UNKNOWN;
+		break;
+	}
+	dev_dbg(mtu->dev, "%s: %s\n", __func__, usb_speed_string(udev_speed));
+
+	mtu->g.speed = udev_speed;
+	mtu->g.ep0->maxpacket = maxpkt;
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+	if (udev_speed == USB_SPEED_UNKNOWN)
+		mtu3_gadget_disconnect(mtu);
+	else
+		mtu3_ep0_setup(mtu);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u3_ltssm_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 ltssm;
+
+	ltssm = mtu3_readl(mbase, U3D_LTSSM_INTR);
+	ltssm &= mtu3_readl(mbase, U3D_LTSSM_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_LTSSM_INTR, ltssm); /* W1C */
+	dev_dbg(mtu->dev, "=== LTSSM[%x] ===\n", ltssm);
+
+	if (ltssm & HOT_RST_INTR)
+		mtu3_gadget_reset(mtu);
+
+	if (ltssm & WARM_RST_INTR)
+		mtu3_gadget_reset(mtu);
+
+	if (ltssm & VBUS_FALL_INTR)
+		mtu3_ss_func_set(mtu, false);
+
+	if (ltssm & VBUS_RISE_INTR)
+		mtu3_ss_func_set(mtu, true);
+
+	if (ltssm & EXIT_U3_INTR)
+		mtu3_gadget_resume(mtu);
+
+	if (ltssm & ENTER_U3_INTR)
+		mtu3_gadget_suspend(mtu);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtu3_u2_common_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 u2comm;
+
+	u2comm = mtu3_readl(mbase, U3D_COMMON_USB_INTR);
+	u2comm &= mtu3_readl(mbase, U3D_COMMON_USB_INTR_ENABLE);
+	mtu3_writel(mbase, U3D_COMMON_USB_INTR, u2comm); /* W1C */
+	dev_dbg(mtu->dev, "=== U2COMM[%x] ===\n", u2comm);
+
+	if (u2comm & SUSPEND_INTR)
+		mtu3_gadget_suspend(mtu);
+
+	if (u2comm & RESUME_INTR)
+		mtu3_gadget_resume(mtu);
+
+	if (u2comm & RESET_INTR)
+		mtu3_gadget_reset(mtu);
+
+	return IRQ_HANDLED;
+}
+
+irqreturn_t mtu3_irq(int irq, void *data)
+{
+	struct mtu3 *mtu = (struct mtu3 *)data;
+	unsigned long flags;
+	u32 level1;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	/* U3D_LV1ISR is RU */
+	level1 = mtu3_readl(mtu->mac_base, U3D_LV1ISR);
+	level1 &= mtu3_readl(mtu->mac_base, U3D_LV1IER);
+
+	if (level1 & EP_CTRL_INTR)
+		mtu3_link_isr(mtu);
+
+	if (level1 & MAC2_INTR)
+		mtu3_u2_common_isr(mtu);
+
+	if (level1 & MAC3_INTR)
+		mtu3_u3_ltssm_isr(mtu);
+
+	if (level1 & BMU_INTR)
+		mtu3_ep0_isr(mtu);
+
+	if (level1 & QMU_INTR)
+		mtu3_qmu_isr(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static int mtu3_hw_init(struct mtu3 *mtu)
+{
+	int ret;
+
+	mtu3_device_reset(mtu);
+
+	ret = mtu3_device_enable(mtu);
+	if (ret) {
+		dev_err(mtu->dev, "device enable failed %d\n", ret);
+		return ret;
+	}
+
+	ret = mtu3_mem_alloc(mtu);
+	if (ret) {
+		dev_err(mtu->dev, "mem alloc failed, aborting\n");
+		return -ENOMEM;
+	}
+
+	mtu3_regs_init(mtu);
+
+	return 0;
+}
+
+static void mtu3_hw_exit(struct mtu3 *mtu)
+{
+	mtu3_device_disable(mtu);
+	mtu3_mem_free(mtu);
+}
+
+/*-------------------------------------------------------------------------*/
+
+int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+	struct device *dev = ssusb->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct mtu3 *mtu = NULL;
+	struct resource *res;
+	int ret = -ENOMEM;
+
+	mtu = devm_kzalloc(dev, sizeof(struct mtu3), GFP_KERNEL);
+	if (mtu == NULL)
+		return -ENOMEM;
+
+	mtu->irq = platform_get_irq(pdev, 0);
+	if (mtu->irq <= 0) {
+		dev_err(dev, "fail to get irq number\n");
+		return -ENODEV;
+	}
+	dev_info(dev, "irq %d\n", mtu->irq);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mac");
+	mtu->mac_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(mtu->mac_base)) {
+		dev_err(dev, "error mapping memory for dev mac\n");
+		return PTR_ERR(mtu->mac_base);
+	}
+
+	spin_lock_init(&mtu->lock);
+	mtu->dev = dev;
+	mtu->ippc_base = ssusb->ippc_base;
+	ssusb->mac_base	= mtu->mac_base;
+	ssusb->u3d = mtu;
+	mtu->ssusb = ssusb;
+	mtu->max_speed = usb_get_maximum_speed(dev);
+
+	/* check the max_speed parameter */
+	switch (mtu->max_speed) {
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(dev, "invalid max_speed: %s\n",
+			usb_speed_string(mtu->max_speed));
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default as SS */
+		mtu->max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
+	dev_dbg(dev, "mac_base=0x%p, ippc_base=0x%p\n",
+		mtu->mac_base, mtu->ippc_base);
+
+	ret = mtu3_hw_init(mtu);
+	if (ret) {
+		dev_err(dev, "mtu3 hw init failed:%d\n", ret);
+		return ret;
+	}
+
+	ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu);
+	if (ret) {
+		dev_err(dev, "request irq %d failed!\n", mtu->irq);
+		goto irq_err;
+	}
+
+	device_init_wakeup(dev, true);
+
+	ret = mtu3_gadget_setup(mtu);
+	if (ret) {
+		dev_err(dev, "mtu3 gadget init failed:%d\n", ret);
+		goto gadget_err;
+	}
+
+	/* init as host mode, power down device IP for power saving */
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG)
+		mtu3_stop(mtu);
+
+	dev_dbg(dev, " %s() done...\n", __func__);
+
+	return 0;
+
+gadget_err:
+	device_init_wakeup(dev, false);
+
+irq_err:
+	mtu3_hw_exit(mtu);
+	ssusb->u3d = NULL;
+	dev_err(dev, " %s() fail...\n", __func__);
+
+	return ret;
+}
+
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{
+	struct mtu3 *mtu = ssusb->u3d;
+
+	mtu3_gadget_cleanup(mtu);
+	device_init_wakeup(ssusb->dev, false);
+	mtu3_hw_exit(mtu);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c
new file mode 100644
index 0000000..bb39723
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.c
@@ -0,0 +1,375 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/debugfs.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define USB2_PORT 2
+#define USB3_PORT 3
+
+enum mtu3_vbus_id_state {
+	MTU3_ID_FLOAT = 1,
+	MTU3_ID_GROUND,
+	MTU3_VBUS_OFF,
+	MTU3_VBUS_VALID,
+};
+
+static void toggle_opstate(struct ssusb_mtk *ssusb)
+{
+	if (!ssusb->otg_switch.is_u3_drd) {
+		mtu3_setbits(ssusb->mac_base, U3D_DEVICE_CONTROL, DC_SESSION);
+		mtu3_setbits(ssusb->mac_base, U3D_POWER_MANAGEMENT, SOFT_CONN);
+	}
+}
+
+/* only port0 supports dual-role mode */
+static int ssusb_port0_switch(struct ssusb_mtk *ssusb,
+	int version, bool tohost)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	u32 temp;
+
+	dev_dbg(ssusb->dev, "%s (switch u%d port0 to %s)\n", __func__,
+		version, tohost ? "host" : "device");
+
+	if (version == USB2_PORT) {
+		/* 1. power off and disable u2 port0 */
+		temp = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+		temp |= SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(0), temp);
+
+		/* 2. power on, enable u2 port0 and select its mode */
+		temp = mtu3_readl(ibase, SSUSB_U2_CTRL(0));
+		temp &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+		temp = tohost ? (temp | SSUSB_U2_PORT_HOST_SEL) :
+			(temp & (~SSUSB_U2_PORT_HOST_SEL));
+		mtu3_writel(ibase, SSUSB_U2_CTRL(0), temp);
+	} else {
+		/* 1. power off and disable u3 port0 */
+		temp = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+		temp |= SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(0), temp);
+
+		/* 2. power on, enable u3 port0 and select its mode */
+		temp = mtu3_readl(ibase, SSUSB_U3_CTRL(0));
+		temp &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+		temp = tohost ? (temp | SSUSB_U3_PORT_HOST_SEL) :
+			(temp & (~SSUSB_U3_PORT_HOST_SEL));
+		mtu3_writel(ibase, SSUSB_U3_CTRL(0), temp);
+	}
+
+	return 0;
+}
+
+static void switch_port_to_host(struct ssusb_mtk *ssusb)
+{
+	u32 check_clk = 0;
+
+	dev_dbg(ssusb->dev, "%s\n", __func__);
+
+	ssusb_port0_switch(ssusb, USB2_PORT, true);
+
+	if (ssusb->otg_switch.is_u3_drd) {
+		ssusb_port0_switch(ssusb, USB3_PORT, true);
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+	}
+
+	ssusb_check_clocks(ssusb, check_clk);
+
+	/* after all clocks are stable */
+	toggle_opstate(ssusb);
+}
+
+static void switch_port_to_device(struct ssusb_mtk *ssusb)
+{
+	u32 check_clk = 0;
+
+	dev_dbg(ssusb->dev, "%s\n", __func__);
+
+	ssusb_port0_switch(ssusb, USB2_PORT, false);
+
+	if (ssusb->otg_switch.is_u3_drd) {
+		ssusb_port0_switch(ssusb, USB3_PORT, false);
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+	}
+
+	ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct regulator *vbus = otg_sx->vbus;
+	int ret;
+
+	/* vbus is optional */
+	if (!vbus)
+		return 0;
+
+	dev_dbg(ssusb->dev, "%s: turn %s\n", __func__, is_on ? "on" : "off");
+
+	if (is_on) {
+		ret = regulator_enable(vbus);
+		if (ret) {
+			dev_err(ssusb->dev, "vbus regulator enable failed\n");
+			return ret;
+		}
+	} else {
+		regulator_disable(vbus);
+	}
+
+	return 0;
+}
+
+/*
+ * switch to host: -> MTU3_VBUS_OFF --> MTU3_ID_GROUND
+ * switch to device: -> MTU3_ID_FLOAT --> MTU3_VBUS_VALID
+ */
+static void ssusb_set_mailbox(struct otg_switch_mtk *otg_sx,
+	enum mtu3_vbus_id_state status)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct mtu3 *mtu = ssusb->u3d;
+
+	dev_dbg(ssusb->dev, "mailbox state(%d)\n", status);
+
+	switch (status) {
+	case MTU3_ID_GROUND:
+		switch_port_to_host(ssusb);
+		ssusb_set_vbus(otg_sx, 1);
+		ssusb->is_host = true;
+		break;
+	case MTU3_ID_FLOAT:
+		ssusb->is_host = false;
+		ssusb_set_vbus(otg_sx, 0);
+		switch_port_to_device(ssusb);
+		break;
+	case MTU3_VBUS_OFF:
+		mtu3_stop(mtu);
+		pm_relax(ssusb->dev);
+		break;
+	case MTU3_VBUS_VALID:
+		/* avoid suspend when works as device */
+		pm_stay_awake(ssusb->dev);
+		mtu3_start(mtu);
+		break;
+	default:
+		dev_err(ssusb->dev, "invalid state\n");
+	}
+}
+
+static int ssusb_id_notifier(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct otg_switch_mtk *otg_sx =
+		container_of(nb, struct otg_switch_mtk, id_nb);
+
+	if (event)
+		ssusb_set_mailbox(otg_sx, MTU3_ID_GROUND);
+	else
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+
+	return NOTIFY_DONE;
+}
+
+static int ssusb_vbus_notifier(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct otg_switch_mtk *otg_sx =
+		container_of(nb, struct otg_switch_mtk, vbus_nb);
+
+	if (event)
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+	else
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_OFF);
+
+	return NOTIFY_DONE;
+}
+
+static int ssusb_extcon_register(struct otg_switch_mtk *otg_sx)
+{
+	struct ssusb_mtk *ssusb =
+		container_of(otg_sx, struct ssusb_mtk, otg_switch);
+	struct extcon_dev *edev = otg_sx->edev;
+	int ret;
+
+	/* extcon is optional */
+	if (!edev)
+		return 0;
+
+	otg_sx->vbus_nb.notifier_call = ssusb_vbus_notifier;
+	ret = extcon_register_notifier(edev, EXTCON_USB,
+					&otg_sx->vbus_nb);
+	if (ret < 0)
+		dev_err(ssusb->dev, "failed to register notifier for USB\n");
+
+	otg_sx->id_nb.notifier_call = ssusb_id_notifier;
+	ret = extcon_register_notifier(edev, EXTCON_USB_HOST,
+					&otg_sx->id_nb);
+	if (ret < 0)
+		dev_err(ssusb->dev, "failed to register notifier for USB-HOST\n");
+
+	dev_dbg(ssusb->dev, "EXTCON_USB: %d, EXTCON_USB_HOST: %d\n",
+		extcon_get_cable_state_(edev, EXTCON_USB),
+		extcon_get_cable_state_(edev, EXTCON_USB_HOST));
+
+	/* default as host, switch to device mode if needed */
+	if (extcon_get_cable_state_(edev, EXTCON_USB_HOST) == false)
+		ssusb_set_mailbox(otg_sx, MTU3_ID_FLOAT);
+	if (extcon_get_cable_state_(edev, EXTCON_USB) == true)
+		ssusb_set_mailbox(otg_sx, MTU3_VBUS_VALID);
+
+	return 0;
+}
+
+static void extcon_register_dwork(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct otg_switch_mtk *otg_sx =
+	    container_of(dwork, struct otg_switch_mtk, extcon_reg_dwork);
+
+	ssusb_extcon_register(otg_sx);
+}
+
+/*
+ * We provide an interface via debugfs to switch between host and device modes
+ * depending on user input.
+ * This is useful in special cases, such as uses TYPE-A receptacle but also
+ * wants to support dual-role mode.
+ * It generates cable state changes by pulling up/down IDPIN and
+ * notifies driver to switch mode by "extcon-usb-gpio".
+ * NOTE: when use MICRO receptacle, should not enable this interface.
+ */
+static void ssusb_mode_manual_switch(struct ssusb_mtk *ssusb, int to_host)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	if (to_host)
+		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_ground);
+	else
+		pinctrl_select_state(otg_sx->id_pinctrl, otg_sx->id_float);
+}
+
+
+static int ssusb_mode_show(struct seq_file *sf, void *unused)
+{
+	struct ssusb_mtk *ssusb = sf->private;
+
+	seq_printf(sf, "current mode: %s(%s drd)\n(echo device/host)\n",
+		ssusb->is_host ? "host" : "device",
+		ssusb->otg_switch.manual_drd_enabled ? "manual" : "auto");
+
+	return 0;
+}
+
+static int ssusb_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ssusb_mode_show, inode->i_private);
+}
+
+static ssize_t ssusb_mode_write(struct file *file,
+	const char __user *ubuf, size_t count, loff_t *ppos)
+{
+	struct seq_file *sf = file->private_data;
+	struct ssusb_mtk *ssusb = sf->private;
+	char buf[16];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "host", 4) && !ssusb->is_host)
+		ssusb_mode_manual_switch(ssusb, 1);
+	else if (!strncmp(buf, "device", 6) && ssusb->is_host)
+		ssusb_mode_manual_switch(ssusb, 0);
+	else
+		dev_err(ssusb->dev, "wrong or duplicated setting\n");
+
+	return count;
+}
+
+static const struct file_operations ssusb_mode_fops = {
+	.open = ssusb_mode_open,
+	.write = ssusb_mode_write,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static void ssusb_debugfs_init(struct ssusb_mtk *ssusb)
+{
+	struct dentry *root;
+	struct dentry *file;
+
+	root = debugfs_create_dir(dev_name(ssusb->dev), usb_debug_root);
+	if (IS_ERR_OR_NULL(root)) {
+		if (!root)
+			dev_err(ssusb->dev, "create debugfs root failed\n");
+		return;
+	}
+	ssusb->dbgfs_root = root;
+
+	file = debugfs_create_file("mode", S_IRUGO | S_IWUSR, root,
+			ssusb, &ssusb_mode_fops);
+	if (!file)
+		dev_dbg(ssusb->dev, "create debugfs mode failed\n");
+}
+
+static void ssusb_debugfs_exit(struct ssusb_mtk *ssusb)
+{
+	debugfs_remove_recursive(ssusb->dbgfs_root);
+}
+
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	INIT_DELAYED_WORK(&otg_sx->extcon_reg_dwork, extcon_register_dwork);
+
+	if (otg_sx->manual_drd_enabled)
+		ssusb_debugfs_init(ssusb);
+
+	/* It is enough to delay 1s for waiting for host initialization */
+	schedule_delayed_work(&otg_sx->extcon_reg_dwork, HZ);
+
+	return 0;
+}
+
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	if (otg_sx->edev) {
+		extcon_unregister_notifier(otg_sx->edev,
+			EXTCON_USB, &otg_sx->vbus_nb);
+		extcon_unregister_notifier(otg_sx->edev,
+			EXTCON_USB_HOST, &otg_sx->id_nb);
+	}
+
+	if (otg_sx->manual_drd_enabled)
+		ssusb_debugfs_exit(ssusb);
+}
diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h
new file mode 100644
index 0000000..9b228b5
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_dr.h
@@ -0,0 +1,108 @@
+/*
+ * mtu3_dr.h - dual role switch and host glue layer header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 _MTU3_DR_H_
+#define _MTU3_DR_H_
+
+#if IS_ENABLED(CONFIG_USB_MTU3_HOST) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn);
+void ssusb_host_exit(struct ssusb_mtk *ssusb);
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+				struct device_node *dn);
+int ssusb_host_enable(struct ssusb_mtk *ssusb);
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend);
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb);
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb);
+
+#else
+
+static inline int ssusb_host_init(struct ssusb_mtk *ssusb,
+
+	struct device_node *parent_dn)
+{
+	return 0;
+}
+
+static inline void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_wakeup_of_property_parse(
+	struct ssusb_mtk *ssusb, struct device_node *dn)
+{
+	return 0;
+}
+
+static inline int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+	return 0;
+}
+
+static inline int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{}
+
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_GADGET) || IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_gadget_init(struct ssusb_mtk *ssusb);
+void ssusb_gadget_exit(struct ssusb_mtk *ssusb);
+#else
+static inline int ssusb_gadget_init(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_gadget_exit(struct ssusb_mtk *ssusb)
+{}
+#endif
+
+
+#if IS_ENABLED(CONFIG_USB_MTU3_DUAL_ROLE)
+int ssusb_otg_switch_init(struct ssusb_mtk *ssusb);
+void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb);
+int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on);
+
+#else
+
+static inline int ssusb_otg_switch_init(struct ssusb_mtk *ssusb)
+{
+	return 0;
+}
+
+static inline void ssusb_otg_switch_exit(struct ssusb_mtk *ssusb)
+{}
+
+static inline int ssusb_set_vbus(struct otg_switch_mtk *otg_sx, int is_on)
+{
+	return 0;
+}
+
+#endif
+
+#endif		/* _MTU3_DR_H_ */
diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
new file mode 100644
index 0000000..2a255e7
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -0,0 +1,731 @@
+/*
+ * mtu3_gadget.c - MediaTek usb3 DRD peripheral support
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 "mtu3.h"
+
+void mtu3_req_complete(struct mtu3_ep *mep,
+		     struct usb_request *req, int status)
+__releases(mep->mtu->lock)
+__acquires(mep->mtu->lock)
+{
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	int busy = mep->busy;
+
+	mreq = to_mtu3_request(req);
+	list_del(&mreq->list);
+	if (mreq->request.status == -EINPROGRESS)
+		mreq->request.status = status;
+
+	mtu = mreq->mtu;
+	mep->busy = 1;
+	spin_unlock(&mtu->lock);
+
+	/* ep0 makes use of PIO, needn't unmap it */
+	if (mep->epnum)
+		usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
+
+	dev_dbg(mtu->dev, "%s complete req: %p, sts %d, %d/%d\n", mep->name,
+		req, req->status, mreq->request.actual, mreq->request.length);
+
+	usb_gadget_giveback_request(&mep->ep, &mreq->request);
+
+	spin_lock(&mtu->lock);
+	mep->busy = busy;
+}
+
+static void nuke(struct mtu3_ep *mep, const int status)
+{
+	struct mtu3_request *mreq = NULL;
+
+	mep->busy = 1;
+	if (list_empty(&mep->req_list))
+		return;
+
+	dev_dbg(mep->mtu->dev, "abort %s's req: sts %d\n", mep->name, status);
+
+	/* exclude EP0 */
+	if (mep->epnum)
+		mtu3_qmu_flush(mep);
+
+	while (!list_empty(&mep->req_list)) {
+		mreq = list_first_entry(&mep->req_list,
+					struct mtu3_request, list);
+		mtu3_req_complete(mep, &mreq->request, status);
+	}
+}
+
+static int mtu3_ep_enable(struct mtu3_ep *mep)
+{
+	const struct usb_endpoint_descriptor *desc;
+	const struct usb_ss_ep_comp_descriptor *comp_desc;
+	struct mtu3 *mtu = mep->mtu;
+	u32 interval = 0;
+	u32 multi = 0;
+	u32 burst = 0;
+	int max_packet;
+	int ret;
+
+	desc = mep->desc;
+	comp_desc = mep->comp_desc;
+	mep->type = usb_endpoint_type(desc);
+	max_packet = usb_endpoint_maxp(desc);
+	mep->maxp = max_packet & GENMASK(10, 0);
+
+	switch (mtu->g.speed) {
+	case USB_SPEED_SUPER:
+		if (usb_endpoint_xfer_int(desc) ||
+				usb_endpoint_xfer_isoc(desc)) {
+			interval = desc->bInterval;
+			interval = clamp_val(interval, 1, 16) - 1;
+			if (usb_endpoint_xfer_isoc(desc) && comp_desc)
+				multi = comp_desc->bmAttributes;
+		}
+		if (comp_desc)
+			burst = comp_desc->bMaxBurst;
+
+		break;
+	case USB_SPEED_HIGH:
+		if (usb_endpoint_xfer_isoc(desc) ||
+				usb_endpoint_xfer_int(desc)) {
+			interval = desc->bInterval;
+			interval = clamp_val(interval, 1, 16) - 1;
+			burst = (max_packet & GENMASK(12, 11)) >> 11;
+		}
+		break;
+	default:
+		break; /*others are ignored */
+	}
+
+	dev_dbg(mtu->dev, "%s maxp:%d, interval:%d, burst:%d, multi:%d\n",
+		__func__, mep->maxp, interval, burst, multi);
+
+	mep->ep.maxpacket = mep->maxp;
+	mep->ep.desc = desc;
+	mep->ep.comp_desc = comp_desc;
+
+	/* slot mainly affects bulk/isoc transfer, so ignore int */
+	mep->slot = usb_endpoint_xfer_int(desc) ? 0 : mtu->slot;
+
+	ret = mtu3_config_ep(mtu, mep, interval, burst, multi);
+	if (ret < 0)
+		return ret;
+
+	ret = mtu3_gpd_ring_alloc(mep);
+	if (ret < 0) {
+		mtu3_deconfig_ep(mtu, mep);
+		return ret;
+	}
+
+	mtu3_qmu_start(mep);
+
+	return 0;
+}
+
+static int mtu3_ep_disable(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+
+	mtu3_qmu_stop(mep);
+
+	/* abort all pending requests */
+	nuke(mep, -ESHUTDOWN);
+	mtu3_deconfig_ep(mtu, mep);
+	mtu3_gpd_ring_free(mep);
+
+	mep->desc = NULL;
+	mep->ep.desc = NULL;
+	mep->comp_desc = NULL;
+	mep->type = 0;
+	mep->flags = 0;
+
+	return 0;
+}
+
+static int mtu3_gadget_ep_enable(struct usb_ep *ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_debug("%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		pr_debug("%s missing wMaxPacketSize\n", __func__);
+		return -EINVAL;
+	}
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+
+	/* check ep number and direction against endpoint */
+	if (usb_endpoint_num(desc) != mep->epnum)
+		return -EINVAL;
+
+	if (!!usb_endpoint_dir_in(desc) ^ !!mep->is_in)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s %s\n", __func__, ep->name);
+
+	if (mep->flags & MTU3_EP_ENABLED) {
+		dev_WARN_ONCE(mtu->dev, true, "%s is already enabled\n",
+				mep->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	mep->desc = desc;
+	mep->comp_desc = ep->comp_desc;
+
+	ret = mtu3_ep_enable(mep);
+	if (ret)
+		goto error;
+
+	mep->busy = 0;
+	mep->wedged = 0;
+	mep->flags |= MTU3_EP_ENABLED;
+	mtu->active_ep++;
+
+error:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	dev_dbg(mtu->dev, "%s active_ep=%d\n", __func__, mtu->active_ep);
+
+	return ret;
+}
+
+static int mtu3_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3 *mtu = mep->mtu;
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s %s\n", __func__, mep->name);
+
+	if (!(mep->flags & MTU3_EP_ENABLED)) {
+		dev_warn(mtu->dev, "%s is already disabled\n", mep->name);
+		return 0;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	mtu3_ep_disable(mep);
+	mep->flags &= ~MTU3_EP_ENABLED;
+	mtu->active_ep--;
+	spin_unlock_irqrestore(&(mtu->lock), flags);
+
+	dev_dbg(mtu->dev, "%s active_ep=%d, mtu3 is_active=%d\n",
+		__func__, mtu->active_ep, mtu->is_active);
+
+	return 0;
+}
+
+struct usb_request *mtu3_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3_request *mreq;
+
+	mreq = kzalloc(sizeof(*mreq), gfp_flags);
+	if (!mreq)
+		return NULL;
+
+	mreq->request.dma = DMA_ADDR_INVALID;
+	mreq->epnum = mep->epnum;
+	mreq->mep = mep;
+
+	return &mreq->request;
+}
+
+void mtu3_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+	kfree(to_mtu3_request(req));
+}
+
+static int mtu3_gadget_queue(struct usb_ep *ep,
+		struct usb_request *req, gfp_t gfp_flags)
+{
+	struct mtu3_ep *mep;
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !req)
+		return -EINVAL;
+
+	if (!req->buf)
+		return -ENODATA;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+	mreq = to_mtu3_request(req);
+	mreq->mtu = mtu;
+
+	if (mreq->mep != mep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s %s EP%d(%s), req=%p, maxp=%d, len#%d\n",
+		__func__, mep->is_in ? "TX" : "RX", mreq->epnum, ep->name,
+		mreq, ep->maxpacket, mreq->request.length);
+
+	if (req->length > GPD_BUF_SIZE) {
+		dev_warn(mtu->dev,
+			"req length > supported MAX:%d requested:%d\n",
+			GPD_BUF_SIZE, req->length);
+		return -EOPNOTSUPP;
+	}
+
+	/* don't queue if the ep is down */
+	if (!mep->desc) {
+		dev_dbg(mtu->dev, "req=%p queued to %s while it's disabled\n",
+			req, ep->name);
+		return -ESHUTDOWN;
+	}
+
+	mreq->request.actual = 0;
+	mreq->request.status = -EINPROGRESS;
+
+	ret = usb_gadget_map_request(&mtu->g, req, mep->is_in);
+	if (ret) {
+		dev_err(mtu->dev, "dma mapping failed\n");
+		return ret;
+	}
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (mtu3_prepare_transfer(mep)) {
+		ret = -EAGAIN;
+		goto error;
+	}
+
+	list_add_tail(&mreq->list, &mep->req_list);
+	mtu3_insert_gpd(mep, mreq);
+	mtu3_qmu_resume(mep);
+
+error:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+static int mtu3_gadget_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3_request *mreq = to_mtu3_request(req);
+	struct mtu3_request *r;
+	unsigned long flags;
+	int ret = 0;
+	struct mtu3 *mtu = mep->mtu;
+
+	if (!ep || !req || mreq->mep != mep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s : req=%p\n", __func__, req);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	list_for_each_entry(r, &mep->req_list, list) {
+		if (r == mreq)
+			break;
+	}
+	if (r != mreq) {
+		dev_dbg(mtu->dev, "req=%p not queued to %s\n", req, ep->name);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mtu3_qmu_flush(mep);  /* REVISIT: set BPS ?? */
+	mtu3_req_complete(mep, req, -ECONNRESET);
+	mtu3_qmu_start(mep);
+
+done:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+/*
+ * Set or clear the halt bit of an EP.
+ * A halted EP won't TX/RX any data but will queue requests.
+ */
+static int mtu3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+	struct mtu3 *mtu = mep->mtu;
+	struct mtu3_request *mreq;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep)
+		return -EINVAL;
+
+	dev_dbg(mtu->dev, "%s : %s...", __func__, ep->name);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (mep->type == USB_ENDPOINT_XFER_ISOC) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mreq = next_request(mep);
+	if (value) {
+		/*
+		 * If there is not request for TX-EP, QMU will not transfer
+		 * data to TX-FIFO, so no need check whether TX-FIFO
+		 * holds bytes or not here
+		 */
+		if (mreq) {
+			dev_dbg(mtu->dev, "req in progress, cannot halt %s\n",
+				ep->name);
+			ret = -EAGAIN;
+			goto done;
+		}
+	} else {
+		mep->wedged = 0;
+	}
+
+	dev_dbg(mtu->dev, "%s %s stall\n", ep->name, value ? "set" : "clear");
+
+	mtu3_ep_stall_set(mep, value);
+
+done:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return ret;
+}
+
+/* Sets the halt feature with the clear requests ignored */
+static int mtu3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct mtu3_ep *mep = to_mtu3_ep(ep);
+
+	if (!ep)
+		return -EINVAL;
+
+	mep->wedged = 1;
+
+	return usb_ep_set_halt(ep);
+}
+
+static const struct usb_ep_ops mtu3_ep_ops = {
+	.enable = mtu3_gadget_ep_enable,
+	.disable = mtu3_gadget_ep_disable,
+	.alloc_request = mtu3_alloc_request,
+	.free_request = mtu3_free_request,
+	.queue = mtu3_gadget_queue,
+	.dequeue = mtu3_gadget_dequeue,
+	.set_halt = mtu3_gadget_ep_set_halt,
+	.set_wedge = mtu3_gadget_ep_set_wedge,
+};
+
+static int mtu3_gadget_get_frame(struct usb_gadget *gadget)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+	return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM);
+}
+
+static int mtu3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	/* remote wakeup feature not enabled by host */
+	if (!mtu->may_wakeup)
+		return  -EOPNOTSUPP;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	if (mtu->g.speed == USB_SPEED_SUPER) {
+		mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT);
+	} else {
+		mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+		spin_unlock_irqrestore(&mtu->lock, flags);
+		usleep_range(10000, 11000);
+		spin_lock_irqsave(&mtu->lock, flags);
+		mtu3_clrbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME);
+	}
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return 0;
+}
+
+static int mtu3_gadget_set_self_powered(struct usb_gadget *gadget,
+		int is_selfpowered)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+
+	mtu->is_self_powered = !!is_selfpowered;
+	return 0;
+}
+
+static int mtu3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s (%s) for %sactive device\n", __func__,
+		is_on ? "on" : "off", mtu->is_active ? "" : "in");
+
+	/* we'd rather not pullup unless the device is active. */
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	is_on = !!is_on;
+	if (mtu->is_active == 0) {
+		/* save it for mtu3_start() to process the request */
+		mtu->softconnect = is_on;
+	} else if (is_on != mtu->softconnect) {
+		mtu->softconnect = is_on;
+		mtu3_dev_on_off(mtu, is_on);
+	}
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static int mtu3_gadget_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(gadget);
+	unsigned long flags;
+
+	if (mtu->gadget_driver) {
+		dev_err(mtu->dev, "%s is already bound to %s\n",
+			mtu->g.name, mtu->gadget_driver->driver.name);
+		return -EBUSY;
+	}
+
+	dev_dbg(mtu->dev, "bind driver %s\n", driver->function);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	mtu->softconnect = 0;
+	mtu->gadget_driver = driver;
+
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		mtu3_start(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static void stop_activity(struct mtu3 *mtu)
+{
+	struct usb_gadget_driver *driver = mtu->gadget_driver;
+	int i;
+
+	/* don't disconnect if it's not connected */
+	if (mtu->g.speed == USB_SPEED_UNKNOWN)
+		driver = NULL;
+	else
+		mtu->g.speed = USB_SPEED_UNKNOWN;
+
+	/* deactivate the hardware */
+	if (mtu->softconnect) {
+		mtu->softconnect = 0;
+		mtu3_dev_on_off(mtu, 0);
+	}
+
+	/*
+	 * killing any outstanding requests will quiesce the driver;
+	 * then report disconnect
+	 */
+	nuke(mtu->ep0, -ESHUTDOWN);
+	for (i = 1; i < mtu->num_eps; i++) {
+		nuke(mtu->in_eps + i, -ESHUTDOWN);
+		nuke(mtu->out_eps + i, -ESHUTDOWN);
+	}
+
+	if (driver) {
+		spin_unlock(&mtu->lock);
+		driver->disconnect(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+static int mtu3_gadget_stop(struct usb_gadget *g)
+{
+	struct mtu3 *mtu = gadget_to_mtu3(g);
+	unsigned long flags;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	stop_activity(mtu);
+	mtu->gadget_driver = NULL;
+
+	if (mtu->ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		mtu3_stop(mtu);
+
+	spin_unlock_irqrestore(&mtu->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops mtu3_gadget_ops = {
+	.get_frame = mtu3_gadget_get_frame,
+	.wakeup = mtu3_gadget_wakeup,
+	.set_selfpowered = mtu3_gadget_set_self_powered,
+	.pullup = mtu3_gadget_pullup,
+	.udc_start = mtu3_gadget_start,
+	.udc_stop = mtu3_gadget_stop,
+};
+
+static void init_hw_ep(struct mtu3 *mtu, struct mtu3_ep *mep,
+		u32 epnum, u32 is_in)
+{
+	mep->epnum = epnum;
+	mep->mtu = mtu;
+	mep->is_in = is_in;
+
+	INIT_LIST_HEAD(&mep->req_list);
+
+	sprintf(mep->name, "ep%d%s", epnum,
+		!epnum ? "" : (is_in ? "in" : "out"));
+
+	mep->ep.name = mep->name;
+	INIT_LIST_HEAD(&mep->ep.ep_list);
+
+	/* initialize maxpacket as SS */
+	if (!epnum) {
+		usb_ep_set_maxpacket_limit(&mep->ep, 512);
+		mep->ep.caps.type_control = true;
+		mep->ep.ops = &mtu3_ep0_ops;
+		mtu->g.ep0 = &mep->ep;
+	} else {
+		usb_ep_set_maxpacket_limit(&mep->ep, 1024);
+		mep->ep.caps.type_iso = true;
+		mep->ep.caps.type_bulk = true;
+		mep->ep.caps.type_int = true;
+		mep->ep.ops = &mtu3_ep_ops;
+		list_add_tail(&mep->ep.ep_list, &mtu->g.ep_list);
+	}
+
+	dev_dbg(mtu->dev, "%s, name=%s, maxp=%d\n", __func__, mep->ep.name,
+		 mep->ep.maxpacket);
+
+	if (!epnum) {
+		mep->ep.caps.dir_in = true;
+		mep->ep.caps.dir_out = true;
+	} else if (is_in) {
+		mep->ep.caps.dir_in = true;
+	} else {
+		mep->ep.caps.dir_out = true;
+	}
+}
+
+static void mtu3_gadget_init_eps(struct mtu3 *mtu)
+{
+	u8 epnum;
+
+	/* initialize endpoint list just once */
+	INIT_LIST_HEAD(&(mtu->g.ep_list));
+
+	dev_dbg(mtu->dev, "%s num_eps(1 for a pair of tx&rx ep)=%d\n",
+		__func__, mtu->num_eps);
+
+	init_hw_ep(mtu, mtu->ep0, 0, 0);
+	for (epnum = 1; epnum < mtu->num_eps; epnum++) {
+		init_hw_ep(mtu, mtu->in_eps + epnum, epnum, 1);
+		init_hw_ep(mtu, mtu->out_eps + epnum, epnum, 0);
+	}
+}
+
+int mtu3_gadget_setup(struct mtu3 *mtu)
+{
+	int ret;
+
+	mtu->g.ops = &mtu3_gadget_ops;
+	mtu->g.max_speed = mtu->max_speed;
+	mtu->g.speed = USB_SPEED_UNKNOWN;
+	mtu->g.sg_supported = 0;
+	mtu->g.name = MTU3_DRIVER_NAME;
+	mtu->is_active = 0;
+
+	mtu3_gadget_init_eps(mtu);
+
+	ret = usb_add_gadget_udc(mtu->dev, &mtu->g);
+	if (ret) {
+		dev_err(mtu->dev, "failed to register udc\n");
+		return ret;
+	}
+
+	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+
+	return 0;
+}
+
+void mtu3_gadget_cleanup(struct mtu3 *mtu)
+{
+	usb_del_gadget_udc(&mtu->g);
+}
+
+void mtu3_gadget_resume(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget RESUME\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->resume) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->resume(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+/* called when SOF packets stop for 3+ msec */
+void mtu3_gadget_suspend(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget SUSPEND\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->suspend) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->suspend(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+}
+
+/* called when VBUS drops below session threshold, and in other cases */
+void mtu3_gadget_disconnect(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "gadget DISCONNECT\n");
+	if (mtu->gadget_driver && mtu->gadget_driver->disconnect) {
+		spin_unlock(&mtu->lock);
+		mtu->gadget_driver->disconnect(&mtu->g);
+		spin_lock(&mtu->lock);
+	}
+
+	usb_gadget_set_state(&mtu->g, USB_STATE_NOTATTACHED);
+}
+
+void mtu3_gadget_reset(struct mtu3 *mtu)
+{
+	dev_dbg(mtu->dev, "HS RESET\n");
+
+	/* report disconnect, if we didn't flush EP state */
+	if (mtu->g.speed != USB_SPEED_UNKNOWN)
+		mtu3_gadget_disconnect(mtu);
+
+	mtu->address = 0;
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+	mtu->may_wakeup = 0;
+}
+
diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c
new file mode 100644
index 0000000..14bff37
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c
@@ -0,0 +1,879 @@
+/*
+ * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ *
+ * Author:  Chunfeng.Yun <chunfeng.yun at mediatek.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 "mtu3.h"
+
+/* ep0 is always mtu3->in_eps[0] */
+#define	next_ep0_request(mtu)	next_request((mtu)->ep0)
+
+/* for high speed test mode; see USB 2.0 spec 7.1.20 */
+static const u8 mtu3_test_packet[53] = {
+	/* implicit SYNC then DATA0 to start */
+
+	/* JKJKJKJK x9 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	/* JJKKJJKK x8 */
+	0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+	/* JJJJKKKK x8 */
+	0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+	/* JJJJJJJKKKKKKK x8 */
+	0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	/* JJJJJJJK x8 */
+	0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd,
+	/* JKKKKKKK x10, JK */
+	0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e,
+	/* implicit CRC16 then EOP to end */
+};
+
+static char *decode_ep0_state(struct mtu3 *mtu)
+{
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_SETUP:
+		return "SETUP";
+	case MU3D_EP0_STATE_TX:
+		return "IN";
+	case MU3D_EP0_STATE_RX:
+		return "OUT";
+	case MU3D_EP0_STATE_TX_END:
+		return "TX-END";
+	case MU3D_EP0_STATE_STALL:
+		return "STALL";
+	default:
+		return "??";
+	}
+}
+
+static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req)
+{
+	mtu3_req_complete(mtu->ep0, req, 0);
+}
+
+static int
+forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+	int ret;
+
+	if (!mtu->gadget_driver)
+		return -EOPNOTSUPP;
+
+	spin_unlock(&mtu->lock);
+	ret = mtu->gadget_driver->setup(&mtu->g, setup);
+	spin_lock(&mtu->lock);
+
+	dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret);
+	return ret;
+}
+
+static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len)
+{
+	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+	u16 index = 0;
+
+	dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n",
+		__func__, mep->epnum, len, src);
+
+	if (len >= 4) {
+		iowrite32_rep(fifo, src, len >> 2);
+		index = len & ~0x03;
+	}
+	if (len & 0x02) {
+		writew(*(u16 *)&src[index], fifo);
+		index += 2;
+	}
+	if (len & 0x01)
+		writeb(src[index], fifo);
+}
+
+static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len)
+{
+	void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0;
+	u32 temp;
+	u16 index = 0;
+
+	dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n",
+		 __func__, mep->epnum, len, dst);
+
+	if (len >= 4) {
+		ioread32_rep(fifo, dst, len >> 2);
+		index = len & ~0x03;
+	}
+	if (len & 0x3) {
+		temp = readl(fifo);
+		memcpy(&dst[index], &temp, len & 0x3);
+	}
+
+}
+
+static void ep0_load_test_packet(struct mtu3 *mtu)
+{
+	/*
+	 * because the length of test packet is less than max packet of HS ep0,
+	 * write it into fifo directly.
+	 */
+	ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet));
+}
+
+/*
+ * A. send STALL for setup transfer without data stage:
+ *		set SENDSTALL and SETUPPKTRDY at the same time;
+ * B. send STALL for other cases:
+ *		set SENDSTALL only.
+ */
+static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy)
+{
+	struct mtu3 *mtu = mep0->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	u32 csr;
+
+	/* EP0_SENTSTALL is W1C */
+	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+	if (set)
+		csr |= EP0_SENDSTALL | pktrdy;
+	else
+		csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr);
+
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+	dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n",
+		set ? "SEND" : "CLEAR", decode_ep0_state(mtu));
+}
+
+static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq);
+
+static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req)
+{}
+
+static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	struct usb_set_sel_req sel;
+
+	memcpy(&sel, req->buf, sizeof(sel));
+
+	mreq = to_mtu3_request(req);
+	mtu = mreq->mtu;
+	dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n",
+		sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel);
+}
+
+/* queue data stage to handle 6 byte SET_SEL request */
+static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	int ret;
+
+	if (unlikely(setup->wLength != 6)) {
+		dev_err(mtu->dev, "%s wrong wLength:%d\n",
+			__func__, setup->wLength);
+		return -EINVAL;
+	}
+
+	mtu->ep0_req.mep = mtu->ep0;
+	mtu->ep0_req.request.length = 6;
+	mtu->ep0_req.request.buf = mtu->setup_buf;
+	mtu->ep0_req.request.complete = ep0_set_sel_complete;
+	ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+
+	return ret < 0 ? ret : 1;
+}
+
+static int
+ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup)
+{
+	struct mtu3_ep *mep = NULL;
+	int handled = 1;
+	u8 result[2] = {0, 0};
+	u8 epnum = 0;
+	int is_in;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED;
+		result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
+		/* superspeed only */
+		if (mtu->g.speed == USB_SPEED_SUPER) {
+			result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED;
+			result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED;
+		}
+
+		dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__,
+			result[0], mtu->u1_enable, mtu->u2_enable);
+
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = (u8) setup->wIndex;
+		is_in = epnum & USB_DIR_IN;
+		epnum &= USB_ENDPOINT_NUMBER_MASK;
+
+		if (epnum >= mtu->num_eps) {
+			handled = -EINVAL;
+			break;
+		}
+		if (!epnum)
+			break;
+
+		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+		if (!mep->desc) {
+			handled = -EINVAL;
+			break;
+		}
+
+		if (mep->flags & MTU3_EP_STALL)
+			result[0] |= 1 << USB_ENDPOINT_HALT;
+
+		break;
+	default:
+		/* class, vendor, etc ... delegate */
+		handled = 0;
+		break;
+	}
+
+	if (handled > 0) {
+		int ret;
+
+		/* prepare a data stage for GET_STATUS */
+		dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result);
+		memcpy(mtu->setup_buf, result, sizeof(result));
+		mtu->ep0_req.mep = mtu->ep0;
+		mtu->ep0_req.request.length = 2;
+		mtu->ep0_req.request.buf = &mtu->setup_buf;
+		mtu->ep0_req.request.complete = ep0_dummy_complete;
+		ret = ep0_queue(mtu->ep0, &mtu->ep0_req);
+		if (ret < 0)
+			handled = ret;
+	}
+	return handled;
+}
+
+static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int handled = 1;
+
+	switch (setup->wIndex >> 8) {
+	case TEST_J:
+		dev_dbg(mtu->dev, "TEST_J\n");
+		mtu->test_mode_nr = TEST_J_MODE;
+		break;
+	case TEST_K:
+		dev_dbg(mtu->dev, "TEST_K\n");
+		mtu->test_mode_nr = TEST_K_MODE;
+		break;
+	case TEST_SE0_NAK:
+		dev_dbg(mtu->dev, "TEST_SE0_NAK\n");
+		mtu->test_mode_nr = TEST_SE0_NAK_MODE;
+		break;
+	case TEST_PACKET:
+		dev_dbg(mtu->dev, "TEST_PACKET\n");
+		mtu->test_mode_nr = TEST_PACKET_MODE;
+		break;
+	default:
+		handled = -EINVAL;
+		goto out;
+	}
+
+	mtu->test_mode = true;
+
+	/* no TX completion interrupt, and need restart platform after test */
+	if (mtu->test_mode_nr == TEST_PACKET_MODE)
+		ep0_load_test_packet(mtu);
+
+	mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr);
+
+	mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+
+out:
+	return handled;
+}
+
+static int ep0_handle_feature_dev(struct mtu3 *mtu,
+		struct usb_ctrlrequest *setup, bool set)
+{
+	void __iomem *mbase = mtu->mac_base;
+	int handled = -EINVAL;
+	u32 lpc;
+
+	switch (setup->wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		mtu->may_wakeup = !!set;
+		handled = 1;
+		break;
+	case USB_DEVICE_TEST_MODE:
+		if (!set || (mtu->g.speed != USB_SPEED_HIGH) ||
+			(setup->wIndex & 0xff))
+			break;
+
+		handled = handle_test_mode(mtu, setup);
+		break;
+	case USB_DEVICE_U1_ENABLE:
+		if (mtu->g.speed != USB_SPEED_SUPER ||
+			mtu->g.state != USB_STATE_CONFIGURED)
+			break;
+
+		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+		if (set)
+			lpc |= SW_U1_ACCEPT_ENABLE;
+		else
+			lpc &= ~SW_U1_ACCEPT_ENABLE;
+		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+		mtu->u1_enable = !!set;
+		handled = 1;
+		break;
+	case USB_DEVICE_U2_ENABLE:
+		if (mtu->g.speed != USB_SPEED_SUPER ||
+			mtu->g.state != USB_STATE_CONFIGURED)
+			break;
+
+		lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL);
+		if (set)
+			lpc |= SW_U2_ACCEPT_ENABLE;
+		else
+			lpc &= ~SW_U2_ACCEPT_ENABLE;
+		mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc);
+
+		mtu->u2_enable = !!set;
+		handled = 1;
+		break;
+	default:
+		handled = -EINVAL;
+		break;
+	}
+	return handled;
+}
+
+static int ep0_handle_feature(struct mtu3 *mtu,
+		struct usb_ctrlrequest *setup, bool set)
+{
+	struct mtu3_ep *mep;
+	int handled = -EINVAL;
+	int is_in;
+	u8 epnum;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		handled = ep0_handle_feature_dev(mtu, setup, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		/* superspeed only */
+		if ((setup->wValue == USB_INTRF_FUNC_SUSPEND)
+			&& (mtu->g.speed == USB_SPEED_SUPER)) {
+			/*
+			 * forward the request because function drivers
+			 * should handle it
+			 */
+			handled = 0;
+		}
+		break;
+	case USB_RECIP_ENDPOINT:
+		epnum = setup->wIndex & USB_ENDPOINT_NUMBER_MASK;
+		if (epnum == 0 || epnum >= mtu->num_eps ||
+			setup->wValue != USB_ENDPOINT_HALT)
+			break;
+
+		is_in = setup->wIndex & USB_DIR_IN;
+		mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum;
+		if (!mep->desc)
+			break;
+
+		handled = 1;
+		/* ignore request if endpoint is wedged */
+		if (mep->wedged)
+			break;
+
+		mtu3_ep_stall_set(mep, set);
+		break;
+	default:
+		/* class, vendor, etc ... delegate */
+		handled = 0;
+		break;
+	}
+	return handled;
+}
+
+/*
+ * handle all control requests can be handled
+ * returns:
+ *	negative errno - error happened
+ *	zero - need delegate SETUP to gadget driver
+ *	positive - already handled
+ */
+static int handle_standard_request(struct mtu3 *mtu,
+			  struct usb_ctrlrequest *setup)
+{
+	void __iomem *mbase = mtu->mac_base;
+	enum usb_device_state state = mtu->g.state;
+	int handled = -EINVAL;
+	u32 dev_conf;
+
+	/* the gadget driver handles everything except what we must handle */
+	switch (setup->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		/* change it after the status stage */
+		mtu->address = (u8) (setup->wValue & 0x7f);
+		dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address);
+
+		dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF);
+		dev_conf &= ~DEV_ADDR_MSK;
+		dev_conf |= DEV_ADDR(mtu->address);
+		mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf);
+
+		if (mtu->address)
+			usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS);
+		else
+			usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT);
+
+		handled = 1;
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		if (state == USB_STATE_ADDRESS) {
+			usb_gadget_set_state(&mtu->g,
+					USB_STATE_CONFIGURED);
+		} else if (state == USB_STATE_CONFIGURED) {
+			/*
+			 * USB2 spec sec 9.4.7, if wValue is 0 then dev
+			 * is moved to addressed state
+			 */
+			if (!setup->wValue)
+				usb_gadget_set_state(&mtu->g,
+						USB_STATE_ADDRESS);
+		}
+		handled = 0;
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		handled = ep0_handle_feature(mtu, setup, 0);
+		break;
+	case USB_REQ_SET_FEATURE:
+		handled = ep0_handle_feature(mtu, setup, 1);
+		break;
+	case USB_REQ_GET_STATUS:
+		handled = ep0_get_status(mtu, setup);
+		break;
+	case USB_REQ_SET_SEL:
+		handled = ep0_set_sel(mtu, setup);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		handled = 1;
+		break;
+	default:
+		/* delegate SET_CONFIGURATION, etc */
+		handled = 0;
+	}
+
+	return handled;
+}
+
+/* receive an data packet (OUT) */
+static void ep0_rx_state(struct mtu3 *mtu)
+{
+	struct mtu3_request *mreq;
+	struct usb_request *req;
+	void __iomem *mbase = mtu->mac_base;
+	u32 maxp;
+	u32 csr;
+	u16 count = 0;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS;
+	mreq = next_ep0_request(mtu);
+	req = &mreq->request;
+
+	/* read packet and ack; or stall because of gadget driver bug */
+	if (req) {
+		void *buf = req->buf + req->actual;
+		unsigned int len = req->length - req->actual;
+
+		/* read the buffer */
+		count = mtu3_readl(mbase, U3D_RXCOUNT0);
+		if (count > len) {
+			req->status = -EOVERFLOW;
+			count = len;
+		}
+		ep0_read_fifo(mtu->ep0, buf, count);
+		req->actual += count;
+		csr |= EP0_RXPKTRDY;
+
+		maxp = mtu->g.ep0->maxpacket;
+		if (count < maxp || req->actual == req->length) {
+			mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+			dev_dbg(mtu->dev, "ep0 state: %s\n",
+				decode_ep0_state(mtu));
+
+			csr |= EP0_DATAEND;
+		} else {
+			req = NULL;
+		}
+	} else {
+		csr |= EP0_RXPKTRDY | EP0_SENDSTALL;
+		dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__);
+	}
+
+	mtu3_writel(mbase, U3D_EP0CSR, csr);
+
+	/* give back the request if have received all data */
+	if (req)
+		ep0_req_giveback(mtu, req);
+
+}
+
+/* transmitting to the host (IN) */
+static void ep0_tx_state(struct mtu3 *mtu)
+{
+	struct mtu3_request *mreq = next_ep0_request(mtu);
+	struct usb_request *req;
+	u32 csr;
+	u8 *src;
+	u8 count;
+	u32 maxp;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	if (!mreq)
+		return;
+
+	maxp = mtu->g.ep0->maxpacket;
+	req = &mreq->request;
+
+	/* load the data */
+	src = (u8 *)req->buf + req->actual;
+	count = min(maxp, req->length - req->actual);
+	if (count)
+		ep0_write_fifo(mtu->ep0, src, count);
+
+	dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n",
+		 __func__, req->actual, req->length, count, maxp, req->zero);
+
+	req->actual += count;
+
+	if ((count < maxp)
+		|| ((req->actual == req->length) && !req->zero))
+		mtu->ep0_state = MU3D_EP0_STATE_TX_END;
+
+	/* send it out, triggering a "txpktrdy cleared" irq */
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+	mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY);
+
+	dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__,
+		mtu3_readl(mtu->mac_base, U3D_EP0CSR));
+}
+
+/*
+ * USB2 spec sec8.1, multiple byte fields in standard descriptors, requests,
+ * and responses are interpreted as and moved over the bus in little-endian
+ * order, and MTK SoCs based on ARM is also little-endian, so no need
+ * to convert setup data temp. If need, will add it later
+ */
+static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup)
+{
+	struct mtu3_request *mreq;
+	u32 count;
+	u32 csr;
+
+	csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS;
+	count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0);
+
+	ep0_read_fifo(mtu->ep0, (u8 *)setup, count);
+
+	dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n",
+		 setup->bRequestType, setup->bRequest, setup->wValue,
+		 setup->wIndex, setup->wLength);
+
+	/* clean up any leftover transfers */
+	mreq = next_ep0_request(mtu);
+	if (mreq)
+		ep0_req_giveback(mtu, &mreq->request);
+
+	if (setup->wLength == 0) {
+		;	/* no data stage, nothing to do */
+	} else if (setup->bRequestType & USB_DIR_IN) {
+		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+			csr | EP0_SETUPPKTRDY | EP0_DPHTX);
+		mtu->ep0_state = MU3D_EP0_STATE_TX;
+	} else {
+		mtu3_writel(mtu->mac_base, U3D_EP0CSR,
+			(csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX));
+		mtu->ep0_state = MU3D_EP0_STATE_RX;
+	}
+}
+
+static int ep0_handle_setup(struct mtu3 *mtu)
+__releases(mtu->lock)
+__acquires(mtu->lock)
+{
+	struct usb_ctrlrequest setup;
+	struct mtu3_request *mreq;
+	void __iomem *mbase = mtu->mac_base;
+	int handled = 0;
+
+	ep0_read_setup(mtu, &setup);
+
+	if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		handled = handle_standard_request(mtu, &setup);
+
+	dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n",
+		 handled, decode_ep0_state(mtu));
+
+	if (handled < 0)
+		goto stall;
+	else if (handled > 0)
+		goto finish;
+
+	handled = forward_to_driver(mtu, &setup);
+	if (handled < 0) {
+stall:
+		dev_err(mtu->dev, "%s stall (%d)\n", __func__, handled);
+
+		ep0_stall_set(mtu->ep0, true,
+				setup.wLength ? 0 : EP0_SETUPPKTRDY);
+
+		return 0;
+	}
+
+finish:
+	if (mtu->test_mode) {
+		;	/* nothing to do */
+	} else if (setup.wLength == 0) { /* no data stage */
+
+		mtu3_writel(mbase, U3D_EP0CSR,
+			(mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS)
+			| EP0_SETUPPKTRDY | EP0_DATAEND);
+
+		/* complete zlp request directly */
+		mreq = next_ep0_request(mtu);
+		if (mreq && !mreq->request.length)
+			ep0_req_giveback(mtu, &mreq->request);
+	}
+
+	return 0;
+}
+
+irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_request *mreq;
+	u32 int_status;
+	irqreturn_t ret = IRQ_NONE;
+	u32 csr;
+	u32 len;
+
+	int_status = mtu3_readl(mbase, U3D_EPISR);
+	int_status &= mtu3_readl(mbase, U3D_EPIER);
+	mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */
+
+	/* only handle ep0's */
+	if (!(int_status & EP0ISR))
+		return IRQ_NONE;
+
+	csr = mtu3_readl(mbase, U3D_EP0CSR);
+
+	dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr);
+
+	/* we sent a stall.. need to clear it now.. */
+	if (csr & EP0_SENTSTALL) {
+		ep0_stall_set(mtu->ep0, false, 0);
+		csr = mtu3_readl(mbase, U3D_EP0CSR);
+		ret = IRQ_HANDLED;
+	}
+	dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_TX:
+		/* irq on clearing txpktrdy */
+		if ((csr & EP0_FIFOFULL) == 0) {
+			ep0_tx_state(mtu);
+			ret = IRQ_HANDLED;
+		}
+		break;
+	case MU3D_EP0_STATE_RX:
+		/* irq on set rxpktrdy */
+		if (csr & EP0_RXPKTRDY) {
+			ep0_rx_state(mtu);
+			ret = IRQ_HANDLED;
+		}
+		break;
+	case MU3D_EP0_STATE_TX_END:
+		mtu3_writel(mbase, U3D_EP0CSR,
+			(csr & EP0_W1C_BITS) | EP0_DATAEND);
+
+		mreq = next_ep0_request(mtu);
+		if (mreq)
+			ep0_req_giveback(mtu, &mreq->request);
+
+		mtu->ep0_state = MU3D_EP0_STATE_SETUP;
+		ret = IRQ_HANDLED;
+		dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu));
+		break;
+	case MU3D_EP0_STATE_SETUP:
+		if (!(csr & EP0_SETUPPKTRDY))
+			break;
+
+		len = mtu3_readl(mbase, U3D_RXCOUNT0);
+		if (len != 8) {
+			dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len);
+			break;
+		}
+
+		ep0_handle_setup(mtu);
+		ret = IRQ_HANDLED;
+		break;
+	default:
+		/* can't happen */
+		ep0_stall_set(mtu->ep0, true, 0);
+		WARN_ON(1);
+		break;
+	}
+
+	return ret;
+}
+
+
+static int mtu3_ep0_enable(struct usb_ep *ep,
+	const struct usb_endpoint_descriptor *desc)
+{
+	/* always enabled */
+	return -EINVAL;
+}
+
+static int mtu3_ep0_disable(struct usb_ep *ep)
+{
+	/* always enabled */
+	return -EINVAL;
+}
+
+static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct mtu3 *mtu = mep->mtu;
+
+	mreq->mtu = mtu;
+	mreq->request.actual = 0;
+	mreq->request.status = -EINPROGRESS;
+
+	dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__,
+		mep->name, decode_ep0_state(mtu), mreq->request.length);
+
+	if (!list_empty(&mep->req_list))
+		return -EBUSY;
+
+	switch (mtu->ep0_state) {
+	case MU3D_EP0_STATE_SETUP:
+	case MU3D_EP0_STATE_RX:	/* control-OUT data */
+	case MU3D_EP0_STATE_TX:	/* control-IN data */
+		break;
+	default:
+		dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__,
+			decode_ep0_state(mtu));
+		return -EINVAL;
+	}
+
+	list_add_tail(&mreq->list, &mep->req_list);
+
+	/* sequence #1, IN ... start writing the data */
+	if (mtu->ep0_state == MU3D_EP0_STATE_TX)
+		ep0_tx_state(mtu);
+
+	return 0;
+}
+
+static int mtu3_ep0_queue(struct usb_ep *ep,
+	struct usb_request *req, gfp_t gfp)
+{
+	struct mtu3_ep *mep;
+	struct mtu3_request *mreq;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !req)
+		return -EINVAL;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+	mreq = to_mtu3_request(req);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+	ret = ep0_queue(mep, mreq);
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return ret;
+}
+
+static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	/* we just won't support this */
+	return -EINVAL;
+}
+
+static int mtu3_ep0_halt(struct usb_ep *ep, int value)
+{
+	struct mtu3_ep *mep;
+	struct mtu3 *mtu;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !value)
+		return -EINVAL;
+
+	mep = to_mtu3_ep(ep);
+	mtu = mep->mtu;
+
+	dev_dbg(mtu->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	if (!list_empty(&mep->req_list)) {
+		ret = -EBUSY;
+		goto cleanup;
+	}
+
+	switch (mtu->ep0_state) {
+	/*
+	 * stalls are usually issued after parsing SETUP packet, either
+	 * directly in irq context from setup() or else later.
+	 */
+	case MU3D_EP0_STATE_TX:
+	case MU3D_EP0_STATE_TX_END:
+	case MU3D_EP0_STATE_RX:
+	case MU3D_EP0_STATE_SETUP:
+		ep0_stall_set(mtu->ep0, true, 0);
+		break;
+	default:
+		dev_dbg(mtu->dev, "ep0 can't halt in state %s\n",
+			decode_ep0_state(mtu));
+		ret = -EINVAL;
+	}
+
+cleanup:
+	spin_unlock_irqrestore(&mtu->lock, flags);
+	return ret;
+}
+
+const struct usb_ep_ops mtu3_ep0_ops = {
+	.enable = mtu3_ep0_enable,
+	.disable = mtu3_ep0_disable,
+	.alloc_request = mtu3_alloc_request,
+	.free_request = mtu3_free_request,
+	.queue = mtu3_ep0_queue,
+	.dequeue = mtu3_ep0_dequeue,
+	.set_halt = mtu3_ep0_halt,
+};
+
diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c
new file mode 100644
index 0000000..373c0c4
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_host.c
@@ -0,0 +1,294 @@
+/*
+ * mtu3_dr.c - dual role switch and host glue layer
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/clk.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+#define PERI_WK_CTRL1		0x404
+#define UWK_CTL1_IS_C(x)	(((x) & 0xf) << 26)
+#define UWK_CTL1_IS_E		BIT(25)
+#define UWK_CTL1_IDDIG_C(x)	(((x) & 0xf) << 11)  /* cycle debounce */
+#define UWK_CTL1_IDDIG_E	BIT(10) /* enable debounce */
+#define UWK_CTL1_IDDIG_P	BIT(9)  /* polarity */
+#define UWK_CTL1_IS_P		BIT(6)  /* polarity for ip sleep */
+
+/*
+ * ip-sleep wakeup mode:
+ * all clocks can be turn off, but power domain should be kept on
+ */
+static void ssusb_wakeup_ip_sleep_en(struct ssusb_mtk *ssusb)
+{
+	u32 tmp;
+	struct regmap *pericfg = ssusb->pericfg;
+
+	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+	tmp &= ~UWK_CTL1_IS_P;
+	tmp &= ~(UWK_CTL1_IS_C(0xf));
+	tmp |= UWK_CTL1_IS_C(0x8);
+	regmap_write(pericfg, PERI_WK_CTRL1, tmp);
+	regmap_write(pericfg, PERI_WK_CTRL1, tmp | UWK_CTL1_IS_E);
+
+	regmap_read(pericfg, PERI_WK_CTRL1, &tmp);
+	dev_dbg(ssusb->dev, "%s(): WK_CTRL1[P6,E25,C26:29]=%#x\n",
+		__func__, tmp);
+}
+
+static void ssusb_wakeup_ip_sleep_dis(struct ssusb_mtk *ssusb)
+{
+	u32 tmp;
+
+	regmap_read(ssusb->pericfg, PERI_WK_CTRL1, &tmp);
+	tmp &= ~UWK_CTL1_IS_E;
+	regmap_write(ssusb->pericfg, PERI_WK_CTRL1, tmp);
+}
+
+int ssusb_wakeup_of_property_parse(struct ssusb_mtk *ssusb,
+				struct device_node *dn)
+{
+	struct device *dev = ssusb->dev;
+
+	/*
+	 * Wakeup function is optional, so it is not an error if this property
+	 * does not exist, and in such case, no need to get relative
+	 * properties anymore.
+	 */
+	ssusb->wakeup_en = of_property_read_bool(dn, "mediatek,enable-wakeup");
+	if (!ssusb->wakeup_en)
+		return 0;
+
+	ssusb->wk_deb_p0 = devm_clk_get(dev, "wakeup_deb_p0");
+	if (IS_ERR(ssusb->wk_deb_p0)) {
+		dev_err(dev, "fail to get wakeup_deb_p0\n");
+		return PTR_ERR(ssusb->wk_deb_p0);
+	}
+
+	if (of_property_read_bool(dn, "wakeup_deb_p1")) {
+		ssusb->wk_deb_p1 = devm_clk_get(dev, "wakeup_deb_p1");
+		if (IS_ERR(ssusb->wk_deb_p1)) {
+			dev_err(dev, "fail to get wakeup_deb_p1\n");
+			return PTR_ERR(ssusb->wk_deb_p1);
+		}
+	}
+
+	ssusb->pericfg = syscon_regmap_lookup_by_phandle(dn,
+						"mediatek,syscon-wakeup");
+	if (IS_ERR(ssusb->pericfg)) {
+		dev_err(dev, "fail to get pericfg regs\n");
+		return PTR_ERR(ssusb->pericfg);
+	}
+
+	return 0;
+}
+
+static int ssusb_wakeup_clks_enable(struct ssusb_mtk *ssusb)
+{
+	int ret;
+
+	ret = clk_prepare_enable(ssusb->wk_deb_p0);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable wk_deb_p0\n");
+		goto usb_p0_err;
+	}
+
+	ret = clk_prepare_enable(ssusb->wk_deb_p1);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable wk_deb_p1\n");
+		goto usb_p1_err;
+	}
+
+	return 0;
+
+usb_p1_err:
+	clk_disable_unprepare(ssusb->wk_deb_p0);
+usb_p0_err:
+	return -EINVAL;
+}
+
+static void ssusb_wakeup_clks_disable(struct ssusb_mtk *ssusb)
+{
+	clk_disable_unprepare(ssusb->wk_deb_p1);
+	clk_disable_unprepare(ssusb->wk_deb_p0);
+}
+
+static void host_ports_num_get(struct ssusb_mtk *ssusb)
+{
+	u32 xhci_cap;
+
+	xhci_cap = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP);
+	ssusb->u2_ports = SSUSB_IP_XHCI_U2_PORT_NUM(xhci_cap);
+	ssusb->u3_ports = SSUSB_IP_XHCI_U3_PORT_NUM(xhci_cap);
+
+	dev_dbg(ssusb->dev, "host - u2_ports:%d, u3_ports:%d\n",
+		 ssusb->u2_ports, ssusb->u3_ports);
+}
+
+/* only configure ports will be used later */
+int ssusb_host_enable(struct ssusb_mtk *ssusb)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	int num_u3p = ssusb->u3_ports;
+	int num_u2p = ssusb->u2_ports;
+	u32 check_clk;
+	u32 value;
+	int i;
+
+	/* power on host ip */
+	mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+	/* power on and enable all u3 ports */
+	for (i = 0; i < num_u3p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+		value &= ~(SSUSB_U3_PORT_PDN | SSUSB_U3_PORT_DIS);
+		value |= SSUSB_U3_PORT_HOST_SEL;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+	}
+
+	/* power on and enable all u2 ports */
+	for (i = 0; i < num_u2p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+		value &= ~(SSUSB_U2_PORT_PDN | SSUSB_U2_PORT_DIS);
+		value |= SSUSB_U2_PORT_HOST_SEL;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+	}
+
+	check_clk = SSUSB_XHCI_RST_B_STS;
+	if (num_u3p)
+		check_clk = SSUSB_U3_MAC_RST_B_STS;
+
+	return ssusb_check_clocks(ssusb, check_clk);
+}
+
+int ssusb_host_disable(struct ssusb_mtk *ssusb, bool suspend)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	int num_u3p = ssusb->u3_ports;
+	int num_u2p = ssusb->u2_ports;
+	u32 value;
+	int ret;
+	int i;
+
+	/* power down and disable all u3 ports */
+	for (i = 0; i < num_u3p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U3_CTRL(i));
+		value |= SSUSB_U3_PORT_PDN;
+		value |= suspend ? 0 : SSUSB_U3_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U3_CTRL(i), value);
+	}
+
+	/* power down and disable all u2 ports */
+	for (i = 0; i < num_u2p; i++) {
+		value = mtu3_readl(ibase, SSUSB_U2_CTRL(i));
+		value |= SSUSB_U2_PORT_PDN;
+		value |= suspend ? 0 : SSUSB_U2_PORT_DIS;
+		mtu3_writel(ibase, SSUSB_U2_CTRL(i), value);
+	}
+
+	/* power down host ip */
+	mtu3_setbits(ibase, U3D_SSUSB_IP_PW_CTRL1, SSUSB_IP_HOST_PDN);
+
+	if (!suspend)
+		return 0;
+
+	/* wait for host ip to sleep */
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+			  (value & SSUSB_IP_SLEEP_STS), 100, 100000);
+	if (ret)
+		dev_err(ssusb->dev, "ip sleep failed!!!\n");
+
+	return ret;
+}
+
+static void ssusb_host_setup(struct ssusb_mtk *ssusb)
+{
+	host_ports_num_get(ssusb);
+
+	/*
+	 * power on host and power on/enable all ports
+	 * if support OTG, gadget driver will switch port0 to device mode
+	 */
+	ssusb_host_enable(ssusb);
+
+	/* if port0 supports dual-role, works as host mode by default */
+	ssusb_set_vbus(&ssusb->otg_switch, 1);
+}
+
+static void ssusb_host_cleanup(struct ssusb_mtk *ssusb)
+{
+	if (ssusb->is_host)
+		ssusb_set_vbus(&ssusb->otg_switch, 0);
+
+	ssusb_host_disable(ssusb, false);
+}
+
+/*
+ * If host supports multiple ports, the VBUSes(5V) of ports except port0
+ * when it supports OTG are better to be enabled by default in DTS.
+ * Because the host driver will keep link with devices attached when system
+ * enters suspend mode, so no need to control VBUSes after initialization.
+ */
+int ssusb_host_init(struct ssusb_mtk *ssusb, struct device_node *parent_dn)
+{
+	struct device *parent_dev = ssusb->dev;
+	int ret;
+
+	ssusb_host_setup(ssusb);
+
+	ret = of_platform_populate(parent_dn, NULL, NULL, parent_dev);
+	if (ret) {
+		dev_dbg(parent_dev, "failed to create child devices at %s\n",
+				parent_dn->full_name);
+		return ret;
+	}
+
+	dev_info(parent_dev, "xHCI platform device register success...\n");
+
+	return 0;
+}
+
+void ssusb_host_exit(struct ssusb_mtk *ssusb)
+{
+	of_platform_depopulate(ssusb->dev);
+	ssusb_host_cleanup(ssusb);
+}
+
+int ssusb_wakeup_enable(struct ssusb_mtk *ssusb)
+{
+	int ret = 0;
+
+	if (ssusb->wakeup_en) {
+		ret = ssusb_wakeup_clks_enable(ssusb);
+		ssusb_wakeup_ip_sleep_en(ssusb);
+	}
+	return ret;
+}
+
+void ssusb_wakeup_disable(struct ssusb_mtk *ssusb)
+{
+	if (ssusb->wakeup_en) {
+		ssusb_wakeup_ip_sleep_dis(ssusb);
+		ssusb_wakeup_clks_disable(ssusb);
+	}
+}
diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h
new file mode 100644
index 0000000..713a75c
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_hw_regs.h
@@ -0,0 +1,474 @@
+/*
+ * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 _SSUSB_HW_REGS_H_
+#define _SSUSB_HW_REGS_H_
+
+/* segment offset of MAC register */
+#define SSUSB_DEV_BASE		0x0000
+#define SSUSB_EPCTL_CSR_BASE	0x0800
+#define SSUSB_USB3_MAC_CSR_BASE	0x1400
+#define SSUSB_USB3_SYS_CSR_BASE	0x1400
+#define SSUSB_USB2_CSR_BASE	0x2400
+
+/* IPPC register in Infra */
+#define SSUSB_SIFSLV_IPPC_BASE	0x0000
+
+/* --------------- SSUSB_DEV REGISTER DEFINITION --------------- */
+
+#define U3D_LV1ISR		(SSUSB_DEV_BASE + 0x0000)
+#define U3D_LV1IER		(SSUSB_DEV_BASE + 0x0004)
+#define U3D_LV1IESR		(SSUSB_DEV_BASE + 0x0008)
+#define U3D_LV1IECR		(SSUSB_DEV_BASE + 0x000C)
+
+#define U3D_EPISR		(SSUSB_DEV_BASE + 0x0080)
+#define U3D_EPIER		(SSUSB_DEV_BASE + 0x0084)
+#define U3D_EPIESR		(SSUSB_DEV_BASE + 0x0088)
+#define U3D_EPIECR		(SSUSB_DEV_BASE + 0x008C)
+
+#define U3D_EP0CSR		(SSUSB_DEV_BASE + 0x0100)
+#define U3D_RXCOUNT0		(SSUSB_DEV_BASE + 0x0108)
+#define U3D_RESERVED		(SSUSB_DEV_BASE + 0x010C)
+#define U3D_TX1CSR0		(SSUSB_DEV_BASE + 0x0110)
+#define U3D_TX1CSR1		(SSUSB_DEV_BASE + 0x0114)
+#define U3D_TX1CSR2		(SSUSB_DEV_BASE + 0x0118)
+
+#define U3D_RX1CSR0		(SSUSB_DEV_BASE + 0x0210)
+#define U3D_RX1CSR1		(SSUSB_DEV_BASE + 0x0214)
+#define U3D_RX1CSR2		(SSUSB_DEV_BASE + 0x0218)
+#define U3D_RX1CSR3		(SSUSB_DEV_BASE + 0x021C)
+
+#define U3D_FIFO0		(SSUSB_DEV_BASE + 0x0300)
+
+#define U3D_QCR0		(SSUSB_DEV_BASE + 0x0400)
+#define U3D_QCR1		(SSUSB_DEV_BASE + 0x0404)
+#define U3D_QCR2		(SSUSB_DEV_BASE + 0x0408)
+#define U3D_QCR3		(SSUSB_DEV_BASE + 0x040C)
+
+#define U3D_TXQCSR1		(SSUSB_DEV_BASE + 0x0510)
+#define U3D_TXQSAR1		(SSUSB_DEV_BASE + 0x0514)
+#define U3D_TXQCPR1		(SSUSB_DEV_BASE + 0x0518)
+
+#define U3D_RXQCSR1		(SSUSB_DEV_BASE + 0x0610)
+#define U3D_RXQSAR1		(SSUSB_DEV_BASE + 0x0614)
+#define U3D_RXQCPR1		(SSUSB_DEV_BASE + 0x0618)
+#define U3D_RXQLDPR1		(SSUSB_DEV_BASE + 0x061C)
+
+#define U3D_QISAR0		(SSUSB_DEV_BASE + 0x0700)
+#define U3D_QIER0		(SSUSB_DEV_BASE + 0x0704)
+#define U3D_QIESR0		(SSUSB_DEV_BASE + 0x0708)
+#define U3D_QIECR0		(SSUSB_DEV_BASE + 0x070C)
+#define U3D_QISAR1		(SSUSB_DEV_BASE + 0x0710)
+#define U3D_QIER1		(SSUSB_DEV_BASE + 0x0714)
+#define U3D_QIESR1		(SSUSB_DEV_BASE + 0x0718)
+#define U3D_QIECR1		(SSUSB_DEV_BASE + 0x071C)
+
+#define U3D_TQERRIR0		(SSUSB_DEV_BASE + 0x0780)
+#define U3D_TQERRIER0		(SSUSB_DEV_BASE + 0x0784)
+#define U3D_TQERRIESR0		(SSUSB_DEV_BASE + 0x0788)
+#define U3D_TQERRIECR0		(SSUSB_DEV_BASE + 0x078C)
+#define U3D_RQERRIR0		(SSUSB_DEV_BASE + 0x07C0)
+#define U3D_RQERRIER0		(SSUSB_DEV_BASE + 0x07C4)
+#define U3D_RQERRIESR0		(SSUSB_DEV_BASE + 0x07C8)
+#define U3D_RQERRIECR0		(SSUSB_DEV_BASE + 0x07CC)
+#define U3D_RQERRIR1		(SSUSB_DEV_BASE + 0x07D0)
+#define U3D_RQERRIER1		(SSUSB_DEV_BASE + 0x07D4)
+#define U3D_RQERRIESR1		(SSUSB_DEV_BASE + 0x07D8)
+#define U3D_RQERRIECR1		(SSUSB_DEV_BASE + 0x07DC)
+
+#define U3D_CAP_EP0FFSZ		(SSUSB_DEV_BASE + 0x0C04)
+#define U3D_CAP_EPNTXFFSZ	(SSUSB_DEV_BASE + 0x0C08)
+#define U3D_CAP_EPNRXFFSZ	(SSUSB_DEV_BASE + 0x0C0C)
+#define U3D_CAP_EPINFO		(SSUSB_DEV_BASE + 0x0C10)
+#define U3D_MISC_CTRL		(SSUSB_DEV_BASE + 0x0C84)
+
+/*---------------- SSUSB_DEV FIELD DEFINITION ---------------*/
+
+/* U3D_LV1ISR */
+#define EP_CTRL_INTR		BIT(5)
+#define MAC2_INTR		BIT(4)
+#define DMA_INTR		BIT(3)
+#define MAC3_INTR		BIT(2)
+#define QMU_INTR		BIT(1)
+#define BMU_INTR		BIT(0)
+
+/* U3D_LV1IECR */
+#define LV1IECR_MSK		GENMASK(31, 0)
+
+/* U3D_EPISR */
+#define EPRISR(x)		(BIT(16) << (x))
+#define EPTISR(x)		(BIT(0) << (x))
+#define EP0ISR			BIT(0)
+
+/* U3D_EP0CSR */
+#define EP0_SENDSTALL		BIT(25)
+#define EP0_FIFOFULL		BIT(23)
+#define EP0_SENTSTALL		BIT(22)
+#define EP0_DPHTX		BIT(20)
+#define EP0_DATAEND		BIT(19)
+#define EP0_TXPKTRDY		BIT(18)
+#define EP0_SETUPPKTRDY		BIT(17)
+#define EP0_RXPKTRDY		BIT(16)
+#define EP0_MAXPKTSZ_MSK	GENMASK(9, 0)
+#define EP0_MAXPKTSZ(x)		((x) & EP0_MAXPKTSZ_MSK)
+#define EP0_W1C_BITS	(~(EP0_RXPKTRDY | EP0_SETUPPKTRDY | EP0_SENTSTALL))
+
+/* U3D_TX1CSR0 */
+#define TX_DMAREQEN		BIT(29)
+#define TX_FIFOFULL		BIT(25)
+#define TX_FIFOEMPTY		BIT(24)
+#define TX_SENTSTALL		BIT(22)
+#define TX_SENDSTALL		BIT(21)
+#define TX_TXPKTRDY		BIT(16)
+#define TX_TXMAXPKTSZ_MSK	GENMASK(10, 0)
+#define TX_TXMAXPKTSZ(x)	((x) & TX_TXMAXPKTSZ_MSK)
+#define TX_W1C_BITS		(~(TX_SENTSTALL))
+
+/* U3D_TX1CSR1 */
+#define TX_MULT(x)		(((x) & 0x3) << 22)
+#define TX_MAX_PKT(x)		(((x) & 0x3f) << 16)
+#define TX_SLOT(x)		(((x) & 0x3f) << 8)
+#define TX_TYPE(x)		(((x) & 0x3) << 4)
+#define TX_SS_BURST(x)		(((x) & 0xf) << 0)
+
+/* for TX_TYPE & RX_TYPE */
+#define TYPE_BULK		(0x0)
+#define TYPE_INT		(0x1)
+#define TYPE_ISO		(0x2)
+#define TYPE_MASK		(0x3)
+
+/* U3D_TX1CSR2 */
+#define TX_BINTERVAL(x)		(((x) & 0xff) << 24)
+#define TX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
+#define TX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
+
+/* U3D_RX1CSR0 */
+#define RX_DMAREQEN		BIT(29)
+#define RX_SENTSTALL		BIT(22)
+#define RX_SENDSTALL		BIT(21)
+#define RX_RXPKTRDY		BIT(16)
+#define RX_RXMAXPKTSZ_MSK	GENMASK(10, 0)
+#define RX_RXMAXPKTSZ(x)	((x) & RX_RXMAXPKTSZ_MSK)
+#define RX_W1C_BITS		(~(RX_SENTSTALL | RX_RXPKTRDY))
+
+/* U3D_RX1CSR1 */
+#define RX_MULT(x)		(((x) & 0x3) << 22)
+#define RX_MAX_PKT(x)		(((x) & 0x3f) << 16)
+#define RX_SLOT(x)		(((x) & 0x3f) << 8)
+#define RX_TYPE(x)		(((x) & 0x3) << 4)
+#define RX_SS_BURST(x)		(((x) & 0xf) << 0)
+
+/* U3D_RX1CSR2 */
+#define RX_BINTERVAL(x)		(((x) & 0xff) << 24)
+#define RX_FIFOSEGSIZE(x)	(((x) & 0xf) << 16)
+#define RX_FIFOADDR(x)		(((x) & 0x1fff) << 0)
+
+/* U3D_QCR0 */
+#define QMU_RX_CS_EN(x)		(BIT(16) << (x))
+#define QMU_TX_CS_EN(x)		(BIT(0) << (x))
+#define QMU_CS16B_EN		BIT(0)
+
+/* U3D_QCR1 */
+#define QMU_TX_ZLP(x)		(BIT(0) << (x))
+
+/* U3D_QCR3 */
+#define QMU_RX_COZ(x)		(BIT(16) << (x))
+#define QMU_RX_ZLP(x)		(BIT(0) << (x))
+
+/* U3D_TXQCSR1 */
+/* U3D_RXQCSR1 */
+#define QMU_Q_ACTIVE		BIT(15)
+#define QMU_Q_STOP		BIT(2)
+#define QMU_Q_RESUME		BIT(1)
+#define QMU_Q_START		BIT(0)
+
+/* U3D_QISAR0, U3D_QIER0, U3D_QIESR0, U3D_QIECR0 */
+#define QMU_RX_DONE_INT(x)	(BIT(16) << (x))
+#define QMU_TX_DONE_INT(x)	(BIT(0) << (x))
+
+/* U3D_QISAR1, U3D_QIER1, U3D_QIESR1, U3D_QIECR1 */
+#define RXQ_ZLPERR_INT		BIT(20)
+#define RXQ_LENERR_INT		BIT(18)
+#define RXQ_CSERR_INT		BIT(17)
+#define RXQ_EMPTY_INT		BIT(16)
+#define TXQ_LENERR_INT		BIT(2)
+#define TXQ_CSERR_INT		BIT(1)
+#define TXQ_EMPTY_INT		BIT(0)
+
+/* U3D_TQERRIR0, U3D_TQERRIER0, U3D_TQERRIESR0, U3D_TQERRIECR0 */
+#define QMU_TX_LEN_ERR(x)	(BIT(16) << (x))
+#define QMU_TX_CS_ERR(x)	(BIT(0) << (x))
+
+/* U3D_RQERRIR0, U3D_RQERRIER0, U3D_RQERRIESR0, U3D_RQERRIECR0 */
+#define QMU_RX_LEN_ERR(x)	(BIT(16) << (x))
+#define QMU_RX_CS_ERR(x)	(BIT(0) << (x))
+
+/* U3D_RQERRIR1, U3D_RQERRIER1, U3D_RQERRIESR1, U3D_RQERRIECR1 */
+#define QMU_RX_ZLP_ERR(n)	(BIT(16) << (n))
+
+/* U3D_CAP_EPINFO */
+#define CAP_RX_EP_NUM(x)	(((x) >> 8) & 0x1f)
+#define CAP_TX_EP_NUM(x)	((x) & 0x1f)
+
+/* U3D_MISC_CTRL */
+#define VBUS_ON			BIT(1)
+#define VBUS_FRC_EN		BIT(0)
+
+
+/*---------------- SSUSB_EPCTL_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_DEVICE_CONF			(SSUSB_EPCTL_CSR_BASE + 0x0000)
+#define U3D_EP_RST			(SSUSB_EPCTL_CSR_BASE + 0x0004)
+
+#define U3D_DEV_LINK_INTR_ENABLE	(SSUSB_EPCTL_CSR_BASE + 0x0050)
+#define U3D_DEV_LINK_INTR		(SSUSB_EPCTL_CSR_BASE + 0x0054)
+
+/*---------------- SSUSB_EPCTL_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_DEVICE_CONF */
+#define DEV_ADDR_MSK		GENMASK(30, 24)
+#define DEV_ADDR(x)		((0x7f & (x)) << 24)
+#define HW_USB2_3_SEL		BIT(18)
+#define SW_USB2_3_SEL_EN	BIT(17)
+#define SW_USB2_3_SEL		BIT(16)
+#define SSUSB_DEV_SPEED(x)	((x) & 0x7)
+
+/* U3D_EP_RST */
+#define EP1_IN_RST		BIT(17)
+#define EP1_OUT_RST		BIT(1)
+#define EP_RST(is_in, epnum)	(((is_in) ? BIT(16) : BIT(0)) << (epnum))
+#define EP0_RST			BIT(0)
+
+/* U3D_DEV_LINK_INTR_ENABLE */
+/* U3D_DEV_LINK_INTR */
+#define SSUSB_DEV_SPEED_CHG_INTR	BIT(0)
+
+
+/*---------------- SSUSB_USB3_MAC_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LTSSM_CTRL		(SSUSB_USB3_MAC_CSR_BASE + 0x0010)
+#define U3D_USB3_CONFIG		(SSUSB_USB3_MAC_CSR_BASE + 0x001C)
+
+#define U3D_LTSSM_INTR_ENABLE	(SSUSB_USB3_MAC_CSR_BASE + 0x013C)
+#define U3D_LTSSM_INTR		(SSUSB_USB3_MAC_CSR_BASE + 0x0140)
+
+/*---------------- SSUSB_USB3_MAC_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LTSSM_CTRL */
+#define FORCE_POLLING_FAIL	BIT(4)
+#define FORCE_RXDETECT_FAIL	BIT(3)
+#define SOFT_U3_EXIT_EN		BIT(2)
+#define COMPLIANCE_EN		BIT(1)
+#define U1_GO_U2_EN		BIT(0)
+
+/* U3D_USB3_CONFIG */
+#define USB3_EN			BIT(0)
+
+/* U3D_LTSSM_INTR_ENABLE */
+/* U3D_LTSSM_INTR */
+#define U3_RESUME_INTR		BIT(18)
+#define U3_LFPS_TMOUT_INTR	BIT(17)
+#define VBUS_FALL_INTR		BIT(16)
+#define VBUS_RISE_INTR		BIT(15)
+#define RXDET_SUCCESS_INTR	BIT(14)
+#define EXIT_U3_INTR		BIT(13)
+#define EXIT_U2_INTR		BIT(12)
+#define EXIT_U1_INTR		BIT(11)
+#define ENTER_U3_INTR		BIT(10)
+#define ENTER_U2_INTR		BIT(9)
+#define ENTER_U1_INTR		BIT(8)
+#define ENTER_U0_INTR		BIT(7)
+#define RECOVERY_INTR		BIT(6)
+#define WARM_RST_INTR		BIT(5)
+#define HOT_RST_INTR		BIT(4)
+#define LOOPBACK_INTR		BIT(3)
+#define COMPLIANCE_INTR		BIT(2)
+#define SS_DISABLE_INTR		BIT(1)
+#define SS_INACTIVE_INTR	BIT(0)
+
+/*---------------- SSUSB_USB3_SYS_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_LINK_UX_INACT_TIMER	(SSUSB_USB3_SYS_CSR_BASE + 0x020C)
+#define U3D_LINK_POWER_CONTROL	(SSUSB_USB3_SYS_CSR_BASE + 0x0210)
+#define U3D_LINK_ERR_COUNT	(SSUSB_USB3_SYS_CSR_BASE + 0x0214)
+
+/*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_LINK_UX_INACT_TIMER */
+#define DEV_U2_INACT_TIMEOUT_MSK	GENMASK(23, 16)
+#define DEV_U2_INACT_TIMEOUT_VALUE(x)	(((x) & 0xff) << 16)
+#define U2_INACT_TIMEOUT_MSK		GENMASK(15, 8)
+#define U1_INACT_TIMEOUT_MSK		GENMASK(7, 0)
+#define U1_INACT_TIMEOUT_VALUE(x)	((x) & 0xff)
+
+/* U3D_LINK_POWER_CONTROL */
+#define SW_U2_ACCEPT_ENABLE	BIT(9)
+#define SW_U1_ACCEPT_ENABLE	BIT(8)
+#define UX_EXIT			BIT(5)
+#define LGO_U3			BIT(4)
+#define LGO_U2			BIT(3)
+#define LGO_U1			BIT(2)
+#define SW_U2_REQUEST_ENABLE	BIT(1)
+#define SW_U1_REQUEST_ENABLE	BIT(0)
+
+/* U3D_LINK_ERR_COUNT */
+#define CLR_LINK_ERR_CNT	BIT(16)
+#define LINK_ERROR_COUNT	GENMASK(15, 0)
+
+/*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/
+
+#define U3D_POWER_MANAGEMENT		(SSUSB_USB2_CSR_BASE + 0x0004)
+#define U3D_DEVICE_CONTROL		(SSUSB_USB2_CSR_BASE + 0x000C)
+#define U3D_USB2_TEST_MODE		(SSUSB_USB2_CSR_BASE + 0x0014)
+#define U3D_COMMON_USB_INTR_ENABLE	(SSUSB_USB2_CSR_BASE + 0x0018)
+#define U3D_COMMON_USB_INTR		(SSUSB_USB2_CSR_BASE + 0x001C)
+#define U3D_LINK_RESET_INFO		(SSUSB_USB2_CSR_BASE + 0x0024)
+#define U3D_USB20_FRAME_NUM		(SSUSB_USB2_CSR_BASE + 0x003C)
+#define U3D_USB20_LPM_PARAMETER		(SSUSB_USB2_CSR_BASE + 0x0044)
+#define U3D_USB20_MISC_CONTROL		(SSUSB_USB2_CSR_BASE + 0x004C)
+
+/*---------------- SSUSB_USB2_CSR FIELD DEFINITION ----------------*/
+
+/* U3D_POWER_MANAGEMENT */
+#define LPM_BESL_STALL		BIT(14)
+#define LPM_BESLD_STALL		BIT(13)
+#define LPM_RWP			BIT(11)
+#define LPM_HRWE		BIT(10)
+#define LPM_MODE(x)		(((x) & 0x3) << 8)
+#define ISO_UPDATE		BIT(7)
+#define SOFT_CONN		BIT(6)
+#define HS_ENABLE		BIT(5)
+#define RESUME			BIT(2)
+#define SUSPENDM_ENABLE		BIT(0)
+
+/* U3D_DEVICE_CONTROL */
+#define DC_HOSTREQ		BIT(1)
+#define DC_SESSION		BIT(0)
+
+/* U3D_USB2_TEST_MODE */
+#define U2U3_AUTO_SWITCH	BIT(10)
+#define LPM_FORCE_STALL		BIT(8)
+#define FIFO_ACCESS		BIT(6)
+#define FORCE_FS		BIT(5)
+#define FORCE_HS		BIT(4)
+#define TEST_PACKET_MODE	BIT(3)
+#define TEST_K_MODE		BIT(2)
+#define TEST_J_MODE		BIT(1)
+#define TEST_SE0_NAK_MODE	BIT(0)
+
+/* U3D_COMMON_USB_INTR_ENABLE */
+/* U3D_COMMON_USB_INTR */
+#define LPM_RESUME_INTR		BIT(9)
+#define LPM_INTR		BIT(8)
+#define DISCONN_INTR		BIT(5)
+#define CONN_INTR		BIT(4)
+#define SOF_INTR		BIT(3)
+#define RESET_INTR		BIT(2)
+#define RESUME_INTR		BIT(1)
+#define SUSPEND_INTR		BIT(0)
+
+/* U3D_LINK_RESET_INFO */
+#define WTCHRP_MSK		GENMASK(19, 16)
+
+/* U3D_USB20_LPM_PARAMETER */
+#define LPM_BESLCK_U3(x)	(((x) & 0xf) << 12)
+#define LPM_BESLCK(x)		(((x) & 0xf) << 8)
+#define LPM_BESLDCK(x)		(((x) & 0xf) << 4)
+#define LPM_BESL		GENMASK(3, 0)
+
+/* U3D_USB20_MISC_CONTROL */
+#define LPM_U3_ACK_EN		BIT(0)
+
+/*---------------- SSUSB_SIFSLV_IPPC REGISTER DEFINITION ----------------*/
+
+#define U3D_SSUSB_IP_PW_CTRL0	(SSUSB_SIFSLV_IPPC_BASE + 0x0000)
+#define U3D_SSUSB_IP_PW_CTRL1	(SSUSB_SIFSLV_IPPC_BASE + 0x0004)
+#define U3D_SSUSB_IP_PW_CTRL2	(SSUSB_SIFSLV_IPPC_BASE + 0x0008)
+#define U3D_SSUSB_IP_PW_CTRL3	(SSUSB_SIFSLV_IPPC_BASE + 0x000C)
+#define U3D_SSUSB_IP_PW_STS1	(SSUSB_SIFSLV_IPPC_BASE + 0x0010)
+#define U3D_SSUSB_IP_PW_STS2	(SSUSB_SIFSLV_IPPC_BASE + 0x0014)
+#define U3D_SSUSB_OTG_STS	(SSUSB_SIFSLV_IPPC_BASE + 0x0018)
+#define U3D_SSUSB_OTG_STS_CLR	(SSUSB_SIFSLV_IPPC_BASE + 0x001C)
+#define U3D_SSUSB_IP_XHCI_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0024)
+#define U3D_SSUSB_IP_DEV_CAP	(SSUSB_SIFSLV_IPPC_BASE + 0x0028)
+#define U3D_SSUSB_OTG_INT_EN	(SSUSB_SIFSLV_IPPC_BASE + 0x002C)
+#define U3D_SSUSB_U3_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0030)
+#define U3D_SSUSB_U2_CTRL_0P	(SSUSB_SIFSLV_IPPC_BASE + 0x0050)
+#define U3D_SSUSB_REF_CK_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x008C)
+#define U3D_SSUSB_DEV_RST_CTRL	(SSUSB_SIFSLV_IPPC_BASE + 0x0098)
+#define U3D_SSUSB_HW_ID		(SSUSB_SIFSLV_IPPC_BASE + 0x00A0)
+#define U3D_SSUSB_HW_SUB_ID	(SSUSB_SIFSLV_IPPC_BASE + 0x00A4)
+#define U3D_SSUSB_IP_SPARE0	(SSUSB_SIFSLV_IPPC_BASE + 0x00C8)
+
+/*---------------- SSUSB_SIFSLV_IPPC FIELD DEFINITION ----------------*/
+
+/* U3D_SSUSB_IP_PW_CTRL0 */
+#define SSUSB_IP_SW_RST			BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL1 */
+#define SSUSB_IP_HOST_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL2 */
+#define SSUSB_IP_DEV_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_CTRL3 */
+#define SSUSB_IP_PCIE_PDN		BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS1 */
+#define SSUSB_IP_SLEEP_STS		BIT(30)
+#define SSUSB_U3_MAC_RST_B_STS		BIT(16)
+#define SSUSB_XHCI_RST_B_STS		BIT(11)
+#define SSUSB_SYS125_RST_B_STS		BIT(10)
+#define SSUSB_REF_RST_B_STS		BIT(8)
+#define SSUSB_SYSPLL_STABLE		BIT(0)
+
+/* U3D_SSUSB_IP_PW_STS2 */
+#define SSUSB_U2_MAC_SYS_RST_B_STS	BIT(0)
+
+/* U3D_SSUSB_OTG_STS */
+#define SSUSB_VBUS_VALID		BIT(9)
+
+/* U3D_SSUSB_OTG_STS_CLR */
+#define SSUSB_VBUS_INTR_CLR		BIT(6)
+
+/* U3D_SSUSB_IP_XHCI_CAP */
+#define SSUSB_IP_XHCI_U2_PORT_NUM(x)	(((x) >> 8) & 0xff)
+#define SSUSB_IP_XHCI_U3_PORT_NUM(x)	((x) & 0xff)
+
+/* U3D_SSUSB_IP_DEV_CAP */
+#define SSUSB_IP_DEV_U3_PORT_NUM(x)	((x) & 0xff)
+
+/* U3D_SSUSB_OTG_INT_EN */
+#define SSUSB_VBUS_CHG_INT_A_EN		BIT(7)
+#define SSUSB_VBUS_CHG_INT_B_EN		BIT(6)
+
+/* U3D_SSUSB_U3_CTRL_0P */
+#define SSUSB_U3_PORT_HOST_SEL		BIT(2)
+#define SSUSB_U3_PORT_PDN		BIT(1)
+#define SSUSB_U3_PORT_DIS		BIT(0)
+
+/* U3D_SSUSB_U2_CTRL_0P */
+#define SSUSB_U2_PORT_OTG_SEL		BIT(7)
+#define SSUSB_U2_PORT_HOST_SEL		BIT(2)
+#define SSUSB_U2_PORT_PDN		BIT(1)
+#define SSUSB_U2_PORT_DIS		BIT(0)
+
+/* U3D_SSUSB_DEV_RST_CTRL */
+#define SSUSB_DEV_SW_RST		BIT(0)
+
+#endif	/* _SSUSB_HW_REGS_H_ */
diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c
new file mode 100644
index 0000000..d74bd9e
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_plat.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#include "mtu3.h"
+#include "mtu3_dr.h"
+
+/* u2-port0 should be powered on and enabled; */
+int ssusb_check_clocks(struct ssusb_mtk *ssusb, u32 ex_clks)
+{
+	void __iomem *ibase = ssusb->ippc_base;
+	u32 value, check_val;
+	int ret;
+
+	check_val = ex_clks | SSUSB_SYS125_RST_B_STS | SSUSB_SYSPLL_STABLE |
+			SSUSB_REF_RST_B_STS;
+
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS1, value,
+			(check_val == (value & check_val)), 100, 20000);
+	if (ret) {
+		dev_err(ssusb->dev, "clks of sts1 are not stable!\n");
+		return ret;
+	}
+
+	ret = readl_poll_timeout(ibase + U3D_SSUSB_IP_PW_STS2, value,
+			(value & SSUSB_U2_MAC_SYS_RST_B_STS), 100, 10000);
+	if (ret) {
+		dev_err(ssusb->dev, "mac2 clock is not stable\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ssusb_phy_init(struct ssusb_mtk *ssusb)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ret = phy_init(ssusb->phys[i]);
+		if (ret)
+			goto exit_phy;
+	}
+	return 0;
+
+exit_phy:
+	for (; i > 0; i--)
+		phy_exit(ssusb->phys[i - 1]);
+
+	return ret;
+}
+
+static int ssusb_phy_exit(struct ssusb_mtk *ssusb)
+{
+	int i;
+
+	for (i = 0; i < ssusb->num_phys; i++)
+		phy_exit(ssusb->phys[i]);
+
+	return 0;
+}
+
+static int ssusb_phy_power_on(struct ssusb_mtk *ssusb)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ret = phy_power_on(ssusb->phys[i]);
+		if (ret)
+			goto power_off_phy;
+	}
+	return 0;
+
+power_off_phy:
+	for (; i > 0; i--)
+		phy_power_off(ssusb->phys[i - 1]);
+
+	return ret;
+}
+
+static void ssusb_phy_power_off(struct ssusb_mtk *ssusb)
+{
+	unsigned int i;
+
+	for (i = 0; i < ssusb->num_phys; i++)
+		phy_power_off(ssusb->phys[i]);
+}
+
+static int ssusb_common_init(struct ssusb_mtk *ssusb)
+{
+	int ret = 0;
+
+	ret = regulator_enable(ssusb->vusb33);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable vusb33\n");
+		goto vusb33_err;
+	}
+
+	ret = clk_prepare_enable(ssusb->sys_clk);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to enable sys_clk\n");
+		goto clk_err;
+	}
+
+	ret = ssusb_phy_init(ssusb);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to init phy\n");
+		goto phy_init_err;
+	}
+
+	ret = ssusb_phy_power_on(ssusb);
+	if (ret) {
+		dev_err(ssusb->dev, "failed to power on phy\n");
+		goto phy_err;
+	}
+
+	return 0;
+
+phy_err:
+	ssusb_phy_exit(ssusb);
+
+phy_init_err:
+	clk_disable_unprepare(ssusb->sys_clk);
+
+clk_err:
+	regulator_disable(ssusb->vusb33);
+
+vusb33_err:
+
+	return ret;
+}
+
+static void ssusb_common_exit(struct ssusb_mtk *ssusb)
+{
+	clk_disable_unprepare(ssusb->sys_clk);
+	regulator_disable(ssusb->vusb33);
+	ssusb_phy_power_off(ssusb);
+	ssusb_phy_exit(ssusb);
+}
+
+static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb)
+{
+	/* reset whole ip (xhci & u3d) */
+	mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+	udelay(1);
+	mtu3_clrbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL0, SSUSB_IP_SW_RST);
+}
+
+static int get_iddig_pinctrl(struct ssusb_mtk *ssusb)
+{
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+
+	otg_sx->id_pinctrl = devm_pinctrl_get(ssusb->dev);
+	if (IS_ERR(otg_sx->id_pinctrl)) {
+		dev_err(ssusb->dev, "Cannot find id pinctrl!\n");
+		return PTR_ERR(otg_sx->id_pinctrl);
+	}
+
+	otg_sx->id_float =
+		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_float");
+	if (IS_ERR(otg_sx->id_float)) {
+		dev_err(ssusb->dev, "Cannot find pinctrl id_float!\n");
+		return PTR_ERR(otg_sx->id_float);
+	}
+
+	otg_sx->id_ground =
+		pinctrl_lookup_state(otg_sx->id_pinctrl, "id_ground");
+	if (IS_ERR(otg_sx->id_ground)) {
+		dev_err(ssusb->dev, "Cannot find pinctrl id_ground!\n");
+		return PTR_ERR(otg_sx->id_ground);
+	}
+
+	return 0;
+}
+
+static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct otg_switch_mtk *otg_sx = &ssusb->otg_switch;
+	struct device *dev = &pdev->dev;
+	struct regulator *vbus;
+	struct resource *res;
+	int i;
+	int ret;
+
+	ssusb->num_phys = of_count_phandle_with_args(node,
+			"phys", "#phy-cells");
+	if (ssusb->num_phys > 0) {
+		ssusb->phys = devm_kcalloc(dev, ssusb->num_phys,
+					sizeof(*ssusb->phys), GFP_KERNEL);
+		if (!ssusb->phys)
+			return -ENOMEM;
+	} else {
+		ssusb->num_phys = 0;
+	}
+
+	for (i = 0; i < ssusb->num_phys; i++) {
+		ssusb->phys[i] = devm_of_phy_get_by_index(dev, node, i);
+		if (IS_ERR(ssusb->phys[i])) {
+			dev_err(dev, "failed to get phy-%d\n", i);
+			return PTR_ERR(ssusb->phys[i]);
+		}
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ippc");
+	ssusb->ippc_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(ssusb->ippc_base)) {
+		dev_err(dev, "failed to map memory for ippc\n");
+		return PTR_ERR(ssusb->ippc_base);
+	}
+
+	ssusb->vusb33 = devm_regulator_get(&pdev->dev, "vusb33");
+	if (IS_ERR(ssusb->vusb33)) {
+		dev_err(dev, "failed to get vusb33\n");
+		return PTR_ERR(ssusb->vusb33);
+	}
+
+	ssusb->sys_clk = devm_clk_get(dev, "sys_ck");
+	if (IS_ERR(ssusb->sys_clk)) {
+		dev_err(dev, "failed to get sys clock\n");
+		return PTR_ERR(ssusb->sys_clk);
+	}
+
+	ssusb->dr_mode = usb_get_dr_mode(dev);
+	if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(dev, "dr_mode is error\n");
+		return -EINVAL;
+	}
+
+	if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL)
+		return 0;
+
+	/* if host role is supported */
+	ret = ssusb_wakeup_of_property_parse(ssusb, node);
+	if (ret)
+		return ret;
+
+	if (ssusb->dr_mode != USB_DR_MODE_OTG)
+		return 0;
+
+	/* if dual-role mode is supported */
+	vbus = devm_regulator_get(&pdev->dev, "vbus");
+	if (IS_ERR(vbus)) {
+		dev_err(dev, "failed to get vbus\n");
+		return PTR_ERR(vbus);
+	}
+	otg_sx->vbus = vbus;
+
+	otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd");
+	otg_sx->manual_drd_enabled =
+		of_property_read_bool(node, "enable-manual-drd");
+
+	if (of_property_read_bool(node, "extcon")) {
+		otg_sx->edev = extcon_get_edev_by_phandle(ssusb->dev, 0);
+		if (IS_ERR(otg_sx->edev)) {
+			dev_err(ssusb->dev, "couldn't get extcon device\n");
+			return -EPROBE_DEFER;
+		}
+		if (otg_sx->manual_drd_enabled) {
+			ret = get_iddig_pinctrl(ssusb);
+			if (ret)
+				return ret;
+		}
+	}
+
+	dev_info(dev, "dr_mode: %d, is_u3_dr: %d\n",
+		ssusb->dr_mode, otg_sx->is_u3_drd);
+
+	return 0;
+}
+
+static int mtu3_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct device *dev = &pdev->dev;
+	struct ssusb_mtk *ssusb;
+	int ret = -ENOMEM;
+
+	/* all elements are set to ZERO as default value */
+	ssusb = devm_kzalloc(dev, sizeof(*ssusb), GFP_KERNEL);
+	if (!ssusb)
+		return -ENOMEM;
+
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "No suitable DMA config available\n");
+		return -ENOTSUPP;
+	}
+
+	platform_set_drvdata(pdev, ssusb);
+	ssusb->dev = dev;
+
+	ret = get_ssusb_rscs(pdev, ssusb);
+	if (ret)
+		return ret;
+
+	/* enable power domain */
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+	device_enable_async_suspend(dev);
+
+	ret = ssusb_common_init(ssusb);
+	if (ret)
+		goto comm_init_err;
+
+	ssusb_ip_sw_reset(ssusb);
+
+	if (IS_ENABLED(CONFIG_USB_MTU3_HOST))
+		ssusb->dr_mode = USB_DR_MODE_HOST;
+	else if (IS_ENABLED(CONFIG_USB_MTU3_GADGET))
+		ssusb->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+	/* default as host */
+	ssusb->is_host = !(ssusb->dr_mode == USB_DR_MODE_PERIPHERAL);
+
+	switch (ssusb->dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		ret = ssusb_gadget_init(ssusb);
+		if (ret) {
+			dev_err(dev, "failed to initialize gadget\n");
+			goto comm_exit;
+		}
+		break;
+	case USB_DR_MODE_HOST:
+		ret = ssusb_host_init(ssusb, node);
+		if (ret) {
+			dev_err(dev, "failed to initialize host\n");
+			goto comm_exit;
+		}
+		break;
+	case USB_DR_MODE_OTG:
+		ret = ssusb_gadget_init(ssusb);
+		if (ret) {
+			dev_err(dev, "failed to initialize gadget\n");
+			goto comm_exit;
+		}
+
+		ret = ssusb_host_init(ssusb, node);
+		if (ret) {
+			dev_err(dev, "failed to initialize host\n");
+			goto gadget_exit;
+		}
+
+		ssusb_otg_switch_init(ssusb);
+		break;
+	default:
+		dev_err(dev, "unsupported mode: %d\n", ssusb->dr_mode);
+		ret = -EINVAL;
+		goto comm_exit;
+	}
+
+	return 0;
+
+gadget_exit:
+	ssusb_gadget_exit(ssusb);
+
+comm_exit:
+	ssusb_common_exit(ssusb);
+
+comm_init_err:
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+static int mtu3_remove(struct platform_device *pdev)
+{
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	switch (ssusb->dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		ssusb_gadget_exit(ssusb);
+		break;
+	case USB_DR_MODE_HOST:
+		ssusb_host_exit(ssusb);
+		break;
+	case USB_DR_MODE_OTG:
+		ssusb_otg_switch_exit(ssusb);
+		ssusb_gadget_exit(ssusb);
+		ssusb_host_exit(ssusb);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ssusb_common_exit(ssusb);
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+/*
+ * when support dual-role mode, we reject suspend when
+ * it works as device mode;
+ */
+static int __maybe_unused mtu3_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	/* REVISIT: disconnect it for only device mode? */
+	if (!ssusb->is_host)
+		return 0;
+
+	ssusb_host_disable(ssusb, true);
+	ssusb_phy_power_off(ssusb);
+	clk_disable_unprepare(ssusb->sys_clk);
+	ssusb_wakeup_enable(ssusb);
+
+	return 0;
+}
+
+static int __maybe_unused mtu3_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ssusb_mtk *ssusb = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (!ssusb->is_host)
+		return 0;
+
+	ssusb_wakeup_disable(ssusb);
+	clk_prepare_enable(ssusb->sys_clk);
+	ssusb_phy_power_on(ssusb);
+	ssusb_host_enable(ssusb);
+
+	return 0;
+}
+
+static const struct dev_pm_ops mtu3_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(mtu3_suspend, mtu3_resume)
+};
+
+#define DEV_PM_OPS (IS_ENABLED(CONFIG_PM) ? &mtu3_pm_ops : NULL)
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id mtu3_of_match[] = {
+	{.compatible = "mediatek,mt8173-mtu3",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, mtu3_of_match);
+
+#endif
+
+static struct platform_driver mtu3_driver = {
+	.probe = mtu3_probe,
+	.remove = mtu3_remove,
+	.driver = {
+		.name = MTU3_DRIVER_NAME,
+		.pm = DEV_PM_OPS,
+		.of_match_table = of_match_ptr(mtu3_of_match),
+	},
+};
+module_platform_driver(mtu3_driver);
+
+MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MediaTek USB3 DRD Controller Driver");
+
diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c
new file mode 100644
index 0000000..3fd687e
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.c
@@ -0,0 +1,599 @@
+/*
+ * mtu3_qmu.c - Queue Management Unit driver for device controller
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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.
+ *
+ */
+
+/*
+ * Queue Management Unit (QMU) is designed to unload SW effort
+ * to serve DMA interrupts.
+ * By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD),
+ * SW links data buffers and triggers QMU to send / receive data to
+ * host / from device at a time.
+ * And now only GPD is supported.
+ *
+ * For more detailed information, please refer to QMU Programming Guide
+ */
+
+#include <linux/dmapool.h>
+#include <linux/iopoll.h>
+
+#include "mtu3.h"
+
+#define QMU_CHECKSUM_LEN	16
+
+#define GPD_FLAGS_HWO	BIT(0)
+#define GPD_FLAGS_BDP	BIT(1)
+#define GPD_FLAGS_BPS	BIT(2)
+#define GPD_FLAGS_IOC	BIT(7)
+
+#define GPD_EXT_FLAG_ZLP	BIT(5)
+
+
+static struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring,
+		dma_addr_t dma_addr)
+{
+	dma_addr_t dma_base = ring->dma;
+	struct qmu_gpd *gpd_head = ring->start;
+	u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head);
+
+	if (offset >= MAX_GPD_NUM)
+		return NULL;
+
+	return gpd_head + offset;
+}
+
+static dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring,
+		struct qmu_gpd *gpd)
+{
+	dma_addr_t dma_base = ring->dma;
+	struct qmu_gpd *gpd_head = ring->start;
+	u32 offset;
+
+	offset = gpd - gpd_head;
+	if (offset >= MAX_GPD_NUM)
+		return 0;
+
+	return dma_base + (offset * sizeof(*gpd));
+}
+
+static void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd)
+{
+	ring->start = gpd;
+	ring->enqueue = gpd;
+	ring->dequeue = gpd;
+	ring->end = gpd + MAX_GPD_NUM - 1;
+}
+
+static void reset_gpd_list(struct mtu3_ep *mep)
+{
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->start;
+
+	if (gpd) {
+		gpd->flag &= ~GPD_FLAGS_HWO;
+		gpd_ring_init(ring, gpd);
+	}
+}
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep)
+{
+	struct qmu_gpd *gpd;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+	/* software own all gpds as default */
+	gpd = dma_pool_zalloc(mep->mtu->qmu_gpd_pool, GFP_ATOMIC, &ring->dma);
+	if (gpd == NULL)
+		return -ENOMEM;
+
+	gpd_ring_init(ring, gpd);
+
+	return 0;
+}
+
+void mtu3_gpd_ring_free(struct mtu3_ep *mep)
+{
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+
+	dma_pool_free(mep->mtu->qmu_gpd_pool,
+			ring->start, ring->dma);
+	memset(ring, 0, sizeof(*ring));
+}
+
+/*
+ * calculate check sum of a gpd or bd
+ * add "noinline" and "mb" to prevent wrong calculation
+ */
+static noinline u8 qmu_calc_checksum(u8 *data)
+{
+	u8 chksum = 0;
+	int i;
+
+	data[1] = 0x0;  /* set checksum to 0 */
+
+	mb();	/* ensure the gpd/bd is really up-to-date */
+	for (i = 0; i < QMU_CHECKSUM_LEN; i++)
+		chksum += data[i];
+
+	/* Default: HWO=1, @flag[bit0] */
+	chksum += 1;
+
+	return 0xFF - chksum;
+}
+
+void mtu3_qmu_resume(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 offset;
+
+	offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+	mtu3_writel(mbase, offset, QMU_Q_RESUME);
+	if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE))
+		mtu3_writel(mbase, offset, QMU_Q_RESUME);
+}
+
+static struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring)
+{
+	if (ring->enqueue < ring->end)
+		ring->enqueue++;
+	else
+		ring->enqueue = ring->start;
+
+	return ring->enqueue;
+}
+
+static struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring)
+{
+	if (ring->dequeue < ring->end)
+		ring->dequeue++;
+	else
+		ring->dequeue = ring->start;
+
+	return ring->dequeue;
+}
+
+/* check if a ring is emtpy */
+int gpd_ring_empty(struct mtu3_gpd_ring *ring)
+{
+	struct qmu_gpd *enq = ring->enqueue;
+	struct qmu_gpd *next;
+
+	if (ring->enqueue < ring->end)
+		next = enq + 1;
+	else
+		next = ring->start;
+
+	/* one gpd is reserved to simplify gpd preparation */
+	return next == ring->dequeue;
+}
+
+int mtu3_prepare_transfer(struct mtu3_ep *mep)
+{
+	return gpd_ring_empty(&mep->gpd_ring);
+}
+
+static int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct qmu_gpd *enq;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->enqueue;
+	struct usb_request *req = &mreq->request;
+
+	/* set all fields to zero as default value */
+	memset(gpd, 0, sizeof(*gpd));
+
+	gpd->buffer = (u32)req->dma;
+	gpd->buf_len = req->length;
+	gpd->flag |= GPD_FLAGS_IOC;
+
+	/* get the next GPD */
+	enq = advance_enq_gpd(ring);
+	dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p\n",
+		mep->epnum, gpd, enq);
+
+	enq->flag &= ~GPD_FLAGS_HWO;
+	gpd->next_gpd = (u32)gpd_virt_to_dma(ring, enq);
+
+	if (req->zero)
+		gpd->ext_flag |= GPD_EXT_FLAG_ZLP;
+
+	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+	gpd->flag |= GPD_FLAGS_HWO;
+
+	mreq->gpd = gpd;
+
+	return 0;
+}
+
+static int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+	struct qmu_gpd *enq;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	struct qmu_gpd *gpd = ring->enqueue;
+	struct usb_request *req = &mreq->request;
+
+	/* set all fields to zero as default value */
+	memset(gpd, 0, sizeof(*gpd));
+
+	gpd->buffer = (u32)req->dma;
+	gpd->data_buf_len = req->length;
+	gpd->flag |= GPD_FLAGS_IOC;
+
+	/* get the next GPD */
+	enq = advance_enq_gpd(ring);
+	dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p\n",
+		mep->epnum, gpd, enq);
+
+	enq->flag &= ~GPD_FLAGS_HWO;
+	gpd->next_gpd = (u32)gpd_virt_to_dma(ring, enq);
+	gpd->chksum = qmu_calc_checksum((u8 *)gpd);
+	gpd->flag |= GPD_FLAGS_HWO;
+
+	mreq->gpd = gpd;
+
+	return 0;
+}
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq)
+{
+
+	if (mep->is_in)
+		mtu3_prepare_tx_gpd(mep, mreq);
+	else
+		mtu3_prepare_rx_gpd(mep, mreq);
+}
+
+int mtu3_qmu_start(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	u8 epnum = mep->epnum;
+
+	if (mep->is_in) {
+		/* set QMU start address */
+		mtu3_writel(mbase, USB_QMU_TQSAR(mep->epnum), ring->dma);
+		mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+		mtu3_setbits(mbase, U3D_QCR0, QMU_TX_CS_EN(epnum));
+
+		/* send zero length packet according to ZLP flag in GPD */
+		mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum));
+
+		mtu3_writel(mbase, U3D_TQERRIESR0,
+				QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum));
+
+		if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) {
+			dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum);
+			return 0;
+		}
+
+		mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START);
+
+	} else {
+
+		mtu3_writel(mbase, USB_QMU_RQSAR(mep->epnum), ring->dma);
+		mtu3_setbits(mbase, MU3D_EP_RXCR0(mep->epnum), RX_DMAREQEN);
+		mtu3_setbits(mbase, U3D_QCR0, QMU_RX_CS_EN(epnum));
+
+		/* don't expect ZLP */
+		mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum));
+		/* move to next GPD when receive ZLP */
+		mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum));
+
+		mtu3_writel(mbase, U3D_RQERRIESR0,
+				QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum));
+		mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum));
+
+		if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) {
+			dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum);
+			return 0;
+		}
+
+		mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START);
+	}
+
+	return 0;
+}
+
+/* may called in atomic context */
+void mtu3_qmu_stop(struct mtu3_ep *mep)
+{
+	struct mtu3 *mtu = mep->mtu;
+	void __iomem *mbase = mtu->mac_base;
+	int epnum = mep->epnum;
+	u32 csr = 0;
+	u32 reg_csr;
+	int ret;
+
+	reg_csr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum);
+
+	if (!(mtu3_readl(mbase, reg_csr) & QMU_Q_ACTIVE)) {
+		dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name);
+		return;
+	}
+	mtu3_writel(mbase, reg_csr, QMU_Q_STOP);
+
+	ret = readl_poll_timeout_atomic(mbase + reg_csr, csr,
+			!(csr & QMU_Q_ACTIVE), 1, 1000);
+	if (ret) {
+		dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name);
+		return;
+	}
+
+	dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name);
+}
+
+void mtu3_qmu_flush(struct mtu3_ep *mep)
+{
+
+	dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__,
+		((mep->is_in) ? "TX" : "RX"));
+
+	/*Stop QMU */
+	mtu3_qmu_stop(mep);
+	reset_gpd_list(mep);
+}
+
+/*
+ * QMU can't transfer zero length packet directly (a hardware issue
+ * on old SoCs), so when needs to send ZLP, we intentionally trigger
+ * a length error interrupt, and in the ISR sends a ZLP by BMU.
+ */
+static void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->in_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+	struct usb_request *req = NULL;
+	struct mtu3_request *mreq;
+	u32 txcsr = 0;
+	int ret;
+
+	mreq = next_request(mep);
+	if (mreq && mreq->request.length == 0)
+		req = &mreq->request;
+	else
+		return;
+
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	if (gpd_current->buf_len != 0) {
+		dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum);
+		return;
+	}
+
+	dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq);
+
+	mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+
+	ret = readl_poll_timeout_atomic(mbase + MU3D_EP_TXCR0(mep->epnum),
+			txcsr, !(txcsr & TX_FIFOFULL), 1, 1000);
+	if (ret) {
+		dev_err(mtu->dev, "%s wait for fifo empty fail\n", __func__);
+		return;
+	}
+	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY);
+
+	/* by pass the current GDP */
+	gpd_current->flag |= GPD_FLAGS_BPS;
+	gpd_current->chksum = qmu_calc_checksum((u8 *)gpd_current);
+	gpd_current->flag |= GPD_FLAGS_HWO;
+
+	/*enable DMAREQEN, switch back to QMU mode */
+	mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN);
+	mtu3_qmu_resume(mep);
+}
+
+/*
+ * NOTE: request list maybe is already empty as following case:
+ * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)-->
+ * queue_tx --> process_tasklet(meanwhile, the second one is transferred,
+ * tasklet process both of them)-->qmu_interrupt for second one.
+ * To avoid upper case, put qmu_done_tx in ISR directly to process it.
+ */
+static void qmu_done_tx(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->in_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd = ring->dequeue;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_TQCPR(epnum));
+	struct usb_request *request = NULL;
+	struct mtu3_request *mreq;
+
+	/*transfer phy address got from QMU register to virtual address */
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+		__func__, epnum, gpd, gpd_current, ring->enqueue);
+
+	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+		mreq = next_request(mep);
+
+		if (mreq == NULL || mreq->gpd != gpd) {
+			dev_err(mtu->dev, "no correct TX req is found\n");
+			break;
+		}
+
+		request = &mreq->request;
+		request->actual = gpd->buf_len;
+		mtu3_req_complete(mep, request, 0);
+
+		gpd = advance_deq_gpd(ring);
+	}
+
+	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+		__func__, epnum, ring->dequeue, ring->enqueue);
+
+}
+
+static void qmu_done_rx(struct mtu3 *mtu, u8 epnum)
+{
+	struct mtu3_ep *mep = mtu->out_eps + epnum;
+	struct mtu3_gpd_ring *ring = &mep->gpd_ring;
+	void __iomem *mbase = mtu->mac_base;
+	struct qmu_gpd *gpd = ring->dequeue;
+	struct qmu_gpd *gpd_current = NULL;
+	dma_addr_t gpd_dma = mtu3_readl(mbase, USB_QMU_RQCPR(epnum));
+	struct usb_request *req = NULL;
+	struct mtu3_request *mreq;
+
+	gpd_current = gpd_dma_to_virt(ring, gpd_dma);
+
+	dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n",
+		__func__, epnum, gpd, gpd_current, ring->enqueue);
+
+	while (gpd != gpd_current && !(gpd->flag & GPD_FLAGS_HWO)) {
+
+		mreq = next_request(mep);
+
+		if (mreq == NULL || mreq->gpd != gpd) {
+			dev_err(mtu->dev, "no correct RX req is found\n");
+			break;
+		}
+		req = &mreq->request;
+
+		req->actual = gpd->buf_len;
+		mtu3_req_complete(mep, req, 0);
+
+		gpd = advance_deq_gpd(ring);
+	}
+
+	dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n",
+		__func__, epnum, ring->dequeue, ring->enqueue);
+}
+
+static void qmu_done_tasklet(unsigned long data)
+{
+	struct mtu3 *mtu = (struct mtu3 *)data;
+	unsigned long flags;
+	u32 qmu_val;
+	int i;
+
+	spin_lock_irqsave(&mtu->lock, flags);
+
+	qmu_val = mtu->qmu_done_intr;
+
+	mtu->qmu_done_intr = 0;
+
+	for (i = 1; i < mtu->num_eps; i++) {
+		if (qmu_val & QMU_RX_DONE_INT(i))
+			qmu_done_rx(mtu, i);
+		if (qmu_val & QMU_TX_DONE_INT(i))
+			qmu_done_tx(mtu, i);
+	}
+	spin_unlock_irqrestore(&mtu->lock, flags);
+}
+
+static void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 errval;
+	int i;
+
+	if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) {
+		errval = mtu3_readl(mbase, U3D_RQERRIR0);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_RX_CS_ERR(i))
+				dev_err(mtu->dev, "Rx %d CS error!\n", i);
+
+			if (errval & QMU_RX_LEN_ERR(i))
+				dev_err(mtu->dev, "RX %d Length error\n", i);
+		}
+		mtu3_writel(mbase, U3D_RQERRIR0, errval);
+	}
+
+	if (qmu_status & RXQ_ZLPERR_INT) {
+		errval = mtu3_readl(mbase, U3D_RQERRIR1);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_RX_ZLP_ERR(i))
+				dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i);
+		}
+		mtu3_writel(mbase, U3D_RQERRIR1, errval);
+	}
+
+	if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) {
+		errval = mtu3_readl(mbase, U3D_TQERRIR0);
+		for (i = 1; i < mtu->num_eps; i++) {
+			if (errval & QMU_TX_CS_ERR(i))
+				dev_err(mtu->dev, "Tx %d checksum error!\n", i);
+
+			if (errval & QMU_TX_LEN_ERR(i))
+				qmu_tx_zlp_error_handler(mtu, i);
+		}
+		mtu3_writel(mbase, U3D_TQERRIR0, errval);
+	}
+}
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu)
+{
+	void __iomem *mbase = mtu->mac_base;
+	u32 qmu_status;
+	u32 qmu_done_status;
+
+	/* U3D_QISAR1 is read update */
+	qmu_status = mtu3_readl(mbase, U3D_QISAR1);
+	qmu_status &= mtu3_readl(mbase, U3D_QIER1);
+
+	qmu_done_status = mtu3_readl(mbase, U3D_QISAR0);
+	qmu_done_status &= mtu3_readl(mbase, U3D_QIER0);
+	mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */
+	dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n",
+		(qmu_done_status & 0xFFFF), qmu_done_status >> 16,
+		qmu_status);
+
+	if (qmu_done_status) {
+		/* qmu maybe not handled yet */
+		mtu->qmu_done_intr |= qmu_done_status;
+		tasklet_schedule(&mtu->qmu_done);
+	}
+
+	if (qmu_status)
+		qmu_exception_isr(mtu, qmu_status);
+
+	return IRQ_HANDLED;
+}
+
+int mtu3_qmu_init(struct mtu3 *mtu)
+{
+
+	compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B");
+
+	mtu->qmu_gpd_pool = dma_pool_create("QMU_GPD", mtu->dev,
+			QMU_GPD_RING_SIZE, QMU_GPD_SIZE, 0);
+
+	if (!mtu->qmu_gpd_pool)
+		return -ENOMEM;
+
+	tasklet_init(&mtu->qmu_done, qmu_done_tasklet, (unsigned long)mtu);
+
+	return 0;
+}
+
+int mtu3_qmu_exit(struct mtu3 *mtu)
+{
+	tasklet_kill(&mtu->qmu_done);
+	dma_pool_destroy(mtu->qmu_gpd_pool);
+
+	return 0;
+}
+
diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h
new file mode 100644
index 0000000..2e07f5b
--- /dev/null
+++ b/drivers/usb/mtu3/mtu3_qmu.h
@@ -0,0 +1,43 @@
+/*
+ * mtu3_qmu.h - Queue Management Unit driver header
+ *
+ * Copyright (C) 2016 MediaTek Inc.
+ *
+ * Author: Chunfeng Yun <chunfeng.yun at mediatek.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 __MTK_QMU_H__
+#define __MTK_QMU_H__
+
+#define MAX_GPD_NUM		64
+#define QMU_GPD_SIZE		(sizeof(struct qmu_gpd))
+#define QMU_GPD_RING_SIZE	(MAX_GPD_NUM * QMU_GPD_SIZE)
+
+#define GPD_BUF_SIZE		65532
+
+void mtu3_qmu_stop(struct mtu3_ep *mep);
+int mtu3_qmu_start(struct mtu3_ep *mep);
+void mtu3_qmu_resume(struct mtu3_ep *mep);
+void mtu3_qmu_flush(struct mtu3_ep *mep);
+
+void mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq);
+int mtu3_prepare_transfer(struct mtu3_ep *mep);
+
+int mtu3_gpd_ring_alloc(struct mtu3_ep *mep);
+void mtu3_gpd_ring_free(struct mtu3_ep *mep);
+
+irqreturn_t mtu3_qmu_isr(struct mtu3 *mtu);
+int mtu3_qmu_init(struct mtu3 *mtu);
+int mtu3_qmu_exit(struct mtu3 *mtu);
+
+#endif
-- 
1.7.9.5



From yingjoe.chen at mediatek.com  Wed Jun 15 03:58:42 2016
From: yingjoe.chen at mediatek.com (Yingjoe Chen)
Date: Wed, 15 Jun 2016 18:58:42 +0800
Subject: [alsa-devel] [PATCH v4 9/9] ASoC: mediatek: Add mt2701-cs42448
 driver and config option.
In-Reply-To: <1465802806-19197-10-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
 <1465802806-19197-10-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465988322.30493.7.camel@mtksdaap41>

On Mon, 2016-06-13 at 15:26 +0800, Garlic Tseng wrote:
> Add machine driver and config option for MT2701.
> 
> Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
> ---
>  sound/soc/mediatek/Kconfig                 |  21 ++
>  sound/soc/mediatek/Makefile                |   1 +
>  sound/soc/mediatek/mt2701/Makefile         |  19 ++
>  sound/soc/mediatek/mt2701/mt2701-cs42448.c | 422 +++++++++++++++++++++++++++++
>  4 files changed, 463 insertions(+)
>  create mode 100644 sound/soc/mediatek/mt2701/Makefile
>  create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
> 
> diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
> index ff1a419..0ecb785 100644
> --- a/sound/soc/mediatek/Kconfig
> +++ b/sound/soc/mediatek/Kconfig
> @@ -7,6 +7,27 @@ config SND_SOC_MEDIATEK
>  	  Select Y if you have such device.
>  	  If unsure select "N".
>  
> +config SND_SOC_MT2701
> +	tristate "ASoC support for Mediatek MT2701 chip"
> +	depends on ARCH_MEDIATEK
> +	select SND_SOC_MEDIATEK
> +	help
> +	  This adds ASoC driver for Mediatek MT2701 boards
> +	  that can be used with other codecs.
> +	  Select Y if you have such device.
> +	  If unsure select "N".
> +
> +config SND_SOC_MT2701_CS42448
> +	tristate "SND_SOC_MT2701_CS42448"

Please provide proper config prompt here.

Joe.C




From yingjoe.chen at mediatek.com  Wed Jun 15 03:57:22 2016
From: yingjoe.chen at mediatek.com (Yingjoe Chen)
Date: Wed, 15 Jun 2016 18:57:22 +0800
Subject: [alsa-devel] [PATCH v4 3/9] ASoC: mediatek: let mt8173 use
 mediatek common structure
In-Reply-To: <1465802806-19197-4-git-send-email-garlic.tseng@mediatek.com>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
 <1465802806-19197-4-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1465988242.30493.6.camel@mtksdaap41>

On Mon, 2016-06-13 at 15:26 +0800, Garlic Tseng wrote:
> Modify mt8173 driver implementation to use common structure.
> 
> Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
> ---
>  sound/soc/mediatek/Kconfig                    |  11 +
>  sound/soc/mediatek/Makefile                   |   3 +-
>  sound/soc/mediatek/common/Makefile            |  17 +
>  sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
>  sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 679 +++++++++++---------------
>  5 files changed, 327 insertions(+), 425 deletions(-)
>  create mode 100644 sound/soc/mediatek/common/Makefile
> 
> diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
> index ae9f664..ff1a419 100644
> --- a/sound/soc/mediatek/Kconfig
> +++ b/sound/soc/mediatek/Kconfig
> @@ -1,6 +1,16 @@
> +config SND_SOC_MEDIATEK
> +	tristate "SND_SOC_MEDIATEK"
> +	depends on ARCH_MEDIATEK
> +	help
> +	  This adds ASoC driver for Mediatek boards
> +	  that can be used with other codecs.
> +	  Select Y if you have such device.
> +	  If unsure select "N".
> +
>  config SND_SOC_MT8173
>  	tristate "ASoC support for Mediatek MT8173 chip"
>  	depends on ARCH_MEDIATEK
> +	select SND_SOC_MEDIATEK
>  	help
>  	  This adds ASoC platform driver support for Mediatek MT8173 chip
>  	  that can be used with other codecs.

It seems both SND_SOC_MT8173 and SND_SOC_MT2701 select SND_SOC_MEDIATEK,
and enabling only SND_SOC_MEDIATEK itself is not really useful. Do we
need to make SND_SOC_MEDIATEK user configurable option?

If it is, we should at least have a proper config prompt instead of just
"SND_SOC_MEDIATEK".


> @@ -49,3 +59,4 @@ config SND_SOC_MT8173_RT5650_RT5676
>  	  with the RT5650 and RT5676 codecs.
>  	  Select Y if you have such device.
>  	  If unsure select "N".
> +

nit: This change is not necessary.

Joe.C




From chris at chris-wilson.co.uk  Wed Jun 15 05:17:54 2016
From: chris at chris-wilson.co.uk (Chris Wilson)
Date: Wed, 15 Jun 2016 13:17:54 +0100
Subject: [PATCH 09/44] drm/mediatek: Remove redundant calls to
 drm_connector_register_all()
In-Reply-To: <1465993109-19523-1-git-send-email-chris@chris-wilson.co.uk>
References: <1465993109-19523-1-git-send-email-chris@chris-wilson.co.uk>
Message-ID: <1465993109-19523-10-git-send-email-chris@chris-wilson.co.uk>

Up to now, the recommendation was for drivers to call drm_dev_register()
followed by drm_connector_register_all(). Now that
drm_connector_register() is safe against multiple invocations, we can
move drm_connector_register_all() to drm_dev_register() and not suffer
from any backwards compatibility issues with drivers not following the
more rigorous init ordering.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
Cc: Matthias Brugger <matthias.bgg at gmail.com>
Cc: David Airlie <airlied at linux.ie>
Cc: dri-devel at lists.freedesktop.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-mediatek at lists.infradead.org
---
 drivers/gpu/drm/mediatek/mtk_drm_drv.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index c33bf98c5d5e..7ab91f4a200f 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -293,14 +293,8 @@ static int mtk_drm_bind(struct device *dev)
 	if (ret < 0)
 		goto err_deinit;
 
-	ret = drm_connector_register_all(drm);
-	if (ret < 0)
-		goto err_unregister;
-
 	return 0;
 
-err_unregister:
-	drm_dev_unregister(drm);
 err_deinit:
 	mtk_drm_kms_deinit(drm);
 err_free:
@@ -455,7 +449,6 @@ static int mtk_drm_remove(struct platform_device *pdev)
 	struct drm_device *drm = private->drm;
 	int i;
 
-	drm_connector_unregister_all(drm);
 	drm_dev_unregister(drm);
 	mtk_drm_kms_deinit(drm);
 	drm_dev_unref(drm);
-- 
2.8.1



From joro at 8bytes.org  Wed Jun 15 06:31:04 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Wed, 15 Jun 2016 15:31:04 +0200
Subject: [PATCH v2 3/3] iommu: convert DT component matching to
 component_match_add_release()
In-Reply-To: <E1b8pyc-0005OV-7X@rmk-PC.armlinux.org.uk>
References: <20160603141548.GC1041@n2100.armlinux.org.uk>
 <E1b8pyc-0005OV-7X@rmk-PC.armlinux.org.uk>
Message-ID: <20160615133104.GL26566@8bytes.org>

On Fri, Jun 03, 2016 at 03:21:30PM +0100, Russell King wrote:
> Convert DT component matching to use component_match_add_release().
> 
> Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
> ---
>  drivers/iommu/mtk_iommu.c | 14 ++++++--------
>  1 file changed, 6 insertions(+), 8 deletions(-)

Applied, thanks.



From john at phrozen.org  Wed Jun 15 07:58:49 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 15 Jun 2016 16:58:49 +0200
Subject: [PATCH 3/4] net-next: mediatek: add IRQ locking
In-Reply-To: <1466002730-58476-1-git-send-email-john@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
Message-ID: <1466002730-58476-4-git-send-email-john@phrozen.org>

The code that enables and disables IRQs is missing proper locking. After
adding the IRQ grouping patch and routing the RX and TX IRQs to different
cores we experienced IRQ stalls. Fix this by adding proper locking.
We use a dedicated lock to reduce the latency if the IRQ code.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    7 +++++++
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index cc38eae..c7da00b 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -300,18 +300,24 @@ static void mtk_mdio_cleanup(struct mtk_eth *eth)
 
 static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
 {
+	unsigned long flags;
 	u32 val;
 
+	spin_lock_irqsave(&eth->irq_lock, flags);
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
+	spin_unlock_irqrestore(&eth->irq_lock, flags);
 }
 
 static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
 {
+	unsigned long flags;
 	u32 val;
 
+	spin_lock_irqsave(&eth->irq_lock, flags);
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
+	spin_unlock_irqrestore(&eth->irq_lock, flags);
 }
 
 static int mtk_set_mac_address(struct net_device *dev, void *p)
@@ -1739,6 +1745,7 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->base);
 
 	spin_lock_init(&eth->page_lock);
+	spin_lock_init(&eth->irq_lock);
 
 	eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 						      "mediatek,ethsys");
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index a5eb7c6..3159d2a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -373,6 +373,7 @@ struct mtk_eth {
 	void __iomem			*base;
 	struct reset_control		*rstc;
 	spinlock_t			page_lock;
+	spinlock_t			irq_lock;
 	struct net_device		dummy_dev;
 	struct net_device		*netdev[MTK_MAX_DEVS];
 	struct mtk_mac			*mac[MTK_MAX_DEVS];
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 15 07:58:48 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 15 Jun 2016 16:58:48 +0200
Subject: [PATCH 2/4] net-next: mediatek: don't use intermediate variables to
 store IRQ masks
In-Reply-To: <1466002730-58476-1-git-send-email-john@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
Message-ID: <1466002730-58476-3-git-send-email-john@phrozen.org>

The code currently uses variables to store and never modify the bit masks
of interrupts. This is legacy code from an early version of the driver
that supported MIPS based SoCs where the IRQ bits depended on the actual
SoC. As the bits are the same for all ARM based SoCs using this driver we
can remove the intermediate variables.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index b3032f4..cc38eae 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -775,7 +775,7 @@ drop:
 }
 
 static int mtk_poll_rx(struct napi_struct *napi, int budget,
-		       struct mtk_eth *eth, u32 rx_intr)
+		       struct mtk_eth *eth)
 {
 	struct mtk_rx_ring *ring = &eth->rx_ring;
 	int idx = ring->calc_idx;
@@ -863,7 +863,7 @@ release_desc:
 	}
 
 	if (done < budget)
-		mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
+		mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
 
 	return done;
 }
@@ -946,28 +946,26 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 static int mtk_poll(struct napi_struct *napi, int budget)
 {
 	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-	u32 status, status2, mask, tx_intr, rx_intr, status_intr;
+	u32 status, status2, mask;
 	int tx_done, rx_done;
 	bool tx_again = false;
 
 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
 	status2 = mtk_r32(eth, MTK_INT_STATUS2);
-	tx_intr = MTK_TX_DONE_INT;
-	rx_intr = MTK_RX_DONE_INT;
-	status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
 	tx_done = 0;
 	rx_done = 0;
 	tx_again = 0;
 
-	if (status & tx_intr)
+	if (status & MTK_TX_DONE_INT)
 		tx_done = mtk_poll_tx(eth, budget, &tx_again);
 
-	if (status & rx_intr)
-		rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
+	if (status & MTK_RX_DONE_INT)
+		rx_done = mtk_poll_rx(napi, budget, eth);
 
-	if (unlikely(status2 & status_intr)) {
+	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
 		mtk_stats_update(eth);
-		mtk_w32(eth, status_intr, MTK_INT_STATUS2);
+		mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
+			MTK_INT_STATUS2);
 	}
 
 	if (unlikely(netif_msg_intr(eth))) {
@@ -985,7 +983,7 @@ static int mtk_poll(struct napi_struct *napi, int budget)
 		return budget;
 
 	napi_complete(napi);
-	mtk_irq_enable(eth, tx_intr | rx_intr);
+	mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
 
 	return rx_done;
 }
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 15 07:58:47 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 15 Jun 2016 16:58:47 +0200
Subject: [PATCH 1/4] net-next: mediatek: remove superfluous register reads
In-Reply-To: <1466002730-58476-1-git-send-email-john@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
Message-ID: <1466002730-58476-2-git-send-email-john@phrozen.org>

The driver was originally written for MIPS based SoC. These required the
IRQ mask register to be read after writing it to ensure that the content
was actually applied. As this version only works on ARM based SoCs, we can
safely remove the 2 reads as they are no longer required.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index ebfca7d..b3032f4 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -304,8 +304,6 @@ static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
 
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
-	/* flush write */
-	mtk_r32(eth, MTK_QDMA_INT_MASK);
 }
 
 static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
@@ -314,8 +312,6 @@ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
 
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
-	/* flush write */
-	mtk_r32(eth, MTK_QDMA_INT_MASK);
 }
 
 static int mtk_set_mac_address(struct net_device *dev, void *p)
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 15 07:58:46 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 15 Jun 2016 16:58:46 +0200
Subject: [PATCH 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
Message-ID: <1466002730-58476-1-git-send-email-john@phrozen.org>

This series contains 2 small code cleanups that are leftovers from the
MIPS support. There is also a small fix that adds proper locking to the
code accessing the IRQ registers. Without this fix we saw deadlocks caused
by the last patch of the series, which adds IRQ grouping. The grouping
feature allows us to use different IRQs for TX and RX. By doing so we can
use affinity to let the SoC handle the IRQs on different cores.

John Crispin (4):
  net-next: mediatek: remove superfluous register reads
  net-next: mediatek: don't use intermediate variables to store IRQ
    masks
  net-next: mediatek: add IRQ locking
  net-next: mediatek: add support for IRQ grouping

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |  175 +++++++++++++++++----------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |   16 ++-
 2 files changed, 122 insertions(+), 69 deletions(-)

-- 
1.7.10.4



From john at phrozen.org  Wed Jun 15 07:58:50 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 15 Jun 2016 16:58:50 +0200
Subject: [PATCH 4/4] net-next: mediatek: add support for IRQ grouping
In-Reply-To: <1466002730-58476-1-git-send-email-john@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
Message-ID: <1466002730-58476-5-git-send-email-john@phrozen.org>

The ethernet core has 3 IRQs. Using the IRQ grouping registers we are able
to separate TX and RX IRQs, which allows us to service them on separate
cores. This patch splits the IRQ handler into 2 separate functions, one for
TX and another for RX. The TX housekeeping is split out into its own NAPI
handler.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |  156 +++++++++++++++++----------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |   15 ++-
 2 files changed, 111 insertions(+), 60 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index c7da00b..79fdb07 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -874,14 +874,13 @@ release_desc:
 	return done;
 }
 
-static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+static int mtk_poll_tx(struct mtk_eth *eth, int budget)
 {
 	struct mtk_tx_ring *ring = &eth->tx_ring;
 	struct mtk_tx_dma *desc;
 	struct sk_buff *skb;
 	struct mtk_tx_buf *tx_buf;
-	int total = 0, done = 0;
-	unsigned int bytes = 0;
+	unsigned int bytes = 0, done = 0;
 	u32 cpu, dma;
 	static int condition;
 	int i;
@@ -933,63 +932,82 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 		netdev_completed_queue(eth->netdev[i], done, bytes);
 	}
 
-	/* read hw index again make sure no new tx packet */
-	if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
-		*tx_again = true;
-	else
-		mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
-
-	if (!total)
-		return 0;
-
 	if (mtk_queue_stopped(eth) &&
 	    (atomic_read(&ring->free_count) > ring->thresh))
 		mtk_wake_queue(eth);
 
-	return total;
+	return done;
 }
 
-static int mtk_poll(struct napi_struct *napi, int budget)
+static void mtk_handle_status_irq(struct mtk_eth *eth)
 {
-	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-	u32 status, status2, mask;
-	int tx_done, rx_done;
-	bool tx_again = false;
-
-	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	status2 = mtk_r32(eth, MTK_INT_STATUS2);
-	tx_done = 0;
-	rx_done = 0;
-	tx_again = 0;
-
-	if (status & MTK_TX_DONE_INT)
-		tx_done = mtk_poll_tx(eth, budget, &tx_again);
-
-	if (status & MTK_RX_DONE_INT)
-		rx_done = mtk_poll_rx(napi, budget, eth);
+	u32 status2 = mtk_r32(eth, MTK_INT_STATUS2);
 
 	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
 		mtk_stats_update(eth);
 		mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
 			MTK_INT_STATUS2);
 	}
+}
+
+static int mtk_napi_tx(struct napi_struct *napi, int budget)
+{
+	struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
+	u32 status, mask;
+	int tx_done = 0;
+
+	mtk_handle_status_irq(eth);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
+	tx_done = mtk_poll_tx(eth, budget);
+
+	if (unlikely(netif_msg_intr(eth))) {
+		status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+		mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
+		dev_info(eth->dev,
+			 "done tx %d, intr 0x%08x/0x%x\n",
+			 tx_done, status, mask);
+	}
+
+	if (tx_done == budget)
+		return budget;
+
+	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+	if (status & MTK_TX_DONE_INT)
+		return budget;
+
+	napi_complete(napi);
+	mtk_irq_enable(eth, MTK_TX_DONE_INT);
+
+	return tx_done;
+}
+
+static int mtk_napi_rx(struct napi_struct *napi, int budget)
+{
+	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+	u32 status, mask;
+	int rx_done = 0;
+
+	mtk_handle_status_irq(eth);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
+	rx_done = mtk_poll_rx(napi, budget, eth);
 
 	if (unlikely(netif_msg_intr(eth))) {
+		status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
 		mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
-		netdev_info(eth->netdev[0],
-			    "done tx %d, rx %d, intr 0x%08x/0x%x\n",
-			    tx_done, rx_done, status, mask);
+		dev_info(eth->dev,
+			 "done rx %d, intr 0x%08x/0x%x\n",
+			 rx_done, status, mask);
 	}
 
-	if (tx_again || rx_done == budget)
+	if (rx_done == budget)
 		return budget;
 
 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	if (status & (tx_intr | rx_intr))
+	if (status & MTK_RX_DONE_INT)
 		return budget;
 
 	napi_complete(napi);
-	mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
+	mtk_irq_enable(eth, MTK_RX_DONE_INT);
 
 	return rx_done;
 }
@@ -1225,22 +1243,26 @@ static void mtk_tx_timeout(struct net_device *dev)
 	schedule_work(&eth->pending_work);
 }
 
-static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
 {
 	struct mtk_eth *eth = _eth;
-	u32 status;
 
-	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	if (unlikely(!status))
-		return IRQ_NONE;
+	if (likely(napi_schedule_prep(&eth->rx_napi))) {
+		__napi_schedule(&eth->rx_napi);
+		mtk_irq_disable(eth, MTK_RX_DONE_INT);
+	}
 
-	if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
-		if (likely(napi_schedule_prep(&eth->rx_napi)))
-			__napi_schedule(&eth->rx_napi);
-	} else {
-		mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
+{
+	struct mtk_eth *eth = _eth;
+
+	if (likely(napi_schedule_prep(&eth->tx_napi))) {
+		__napi_schedule(&eth->tx_napi);
+		mtk_irq_disable(eth, MTK_TX_DONE_INT);
 	}
-	mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
 
 	return IRQ_HANDLED;
 }
@@ -1253,7 +1275,7 @@ static void mtk_poll_controller(struct net_device *dev)
 	u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
 
 	mtk_irq_disable(eth, int_mask);
-	mtk_handle_irq(dev->irq, dev);
+	mtk_handle_irq(dev->irq[0], dev);
 	mtk_irq_enable(eth, int_mask);
 }
 #endif
@@ -1289,6 +1311,7 @@ static int mtk_open(struct net_device *dev)
 		if (err)
 			return err;
 
+		napi_enable(&eth->tx_napi);
 		napi_enable(&eth->rx_napi);
 		mtk_irq_enable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
 	}
@@ -1337,6 +1360,7 @@ static int mtk_stop(struct net_device *dev)
 		return 0;
 
 	mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+	napi_disable(&eth->tx_napi);
 	napi_disable(&eth->rx_napi);
 
 	mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
@@ -1374,7 +1398,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 	/* Enable RX VLan Offloading */
 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
 
-	err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
+	err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
+			       dev_name(eth->dev), eth);
+	if (err)
+		return err;
+	err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
 			       dev_name(eth->dev), eth);
 	if (err)
 		return err;
@@ -1390,7 +1418,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 	mtk_w32(eth, 0, MTK_RST_GL);
 
 	/* FE int grouping */
-	mtk_w32(eth, 0, MTK_FE_INT_GRP);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
+	mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
 
 	for (i = 0; i < 2; i++) {
 		u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
@@ -1438,7 +1470,9 @@ static void mtk_uninit(struct net_device *dev)
 	phy_disconnect(mac->phy_dev);
 	mtk_mdio_cleanup(eth);
 	mtk_irq_disable(eth, ~0);
-	free_irq(dev->irq, dev);
+	free_irq(eth->irq[0], dev);
+	free_irq(eth->irq[1], dev);
+	free_irq(eth->irq[2], dev);
 }
 
 static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -1712,10 +1746,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 		dev_err(eth->dev, "error bringing up device\n");
 		goto free_netdev;
 	}
-	eth->netdev[id]->irq = eth->irq;
+	eth->netdev[id]->irq = eth->irq[0];
 	netif_info(eth, probe, eth->netdev[id],
 		   "mediatek frame engine at 0x%08lx, irq %d\n",
-		   eth->netdev[id]->base_addr, eth->netdev[id]->irq);
+		   eth->netdev[id]->base_addr, eth->irq[0]);
 
 	return 0;
 
@@ -1732,6 +1766,7 @@ static int mtk_probe(struct platform_device *pdev)
 	struct mtk_soc_data *soc;
 	struct mtk_eth *eth;
 	int err;
+	int i;
 
 	match = of_match_device(of_mtk_match, &pdev->dev);
 	soc = (struct mtk_soc_data *)match->data;
@@ -1767,10 +1802,12 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->rstc);
 	}
 
-	eth->irq = platform_get_irq(pdev, 0);
-	if (eth->irq < 0) {
-		dev_err(&pdev->dev, "no IRQ resource found\n");
-		return -ENXIO;
+	for (i = 0; i < 3; i++) {
+		eth->irq[i] = platform_get_irq(pdev, i);
+		if (eth->irq[i] < 0) {
+			dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
+			return -ENXIO;
+		}
 	}
 
 	eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
@@ -1811,7 +1848,9 @@ static int mtk_probe(struct platform_device *pdev)
 	 * for NAPI to work
 	 */
 	init_dummy_netdev(&eth->dummy_dev);
-	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
+	netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
+		       MTK_NAPI_WEIGHT);
+	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx,
 		       MTK_NAPI_WEIGHT);
 
 	platform_set_drvdata(pdev, eth);
@@ -1832,6 +1871,7 @@ static int mtk_remove(struct platform_device *pdev)
 	clk_disable_unprepare(eth->clk_gp1);
 	clk_disable_unprepare(eth->clk_gp2);
 
+	netif_napi_del(&eth->tx_napi);
 	netif_napi_del(&eth->rx_napi);
 	mtk_cleanup(eth);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 3159d2a..f82e3ac 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -68,6 +68,10 @@
 /* Unicast Filter MAC Address Register - High */
 #define MTK_GDMA_MAC_ADRH(x)	(0x50C + (x * 0x1000))
 
+/* PDMA Interrupt grouping registers */
+#define MTK_PDMA_INT_GRP1	0xa50
+#define MTK_PDMA_INT_GRP2	0xa54
+
 /* QDMA TX Queue Configuration Registers */
 #define MTK_QTX_CFG(x)		(0x1800 + (x * 0x10))
 #define QDMA_RES_THRES		4
@@ -125,6 +129,11 @@
 #define MTK_TX_DONE_INT		(MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
 				 MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
 
+/* QDMA Interrupt grouping registers */
+#define MTK_QDMA_INT_GRP1	0x1a20
+#define MTK_QDMA_INT_GRP2	0x1a24
+#define MTK_RLS_DONE_INT	BIT(0)
+
 /* QDMA Interrupt Status Register */
 #define MTK_QDMA_INT_MASK	0x1A1C
 
@@ -356,7 +365,8 @@ struct mtk_rx_ring {
  * @dma_refcnt:		track how many netdevs are using the DMA engine
  * @tx_ring:		Pointer to the memore holding info about the TX ring
  * @rx_ring:		Pointer to the memore holding info about the RX ring
- * @rx_napi:		The NAPI struct
+ * @tx_napi:		The TX NAPI struct
+ * @rx_napi:		The RX NAPI struct
  * @scratch_ring:	Newer SoCs need memory for a second HW managed TX ring
  * @phy_scratch_ring:	physical address of scratch_ring
  * @scratch_head:	The scratch memory that scratch_ring points to.
@@ -377,7 +387,7 @@ struct mtk_eth {
 	struct net_device		dummy_dev;
 	struct net_device		*netdev[MTK_MAX_DEVS];
 	struct mtk_mac			*mac[MTK_MAX_DEVS];
-	int				irq;
+	int				irq[3];
 	u32				msg_enable;
 	unsigned long			sysclk;
 	struct regmap			*ethsys;
@@ -385,6 +395,7 @@ struct mtk_eth {
 	atomic_t			dma_refcnt;
 	struct mtk_tx_ring		tx_ring;
 	struct mtk_rx_ring		rx_ring;
+	struct napi_struct		tx_napi;
 	struct napi_struct		rx_napi;
 	struct mtk_tx_dma		*scratch_ring;
 	dma_addr_t			phy_scratch_ring;
-- 
1.7.10.4



From airlied at gmail.com  Wed Jun 15 13:19:14 2016
From: airlied at gmail.com (Dave Airlie)
Date: Thu, 16 Jun 2016 06:19:14 +1000
Subject: [GIT PULL] drm/mediatek: MT8173 HDMI support
In-Reply-To: <1465811080.3553.8.camel@pengutronix.de>
References: <1465811080.3553.8.camel@pengutronix.de>
Message-ID: <CAPM=9tyMpNpzq9m4tJBe-zFzmsaRxFHYV20yv5yWmsrGeuQcqw@mail.gmail.com>

On 13 June 2016 at 19:44, Philipp Zabel <p.zabel at pengutronix.de> wrote:
> Hi Dave,
>
> please consider merging this tag, which contains the v16 MT8173 HDMI
> patches I sent on 2016-05-26, rebased onto v4.7-rc2. There have been no
> further comments.

arm32 build
 DTC     drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb
/home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:
In function ?mtk_hdmi_ddc_probe?:
/home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:322:111:
warning: format ?%llx? expects argument of type ?long long unsigned
int?, but argument 4 has type ?resource_size_t {aka unsigned int}?
[-Wformat=]
/home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:322:111:
warning: format ?%llx? expects argument of type ?long long unsigned
int?, but argument 5 has type ?resource_size_t {aka unsigned int}?
[-Wformat=]

Please fix that and resend.
Dave.


From garlic.tseng at mediatek.com  Wed Jun 15 18:40:11 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Thu, 16 Jun 2016 09:40:11 +0800
Subject: [alsa-devel] [PATCH v4 3/9] ASoC: mediatek: let mt8173 use
 mediatek common structure
In-Reply-To: <1465988242.30493.6.camel@mtksdaap41>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
 <1465802806-19197-4-git-send-email-garlic.tseng@mediatek.com>
 <1465988242.30493.6.camel@mtksdaap41>
Message-ID: <1466041211.27083.3.camel@mtksdaap41>

Hi Yingjoe, thanks for comments.

> > +config SND_SOC_MEDIATEK
> > +	tristate "SND_SOC_MEDIATEK"
> > +	depends on ARCH_MEDIATEK
> > +	help
> > +	  This adds ASoC driver for Mediatek boards
> > +	  that can be used with other codecs.
> > +	  Select Y if you have such device.
> > +	  If unsure select "N".
> > +
> >  config SND_SOC_MT8173
> >  	tristate "ASoC support for Mediatek MT8173 chip"
> >  	depends on ARCH_MEDIATEK
> > +	select SND_SOC_MEDIATEK
> >  	help
> >  	  This adds ASoC platform driver support for Mediatek MT8173 chip
> >  	  that can be used with other codecs.
> 
> It seems both SND_SOC_MT8173 and SND_SOC_MT2701 select SND_SOC_MEDIATEK,
> and enabling only SND_SOC_MEDIATEK itself is not really useful. Do we
> need to make SND_SOC_MEDIATEK user configurable option?

I'll make the config not user configurable, something like:

+config SND_SOC_MEDIATEK
+	tristate

> 
> If it is, we should at least have a proper config prompt instead of just
> "SND_SOC_MEDIATEK".
> 
> 
> > @@ -49,3 +59,4 @@ config SND_SOC_MT8173_RT5650_RT5676
> >  	  with the RT5650 and RT5676 codecs.
> >  	  Select Y if you have such device.
> >  	  If unsure select "N".
> > +
> 
> nit: This change is not necessary.

I'll fix it, thanks.

> 
> Joe.C
> 
> 




From garlic.tseng at mediatek.com  Wed Jun 15 18:41:02 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Thu, 16 Jun 2016 09:41:02 +0800
Subject: [alsa-devel] [PATCH v4 9/9] ASoC: mediatek: Add mt2701-cs42448
 driver and config option.
In-Reply-To: <1465988322.30493.7.camel@mtksdaap41>
References: <1465802806-19197-1-git-send-email-garlic.tseng@mediatek.com>
 <1465802806-19197-10-git-send-email-garlic.tseng@mediatek.com>
 <1465988322.30493.7.camel@mtksdaap41>
Message-ID: <1466041262.27083.4.camel@mtksdaap41>

On Wed, 2016-06-15 at 18:58 +0800, Yingjoe Chen wrote:
> On Mon, 2016-06-13 at 15:26 +0800, Garlic Tseng wrote:
> > +config SND_SOC_MT2701_CS42448
> > +	tristate "SND_SOC_MT2701_CS42448"
> 
> Please provide proper config prompt here.

OK I'll fix it. Thanks.

> Joe.C
> 
> 




From bibby.hsieh at mediatek.com  Wed Jun 15 20:04:33 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Thu, 16 Jun 2016 11:04:33 +0800
Subject: [PATCH 1/2] drm/mediatek: Add gamma correction
In-Reply-To: <20160614054322.GL1338@phenom.ffwll.local>
References: <1465872953-47706-1-git-send-email-bibby.hsieh@mediatek.com>
 <20160614054322.GL1338@phenom.ffwll.local>
Message-ID: <1466046273.9460.1.camel@mtksdaap41>

Hi, Daniel, 

Thank you for your suggestion, I will modify that to use the atomic
color management.

Bibby

On Tue, 2016-06-14 at 07:43 +0200, Daniel Vetter wrote:
> On Tue, Jun 14, 2016 at 10:55:52AM +0800, Bibby Hsieh wrote:
> > Apply gamma function to correct brightness values.
> > It applies arbitrary mapping curve to compensate the
> > incorrect transfer function of the panel.
> > 
> > Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
> 
> I think it would be much better to use the new atomic color management
> support, which includes gamma. See drm_crtc_enable_color_mgmt.
> -Daniel
> 
> > ---
> >  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   12 ++++++
> >  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   56 ++++++++++++++++++++++++++-
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    9 +++++
> >  4 files changed, 77 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > index 24aa3ba..1b38406 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > @@ -127,6 +127,16 @@ static void mtk_drm_crtc_reset(struct drm_crtc *crtc)
> >  	state->base.crtc = crtc;
> >  }
> >  
> > +static void mtk_crtc_gamma_set(struct drm_crtc *crtc, u16 *r,
> > +			       u16 *g, u16 *b, uint32_t start, uint32_t size)
> > +{
> > +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
> > +		mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], r, g, b, start, size);
> > +}
> > +
> >  static struct drm_crtc_state *mtk_drm_crtc_duplicate_state(struct drm_crtc *crtc)
> >  {
> >  	struct mtk_crtc_state *state;
> > @@ -418,6 +428,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
> >  	.reset			= mtk_drm_crtc_reset,
> >  	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
> >  	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
> > +	.gamma_set		= mtk_crtc_gamma_set,
> >  };
> >  
> >  static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
> > @@ -569,6 +580,7 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
> >  	if (ret < 0)
> >  		goto unprepare;
> >  
> > +	drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
> >  	priv->crtc[pipe] = &mtk_crtc->base;
> >  	priv->num_pipes++;
> >  
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> > index 81e5566..d332564 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
> > @@ -19,6 +19,7 @@
> >  #include "mtk_drm_plane.h"
> >  
> >  #define OVL_LAYER_NR	4
> > +#define MTK_LUT_SIZE	512
> >  
> >  int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
> >  void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > index 3970fcf..3fd5141 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > @@ -24,6 +24,7 @@
> >  #include "mtk_drm_drv.h"
> >  #include "mtk_drm_plane.h"
> >  #include "mtk_drm_ddp_comp.h"
> > +#include "mtk_drm_crtc.h"
> >  
> >  #define DISP_OD_EN				0x0000
> >  #define DISP_OD_INTEN				0x0008
> > @@ -38,6 +39,18 @@
> >  #define DISP_COLOR_WIDTH			0x0c50
> >  #define DISP_COLOR_HEIGHT			0x0c54
> >  
> > +#define DISP_AAL_EN				0x000
> > +#define DISP_AAL_SIZE				0x030
> > +
> > +#define DISP_GAMMA_CFG				0x020
> > +#define DISP_GAMMA_LUT				0x700
> > +
> > +#define LUT_10BIT_MASK				0x3ff
> > +
> > +#define AAL_EN			BIT(0)
> > +
> > +#define GAMMA_LUT_EN		BIT(1)
> > +
> >  #define	OD_RELAY_MODE		BIT(0)
> >  
> >  #define	UFO_BYPASS		BIT(2)
> > @@ -76,6 +89,40 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
> >  	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
> >  }
> >  
> > +static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
> > +			   unsigned int h, unsigned int vrefresh)
> > +{
> > +	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
> > +}
> > +
> > +static void mtk_aal_start(struct mtk_ddp_comp *comp)
> > +{
> > +	writel(AAL_EN, comp->regs  + DISP_AAL_EN);
> > +}
> > +
> > +static void mtk_aal_stop(struct mtk_ddp_comp *comp)
> > +{
> > +	writel_relaxed(0x0, comp->regs  + DISP_AAL_EN);
> > +}
> > +
> > +static void mtk_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
> > +			  u16 *b, uint32_t start, uint32_t size)
> > +{
> > +	int i;
> > +	unsigned int lut;
> > +	int end = (start + size > MTK_LUT_SIZE) ? MTK_LUT_SIZE : start + size;
> > +	void __iomem *lut_base;
> > +
> > +	writel(GAMMA_LUT_EN, comp->regs + DISP_GAMMA_CFG);
> > +	lut_base = comp->regs + DISP_GAMMA_LUT;
> > +	for (i = start; i < end; i++) {
> > +		lut = (((r[i] >> 6) & LUT_10BIT_MASK) << 20) +
> > +			(((g[i] >> 6) & LUT_10BIT_MASK) << 10) +
> > +			((b[i] >> 6) & LUT_10BIT_MASK);
> > +		writel(lut, (lut_base + i * 4));
> > +	}
> > +}
> > +
> >  static const struct mtk_ddp_comp_funcs ddp_color = {
> >  	.config = mtk_color_config,
> >  	.start = mtk_color_start,
> > @@ -90,6 +137,13 @@ static const struct mtk_ddp_comp_funcs ddp_ufoe = {
> >  	.start = mtk_ufoe_start,
> >  };
> >  
> > +static const struct mtk_ddp_comp_funcs ddp_aal = {
> > +	.gamma_set = mtk_gamma_set,
> > +	.config = mtk_aal_config,
> > +	.start = mtk_aal_start,
> > +	.stop = mtk_aal_stop,
> > +};
> > +
> >  static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
> >  	[MTK_DISP_OVL] = "ovl",
> >  	[MTK_DISP_RDMA] = "rdma",
> > @@ -112,7 +166,7 @@ struct mtk_ddp_comp_match {
> >  };
> >  
> >  static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> > -	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
> > +	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
> >  	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
> >  	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
> >  	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > index 6b13ba9..0832179 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > @@ -73,6 +73,8 @@ struct mtk_ddp_comp_funcs {
> >  	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
> >  	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
> >  			     struct mtk_plane_state *state);
> > +	void (*gamma_set)(struct mtk_ddp_comp *comp, u16 *r, u16 *g, u16 *b,
> > +			  u32 start, u32 size);
> >  };
> >  
> >  struct mtk_ddp_comp {
> > @@ -139,6 +141,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
> >  		comp->funcs->layer_config(comp, idx, state);
> >  }
> >  
> > +static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp, u16 *r, u16 *g,
> > +				     u16 *b, uint32_t start, uint32_t size)
> > +{
> > +	if (comp->funcs && comp->funcs->gamma_set)
> > +		comp->funcs->gamma_set(comp, r, g, b, start, size);
> > +}
> > +
> >  int mtk_ddp_comp_get_id(struct device_node *node,
> >  			enum mtk_ddp_comp_type comp_type);
> >  int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
> > -- 
> > 1.7.9.5
> > 
> 




From davem at davemloft.net  Wed Jun 15 22:20:50 2016
From: davem at davemloft.net (David Miller)
Date: Wed, 15 Jun 2016 22:20:50 -0700 (PDT)
Subject: [PATCH 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
In-Reply-To: <1466002730-58476-1-git-send-email-john@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
Message-ID: <20160615.222050.2210130284846212716.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Wed, 15 Jun 2016 16:58:46 +0200

> This series contains 2 small code cleanups that are leftovers from the
> MIPS support. There is also a small fix that adds proper locking to the
> code accessing the IRQ registers. Without this fix we saw deadlocks caused
> by the last patch of the series, which adds IRQ grouping. The grouping
> feature allows us to use different IRQs for TX and RX. By doing so we can
> use affinity to let the SoC handle the IRQs on different cores.

This patch series doesn't apply cleanly to the net-next tree, I get rejects
on patch #4.


From john at phrozen.org  Thu Jun 16 02:44:44 2016
From: john at phrozen.org (John Crispin)
Date: Thu, 16 Jun 2016 11:44:44 +0200
Subject: [PATCH 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
In-Reply-To: <20160615.222050.2210130284846212716.davem@davemloft.net>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
 <20160615.222050.2210130284846212716.davem@davemloft.net>
Message-ID: <5dbb935d-2d9a-48fe-79c7-8e8f57b27678@phrozen.org>



On 16/06/2016 07:20, David Miller wrote:
> From: John Crispin <john at phrozen.org>
> Date: Wed, 15 Jun 2016 16:58:46 +0200
> 
>> This series contains 2 small code cleanups that are leftovers from the
>> MIPS support. There is also a small fix that adds proper locking to the
>> code accessing the IRQ registers. Without this fix we saw deadlocks caused
>> by the last patch of the series, which adds IRQ grouping. The grouping
>> feature allows us to use different IRQs for TX and RX. By doing so we can
>> use affinity to let the SoC handle the IRQs on different cores.
> 
> This patch series doesn't apply cleanly to the net-next tree, I get rejects
> on patch #4.
> 

it depends on the series with the 11 fixes that i sent last week which
is however in the net tree and not the next tree (i also still had the
DQL hack in my tree). if i resend this at the start of the next rc1
would you merge it into the net tree so that it becomes part of v4.8 ?

	John


From daniel at iogearbox.net  Thu Jun 16 02:51:34 2016
From: daniel at iogearbox.net (Daniel Borkmann)
Date: Thu, 16 Jun 2016 11:51:34 +0200
Subject: [PATCH 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
In-Reply-To: <5dbb935d-2d9a-48fe-79c7-8e8f57b27678@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
 <20160615.222050.2210130284846212716.davem@davemloft.net>
 <5dbb935d-2d9a-48fe-79c7-8e8f57b27678@phrozen.org>
Message-ID: <576276A6.6040904@iogearbox.net>

On 06/16/2016 11:44 AM, John Crispin wrote:
> On 16/06/2016 07:20, David Miller wrote:
>> From: John Crispin <john at phrozen.org>
>> Date: Wed, 15 Jun 2016 16:58:46 +0200
>>
>>> This series contains 2 small code cleanups that are leftovers from the
>>> MIPS support. There is also a small fix that adds proper locking to the
>>> code accessing the IRQ registers. Without this fix we saw deadlocks caused
>>> by the last patch of the series, which adds IRQ grouping. The grouping
>>> feature allows us to use different IRQs for TX and RX. By doing so we can
>>> use affinity to let the SoC handle the IRQs on different cores.
>>
>> This patch series doesn't apply cleanly to the net-next tree, I get rejects
>> on patch #4.
>
> it depends on the series with the 11 fixes that i sent last week which
> is however in the net tree and not the next tree (i also still had the
> DQL hack in my tree). if i resend this at the start of the next rc1
> would you merge it into the net tree so that it becomes part of v4.8 ?

Well, it would rather require a merge of -net into -net-next then. But
that would be useful indeed.

Cheers,
Daniel


From mchehab at osg.samsung.com  Thu Jun 16 03:54:28 2016
From: mchehab at osg.samsung.com (Mauro Carvalho Chehab)
Date: Thu, 16 Jun 2016 07:54:28 -0300
Subject: [PATCH v3 0/9] Add MT8173 Video Decoder Driver
In-Reply-To: <1465902488.27938.7.camel@mtksdaap41>
References: <1464611363-14936-1-git-send-email-tiffany.lin@mediatek.com>
 <20160607112235.475c2e4c@recife.lan> <575746EE.3030706@cisco.com>
 <1465902488.27938.7.camel@mtksdaap41>
Message-ID: <20160616075428.0fde4aaa@recife.lan>

Em Tue, 14 Jun 2016 19:08:08 +0800
tiffany lin <tiffany.lin at mediatek.com> escreveu:

> Hi Mauro,
> 
> 
> On Wed, 2016-06-08 at 07:13 +0900, Hans Verkuil wrote:
> > 
> > On 06/07/2016 11:22 PM, Mauro Carvalho Chehab wrote:  
> > > Em Mon, 30 May 2016 20:29:14 +0800
> > > Tiffany Lin <tiffany.lin at mediatek.com> escreveu:
> > >  
> > >> ==============
> > >>   Introduction
> > >> ==============
> > >>
> > >> The purpose of this series is to add the driver for video codec hw embedded in the Mediatek's MT8173 SoCs.
> > >> Mediatek Video Codec is able to handle video decoding of in a range of formats.
> > >>
> > >> This patch series add Mediatek block format V4L2_PIX_FMT_MT21, the decoder driver will decoded bitstream to
> > >> V4L2_PIX_FMT_MT21 format.
> > >>
> > >> This patch series rely on MTK VPU driver in patch series "Add MT8173 Video Encoder Driver and VPU Driver"[1]
> > >> and patch "CHROMIUM: v4l: Add V4L2_PIX_FMT_VP9 definition"[2] for VP9 support.
> > >> Mediatek Video Decoder driver rely on VPU driver to load, communicate with VPU.
> > >>
> > >> Internally the driver uses videobuf2 framework and MTK IOMMU and MTK SMI both have been merged in v4.6-rc1.
> > >>
> > >> [1]https://patchwork.linuxtv.org/patch/33734/
> > >> [2]https://chromium-review.googlesource.com/#/c/245241/  
> > >
> > > Hmm... I'm not seeing the firmware for this driver at the
> > > linux-firmware tree:
> > > 	https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/log/
> > >
> > > Nor I'm seeing any pull request for them. Did you send it?
> > > I'll only merge the driver upstream after seeing such pull request.  
> >   
> Sorry, I am not familiar with how to upstream firmware.
> Do you mean we need to upstream vpu firmware first before merge encoder
> driver upstream?

Please look at this page:
	https://linuxtv.org/wiki/index.php/Development:_How_to_submit_patches#Firmware_submission

The information here can also be useful:
	https://www.kernel.org/doc/readme/firmware-README.AddingFirmware

In summary, you need to provide redistribution rights for the
firmware blob. You can either submit it to me or directly to
linux-firmware. In the latter, please c/c me on such patch.

Thanks!
Mauro

> 
> In
> https://git.kernel.org/cgit/linux/kernel/git/firmware/linux-firmware.git/tree/README, it mentions that 
> "To submit firmware to this repository, please send either a git binary
> diff or preferably a git pull request to: linux-firmware at kernel.org and
> also cc: to related mailing lists."
> 
> How we made a git pull request to linux-firmware at kernel.org? 
> How we find out related mailing lists?
> 
> best regards,
> Tiffany
> 
> > Mauro, are you confusing the decoder and encoder driver? I haven't thoroughly reviewed the decoder driver
> > yet, so there is no pull request for the decoder driver.
> > 
> > The only pull request I made was for the encoder driver.
> > 
> > Regards,
> > 
> > 	Hans  
> 
> 


-- 
Thanks,
Mauro


From p.zabel at pengutronix.de  Thu Jun 16 07:41:34 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Thu, 16 Jun 2016 16:41:34 +0200
Subject: [GIT PULL] drm/mediatek: MT8173 HDMI support
In-Reply-To: <CAPM=9tyMpNpzq9m4tJBe-zFzmsaRxFHYV20yv5yWmsrGeuQcqw@mail.gmail.com>
References: <1465811080.3553.8.camel@pengutronix.de>
 <CAPM=9tyMpNpzq9m4tJBe-zFzmsaRxFHYV20yv5yWmsrGeuQcqw@mail.gmail.com>
Message-ID: <1466088094.3343.36.camel@pengutronix.de>

Am Donnerstag, den 16.06.2016, 06:19 +1000 schrieb Dave Airlie:
> On 13 June 2016 at 19:44, Philipp Zabel <p.zabel at pengutronix.de> wrote:
> > Hi Dave,
> >
> > please consider merging this tag, which contains the v16 MT8173 HDMI
> > patches I sent on 2016-05-26, rebased onto v4.7-rc2. There have been no
> > further comments.
> 
> arm32 build
>  DTC     drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dtb
> /home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:
> In function ?mtk_hdmi_ddc_probe?:
> /home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:322:111:
> warning: format ?%llx? expects argument of type ?long long unsigned
> int?, but argument 4 has type ?resource_size_t {aka unsigned int}?
> [-Wformat=]
> /home/airlied/devel/kernel/drm-next/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c:322:111:
> warning: format ?%llx? expects argument of type ?long long unsigned
> int?, but argument 5 has type ?resource_size_t {aka unsigned int}?
> [-Wformat=]
> 
> Please fix that and resend.
> Dave.

I'll fix it up as follows and resend:

----------8<----------
diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
index c24bbc4..33c9e1b 100644
--- a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
+++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
@@ -319,8 +319,8 @@ static int mtk_hdmi_ddc_probe(struct platform_device *pdev)
 
 	dev_dbg(dev, "ddc->adap: %p\n", &ddc->adap);
 	dev_dbg(dev, "ddc->clk: %p\n", ddc->clk);
-	dev_dbg(dev, "physical adr: 0x%llx, end: 0x%llx\n", mem->start,
-		mem->end);
+	dev_dbg(dev, "physical adr: %pa, end: %pa\n", &mem->start,
+		&mem->end);
 
 	return 0;
 
---------->8----------

regards
Philipp



From emil.l.velikov at gmail.com  Thu Jun 16 12:14:07 2016
From: emil.l.velikov at gmail.com (Emil Velikov)
Date: Thu, 16 Jun 2016 20:14:07 +0100
Subject: [PATCH 2/2 v16] drm/bridge: Add I2C based driver for ps8640 bridge
In-Reply-To: <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>
References: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>
 <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>
Message-ID: <CACvgo53002XCPLufbJHQLC1uL1nNS7_0gh=napedetmvT6FoSw@mail.gmail.com>

Hi Jitao Shi,

A few comments/suggestions which I hope you'll find useful. Note that
I'm not an expert in the area so take them with a pinch of salt.

On 2 June 2016 at 10:57, Jitao Shi <jitao.shi at mediatek.com> wrote:

> +#define WRITE_STATUS_REG_CMD   0x01
> +#define READ_STATUS_REG_CMD    0x05
> +#define BUSY                   BIT(0)
> +#define CLEAR_ALL_PROTECT      0x00
> +#define BLK_PROTECT_BITS       0x0c
> +#define STATUS_REG_PROTECT     BIT(7)
> +#define WRITE_ENABLE_CMD       0x06
> +#define CHIP_ERASE_CMD         0xc7
> +#define MAX_DEVS               0x8
Some of the above are unused - SPI_MAX_RETRY_CNT and PAGE2_SWSPI_CTL
come to might. Please double-check and nuke any unused ones for now ?

Add blank line between the macro definitions and the struct.

> +struct ps8640_info {
> +       u8 family_id;
> +       u8 variant_id;
> +       u16 version;
> +};
> +
> +struct ps8640 {
> +       struct drm_connector connector;
> +       struct drm_bridge bridge;
> +       struct edid *edid;
> +       struct mipi_dsi_device dsi;
> +       struct i2c_client *page[MAX_DEVS];
Throw in an enum that provides symbolic names for the different i2c
clients and rename "page" to clients or anything else that makes it
clearer ? Seems like '1' and '6' are never used.

Using better names than client2/7 in the actual code will be great :-)

> +       struct i2c_client *ddc_i2c;
> +       struct regulator_bulk_data supplies[2];
> +       struct drm_panel *panel;
> +       struct gpio_desc *gpio_rst_n;
> +       struct gpio_desc *gpio_slp_n;
> +       struct gpio_desc *gpio_mode_sel_n;
What does the _n suffix mean in the above three ? Bikeshed:
reset_gpio, sleep_gpio and mode_sel(ect)_gpio could also be used ;-)

> +       bool enabled;
> +
> +       /* firmware file info */
> +       struct ps8640_info info;
> +       bool in_fw_update;
> +       /* for firmware update protect */
> +       struct mutex fw_mutex;
> +};
> +
> +static const u8 enc_ctrl_code[6] = { 0xaa, 0x55, 0x50, 0x41, 0x52, 0x44 };
These feel a bit magical. Any chance you can give them symbolic names ?

> +static const u8 hw_chip_id[4] = { 0x00, 0x0a, 0x00, 0x30 };
> +
> +static inline struct ps8640 *bridge_to_ps8640(struct drm_bridge *e)
> +{
> +       return container_of(e, struct ps8640, bridge);
> +}
> +
> +static inline struct ps8640 *connector_to_ps8640(struct drm_connector *e)
> +{
> +       return container_of(e, struct ps8640, connector);
> +}
> +
> +static int ps8640_read(struct i2c_client *client, u8 reg, u8 *data,
> +                      u16 data_len)
> +{
> +       int ret;
> +       struct i2c_msg msgs[] = {
> +               {
> +                .addr = client->addr,
> +                .flags = 0,
> +                .len = 1,
> +                .buf = &reg,
> +               },
> +               {
> +                .addr = client->addr,
> +                .flags = I2C_M_RD,
> +                .len = data_len,
> +                .buf = data,
> +               }
> +       };
> +
> +       ret = i2c_transfer(client->adapter, msgs, 2);
> +
> +       if (ret == 2)
> +               return 0;
> +       if (ret < 0)
> +               return ret;
> +       else
> +               return -EIO;
> +}
> +
> +static int ps8640_write_bytes(struct i2c_client *client, const u8 *data,
> +                             u16 data_len)
> +{
> +       int ret;
> +       struct i2c_msg msg;
> +
> +       msg.addr = client->addr;
> +       msg.flags = 0;
> +       msg.len = data_len;
> +       msg.buf = (u8 *)data;
> +
> +       ret = i2c_transfer(client->adapter, &msg, 1);
> +       if (ret == 1)
> +               return 0;
> +       if (ret < 0)
> +               return ret;
> +       else
> +               return -EIO;
> +}
> +
> +static int ps8640_write_byte(struct i2c_client *client, u8 reg,  u8 data)
> +{
> +       u8 buf[] = { reg, data };
> +
> +       return ps8640_write_bytes(client, buf, sizeof(buf));
> +}
> +
> +static void ps8640_get_mcu_fw_version(struct ps8640 *ps_bridge)
> +{
> +       struct i2c_client *client = ps_bridge->page[5];
> +       u8 fw_ver[2];
> +
> +       ps8640_read(client, 0x4, fw_ver, sizeof(fw_ver));
> +       ps_bridge->info.version = (fw_ver[0] << 8) | fw_ver[1];
> +
> +       DRM_INFO_ONCE("ps8640 rom fw version %d.%d\n", fw_ver[0], fw_ver[1]);
> +}
> +
> +static int ps8640_bridge_unmute(struct ps8640 *ps_bridge)
> +{
> +       struct i2c_client *client = ps_bridge->page[3];
> +       u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_EN };
> +
> +       return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static int ps8640_bridge_mute(struct ps8640 *ps_bridge)
> +{
> +       struct i2c_client *client = ps_bridge->page[3];
> +       u8 vdo_ctrl_buf[3] = { PAGE3_SET_ADD, VDO_CTL_ADD, VDO_DIS };
> +
> +       return ps8640_write_bytes(client, vdo_ctrl_buf, sizeof(vdo_ctrl_buf));
> +}
> +
> +static void ps8640_pre_enable(struct drm_bridge *bridge)
> +{
> +       struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +       struct i2c_client *client = ps_bridge->page[2];
> +       int err;
> +       u8 set_vdo_done;
> +       ktime_t timeout;
> +
> +       if (ps_bridge->in_fw_update)
> +               return;
> +
> +       if (ps_bridge->enabled)
> +               return;
> +
> +       err = drm_panel_prepare(ps_bridge->panel);
> +       if (err < 0) {
> +               DRM_ERROR("failed to prepare panel: %d\n", err);
> +               return;
> +       }
> +
> +       err = regulator_bulk_enable(ARRAY_SIZE(ps_bridge->supplies),
> +                                   ps_bridge->supplies);
> +       if (err < 0) {
> +               DRM_ERROR("cannot enable regulators %d\n", err);
> +               goto err_panel_unprepare;
> +       }
> +
> +       gpiod_set_value(ps_bridge->gpio_slp_n, 1);
> +       gpiod_set_value(ps_bridge->gpio_rst_n, 0);
> +       usleep_range(2000, 2500);
> +       gpiod_set_value(ps_bridge->gpio_rst_n, 1);
> +
> +       /*
> +        * Wait for the ps8640 embed mcu ready
> +        * First wait 200ms and then check the mcu ready flag every 20ms
> +        */
> +       msleep(200);
> +

> +       timeout = ktime_add_ms(ktime_get(), 200);
> +       for (;;) {
> +               err = ps8640_read(client, PAGE2_GPIO_H, &set_vdo_done, 1);
> +               if (err < 0) {
> +                       DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err);
> +                       goto err_regulators_disable;
> +               }
> +               if ((set_vdo_done & PS_GPIO9) == PS_GPIO9)
> +                       break;
> +               if (ktime_compare(ktime_get(), timeout) > 0)
> +                       break;
> +               msleep(20);
> +       }
> +
This is the first such construct in DRM. Is there anything wrong using
something like the following ? Same question applies for the rest of
the patch.


count = XX;

for (i = 0; i < count; i++) {
    err = ps8640_read(client, PAGE2_GPIO_H, &set_vdo_done, 1);
    if (err < 0) {
        DRM_ERROR("failed read PAGE2_GPIO_H: %d\n", err);
        goto err_regulators_disable;
    }
    if ((set_vdo_done & PS_GPIO9) == PS_GPIO9)
        break;

    msleep(20);
}

if (i == count) {
    print_some_warning()
    error_out()
}



> +static int ps8640_get_modes(struct drm_connector *connector)
> +{

> +
> +       edid = devm_kmalloc(dev, sizeof(edid), GFP_KERNEL);
> +       if (!edid) {
> +               DRM_ERROR("Failed to allocate EDID\n");
> +               return 0;
Remove this. drm_get_edid() already returns a kmalloc'd hunk of memory.

> +       }
> +
> +       edid = drm_get_edid(connector, ps_bridge->ddc_i2c->adapter);
> +       if (!edid)
> +               goto out;
> +


> +int ps8640_bridge_attach(struct drm_bridge *bridge)
> +{
> +       struct ps8640 *ps_bridge = bridge_to_ps8640(bridge);
> +       struct device *dev = &ps_bridge->page[0]->dev;
> +       struct device_node *np = dev->of_node;
Kill off the temporary variable. dev->of_node is not that long and
it's used only once.

> +       struct mipi_dsi_host *host = ps_bridge->dsi.host;
This looks a bit odd. Can you move it to the !dsi_node below and add a
small comment ?



> +       if (dsi_node) {
> +               host = of_find_mipi_dsi_host_by_node(dsi_node);
> +               of_node_put(dsi_node);
> +               if (!host) {
> +                       ret = -ENODEV;
> +                       goto err;
> +               }
> +       }
> +
} else {
    // Use XXX if there's no dsi host provided by DT
    host = ps_bridge->dsi.host;
}


> +static int ps8640_wait_rom_idle(struct ps8640 *ps_bridge)
> +{
> +       struct i2c_client *client = ps_bridge->page[0];
> +       int ret;
> +
> +       ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_ENABLE);
> +       if (ret)
> +               goto exit;
> +
> +       ret = ps8640_wait_spi_ready(ps_bridge);
> +       if (ret)
> +               goto err_spi;
> +
> +       ret = ps8640_wait_spi_nobusy(ps_bridge);
> +       if (ret)
> +               goto err_spi;
> +
> +       ret = ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +       if (ret)
> +               goto exit;
> +
> +       return 0;
> +
> +err_spi:
> +       ps8640_write_byte(client, PAGE2_IROM_CTRL, IROM_DISABLE);
> +exit:
> +       if (ret)
ret is always non zero here.

> +               dev_err(&client->dev, "wait ps8640 rom idle fail: %d\n", ret);
> +
> +       return ret;
> +}
> +
> +static int ps8640_spi_dl_mode(struct ps8640 *ps_bridge)
> +{
> +       struct i2c_client *client = ps_bridge->page[2];
> +       int ret;
> +
> +       /* switch ps8640 mode to spi dl mode */
> +       if (ps_bridge->gpio_mode_sel_n)
> +               gpiod_set_value(ps_bridge->gpio_mode_sel_n, 0);
> +
> +       /* reset spi interface */
> +       ret = ps8640_write_byte(client, PAGE2_SW_RESET,
> +                               SPI_SW_RESET | MPU_SW_RESET);
> +       if (ret)
> +               goto exit;
> +
> +       ret = ps8640_write_byte(client, PAGE2_SW_RESET, MPU_SW_RESET);
> +       if (ret)
> +               goto exit;
> +
Add "return 0;" ...

> +exit:
> +       if (ret)
... drop this check.

> +               dev_err(&client->dev, "fail reset spi interface: %d\n", ret);
> +
> +       return ret;
> +}
> +
> +static int ps8640_rom_prepare(struct ps8640 *ps_bridge)
> +{


> +       for (i = 0; i < 6; i++)
s/6/ARRAY_SIZE(enc_ctrl_code)/

> +               ps8640_write_byte(client, PAGE2_ENCTLSPI_WR, enc_ctrl_code[i]);


> +static int ps8640_write_rom(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> +       struct i2c_client *client = ps_bridge->page[0];
> +       struct device *dev = &client->dev;
> +       struct i2c_client *client2 = ps_bridge->page[2];
> +       struct i2c_client *client7 = ps_bridge->page[7];
> +       size_t pos;
> +       u8 buf[257], rom_page_id_buf[3];
> +       int ret;
> +       u16 cpy_len;
> +
> +       ps8640_write_byte(client2, PAGE2_SPI_CFG3, I2C_TO_SPI_RESET);
> +       msleep(100);
> +       ps8640_write_byte(client2, PAGE2_SPI_CFG3, 0x00);
> +
> +       for (pos = 0; pos < fw->size; pos += cpy_len) {
> +               rom_page_id_buf[0] = PAGE2_ROMADD_BYTE1;
> +               rom_page_id_buf[1] = pos >> 8;
> +               rom_page_id_buf[2] = pos >> 16;
Reuse buf[], the stack is getting big enough already ?

> +               ret = ps8640_write_bytes(client2, rom_page_id_buf, 3);
> +               if (ret)
> +                       goto error;
> +               cpy_len = fw->size >= 256 + pos ? 256 : fw->size - pos;
cpy_len = min(256, fw->size - pos); perhaps ?

> +               buf[0] = 0;
> +               memcpy(buf + 1, fw->data + pos, cpy_len);
> +               ret = ps8640_write_bytes(client7, buf, cpy_len + 1);
> +               if (ret)
> +                       goto error;
> +
> +               dev_dbg(dev, "fw update completed %zu / %zu bytes\n", pos,
> +                       fw->size);
> +       }
> +       return 0;
> +
> +error:
> +       dev_err(dev, "failed write external flash, %d\n", ret);
> +       return ret;
> +}
> +
> +static int ps8640_spi_normal_mode(struct ps8640 *ps_bridge)
> +{
Is it that ps8640_spi_send_cmd()... 'cannot fail' in here, unlike in
ps8640_spi_dl_mode() and ps8640_rom_prepare() ? If so it might be
worth adding a line or two of comment and making
ps8640_spi_normal_mode() return void.


> +static int ps8640_load_fw(struct ps8640 *ps_bridge, const struct firmware *fw)
> +{
> +       struct i2c_client *client = ps_bridge->page[0];
> +       struct device *dev = &client->dev;
> +       int ret;
> +       bool ps8640_status_backup = ps_bridge->enabled;
> +
> +       ret = ps8640_validate_firmware(ps_bridge, fw);
> +       if (ret)
> +               return ret;
> +
> +       mutex_lock(&ps_bridge->fw_mutex);
> +       if (!ps_bridge->in_fw_update) {
> +               if (!ps8640_status_backup)
> +                       ps8640_pre_enable(&ps_bridge->bridge);
> +
> +               ret = ps8640_enter_bl(ps_bridge);
IMHO open-coding ps8640_enter_bl/ps8640_exit_bl and having the
in_fw_update manipulation visible will make things more obvious.

> +               if (ret)
> +                       goto exit;
> +       }
> +
> +       ret = ps8640_rom_prepare(ps_bridge);
> +       if (ret)
> +               goto exit;
> +
> +       ret = ps8640_write_rom(ps_bridge, fw);
> +
> +exit:
> +       if (ret)
> +               dev_err(dev, "Failed to load firmware, %d\n", ret);
> +

> +       ps8640_exit_bl(ps_bridge, fw);
> +       if (!ps8640_status_backup)
> +               ps8640_post_disable(&ps_bridge->bridge);
Why are we calling these even if we never executed
ps8640_pre_enable/ps8640_enter_bl above ?


> +static ssize_t ps8640_update_fw_store(struct device *dev,
> +                                     struct device_attribute *attr,
> +                                     const char *buf, size_t count)
> +{
> +       struct i2c_client *client = to_i2c_client(dev);
> +       struct ps8640 *ps_bridge = i2c_get_clientdata(client);
> +       const struct firmware *fw;
> +       int error;
> +
> +       error = request_firmware(&fw, PS_FW_NAME, dev);
Can the device operate without a firmware ? If not, why is the
firmware loaded so later/after user interaction (via sysfs) ? I don't
recall any other driver in DRM to use such an approach.


Regards,
Emil


From robh at kernel.org  Thu Jun 16 14:06:57 2016
From: robh at kernel.org (Rob Herring)
Date: Thu, 16 Jun 2016 16:06:57 -0500
Subject: [PATCH 1/2] Document: DT: Add bindings for mediatek MT6755 SoC
 Platform
In-Reply-To: <1465870805-10451-2-git-send-email-mars.cheng@mediatek.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
 <1465870805-10451-2-git-send-email-mars.cheng@mediatek.com>
Message-ID: <20160616210657.GA16085@rob-hp-laptop>

On Tue, Jun 14, 2016 at 10:20:04AM +0800, Mars Cheng wrote:
> This adds DT binding documentation for Mediatek MT6755.
> 
> Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
> ---
>  Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
>  .../interrupt-controller/mediatek,sysirq.txt       |    1 +
>  .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
>  3 files changed, 6 insertions(+)

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


From daniel.lezcano at linaro.org  Thu Jun 16 14:26:23 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu, 16 Jun 2016 23:26:23 +0200
Subject: [PATCH V2 04/63] clocksource/drivers/mkt_timer: Convert init function
 to return error
In-Reply-To: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1466112442-31105-5-git-send-email-daniel.lezcano@linaro.org>

The init functions do not return any error. They behave as the following:

 - panic, thus leading to a kernel crash while another timer may work and
   make the system boot up correctly

 or

 - print an error and let the caller unaware if the state of the system

Change that by converting the init functions to return an error conforming
to the CLOCKSOURCE_OF_RET prototype.

Proper error handling (rollback, errno value) will be changed later case
by case, thus this change just return back an error or success in the init
function.

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 drivers/clocksource/mtk_timer.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 7e583f8..432a2c0 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -181,7 +181,7 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
 			evt->gpt_base + GPT_IRQ_EN_REG);
 }
 
-static void __init mtk_timer_init(struct device_node *node)
+static int __init mtk_timer_init(struct device_node *node)
 {
 	struct mtk_clock_event_device *evt;
 	struct resource res;
@@ -190,7 +190,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	evt = kzalloc(sizeof(*evt), GFP_KERNEL);
 	if (!evt)
-		return;
+		return -ENOMEM;
 
 	evt->dev.name = "mtk_tick";
 	evt->dev.rating = 300;
@@ -248,7 +248,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	mtk_timer_enable_irq(evt, GPT_CLK_EVT);
 
-	return;
+	return 0;
 
 err_clk_disable:
 	clk_disable_unprepare(clk);
@@ -262,5 +262,7 @@ err_mem:
 	release_mem_region(res.start, resource_size(&res));
 err_kzalloc:
 	kfree(evt);
+
+	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
-- 
1.9.1



From daniel.lezcano at linaro.org  Thu Jun 16 14:27:22 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Thu, 16 Jun 2016 23:27:22 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>

All the clocksource drivers's init function are now converted to return
an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
clksrc-of table.

Let's convert back the names:
 - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
 - clksrc-of-ret              => clksrc-of

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
---
 arch/arc/kernel/time.c                    |  6 +++---
 arch/arm/kernel/smp_twd.c                 |  6 +++---
 arch/microblaze/kernel/timer.c            |  2 +-
 arch/mips/ralink/cevt-rt3352.c            |  2 +-
 arch/nios2/kernel/time.c                  |  2 +-
 drivers/clocksource/arm_arch_timer.c      |  6 +++---
 drivers/clocksource/arm_global_timer.c    |  2 +-
 drivers/clocksource/armv7m_systick.c      |  2 +-
 drivers/clocksource/asm9260_timer.c       |  2 +-
 drivers/clocksource/bcm2835_timer.c       |  2 +-
 drivers/clocksource/bcm_kona_timer.c      |  4 ++--
 drivers/clocksource/cadence_ttc_timer.c   |  2 +-
 drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
 drivers/clocksource/clksrc-probe.c        | 14 --------------
 drivers/clocksource/clksrc_st_lpc.c       |  2 +-
 drivers/clocksource/clps711x-timer.c      |  2 +-
 drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
 drivers/clocksource/exynos_mct.c          |  4 ++--
 drivers/clocksource/fsl_ftm_timer.c       |  2 +-
 drivers/clocksource/h8300_timer16.c       |  2 +-
 drivers/clocksource/h8300_timer8.c        |  2 +-
 drivers/clocksource/h8300_tpu.c           |  2 +-
 drivers/clocksource/meson6_timer.c        |  2 +-
 drivers/clocksource/mips-gic-timer.c      |  2 +-
 drivers/clocksource/moxart_timer.c        |  2 +-
 drivers/clocksource/mps2-timer.c          |  2 +-
 drivers/clocksource/mtk_timer.c           |  2 +-
 drivers/clocksource/mxs_timer.c           |  2 +-
 drivers/clocksource/nomadik-mtu.c         |  2 +-
 drivers/clocksource/pxa_timer.c           |  2 +-
 drivers/clocksource/qcom-timer.c          |  4 ++--
 drivers/clocksource/rockchip_timer.c      |  8 ++++----
 drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
 drivers/clocksource/sun4i_timer.c         |  2 +-
 drivers/clocksource/tango_xtal.c          |  2 +-
 drivers/clocksource/tegra20_timer.c       |  4 ++--
 drivers/clocksource/time-armada-370-xp.c  |  6 +++---
 drivers/clocksource/time-efm32.c          |  4 ++--
 drivers/clocksource/time-lpc32xx.c        |  2 +-
 drivers/clocksource/time-orion.c          |  2 +-
 drivers/clocksource/time-pistachio.c      |  2 +-
 drivers/clocksource/timer-atlas7.c        |  2 +-
 drivers/clocksource/timer-atmel-pit.c     |  2 +-
 drivers/clocksource/timer-atmel-st.c      |  2 +-
 drivers/clocksource/timer-digicolor.c     |  2 +-
 drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
 drivers/clocksource/timer-integrator-ap.c |  2 +-
 drivers/clocksource/timer-keystone.c      |  2 +-
 drivers/clocksource/timer-nps.c           |  4 ++--
 drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
 drivers/clocksource/timer-prima2.c        |  2 +-
 drivers/clocksource/timer-sp804.c         |  4 ++--
 drivers/clocksource/timer-stm32.c         |  2 +-
 drivers/clocksource/timer-sun5i.c         |  4 ++--
 drivers/clocksource/timer-ti-32k.c        |  2 +-
 drivers/clocksource/timer-u300.c          |  2 +-
 drivers/clocksource/versatile.c           |  4 ++--
 drivers/clocksource/vf_pit_timer.c        |  2 +-
 drivers/clocksource/vt8500_timer.c        |  2 +-
 drivers/clocksource/zevio-timer.c         |  2 +-
 include/asm-generic/vmlinux.lds.h         |  2 --
 include/linux/clocksource.h               |  5 +----
 62 files changed, 98 insertions(+), 117 deletions(-)

diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c
index 36110cd..ee00f2f 100644
--- a/arch/arc/kernel/time.c
+++ b/arch/arc/kernel/time.c
@@ -130,7 +130,7 @@ static int __init arc_cs_setup_gfrc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
+CLOCKSOURCE_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
 
 #endif
 
@@ -192,7 +192,7 @@ static int __init arc_cs_setup_rtc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
+CLOCKSOURCE_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
 
 #endif
 
@@ -379,7 +379,7 @@ static int __init arc_of_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
 
 /*
  * Called from start_kernel() - boot CPU only
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 2b24be4..b6ec65e 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -412,7 +412,7 @@ out:
 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
 	return err;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
 #endif
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index 7f35e7b..5bbf38b 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -332,5 +332,5 @@ static int __init xilinx_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(xilinx_timer, "xlnx,xps-timer-1.00.a",
+CLOCKSOURCE_OF_DECLARE(xilinx_timer, "xlnx,xps-timer-1.00.a",
 		       xilinx_timer_init);
diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
index f2d3c79..f24eee0 100644
--- a/arch/mips/ralink/cevt-rt3352.c
+++ b/arch/mips/ralink/cevt-rt3352.c
@@ -150,4 +150,4 @@ static int __init ralink_systick_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(systick, "ralink,cevt-systick", ralink_systick_init);
+CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c
index 9fa7739..f337eef 100644
--- a/arch/nios2/kernel/time.c
+++ b/arch/nios2/kernel/time.c
@@ -354,4 +354,4 @@ void __init time_init(void)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
+CLOCKSOURCE_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d0cda68..9e33309 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -784,8 +784,8 @@ static int __init arch_timer_of_init(struct device_node *np)
 
 	return arch_timer_init();
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
-CLOCKSOURCE_OF_DECLARE_RET(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
 
 static int __init arch_timer_mem_init(struct device_node *np)
 {
@@ -868,7 +868,7 @@ out:
 	of_node_put(best_frame);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer_mem, "arm,armv7-timer-mem",
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
 		       arch_timer_mem_init);
 
 #ifdef CONFIG_ACPI
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 40104fc..2a9ceb6 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -358,5 +358,5 @@ out_unmap:
 }
 
 /* Only tested on r2p2 and r3p0  */
-CLOCKSOURCE_OF_DECLARE_RET(arm_gt, "arm,cortex-a9-global-timer",
+CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
 			global_timer_of_register);
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
index 2b55410..e93af1f 100644
--- a/drivers/clocksource/armv7m_systick.c
+++ b/drivers/clocksource/armv7m_systick.c
@@ -81,5 +81,5 @@ out_unmap:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(arm_systick, "arm,armv7m-systick",
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
 			system_timer_of_register);
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index d113c02..1ba871b 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -238,5 +238,5 @@ static int __init asm9260_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(asm9260_timer, "alphascale,asm9260-timer",
+CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer",
 		asm9260_timer_init);
diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index 2dcf896..e71acf2 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -142,5 +142,5 @@ static int __init bcm2835_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(bcm2835, "brcm,bcm2835-system-timer",
+CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
 			bcm2835_timer_init);
diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
index 98bc2a2..ee07e7e3 100644
--- a/drivers/clocksource/bcm_kona_timer.c
+++ b/drivers/clocksource/bcm_kona_timer.c
@@ -200,9 +200,9 @@ static int __init kona_timer_init(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(brcm_kona, "brcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
 /*
  * bcm,kona-timer is deprecated by brcm,kona-timer
  * being kept here for driver compatibility
  */
-CLOCKSOURCE_OF_DECLARE_RET(bcm_kona, "bcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index e2e7631..388a77b 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -539,4 +539,4 @@ static int __init ttc_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ttc, "cdns,ttc", ttc_timer_init);
+CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index 5a59d29..77a365f 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -86,5 +86,5 @@ static int __init clksrc_dbx500_prcmu_init(struct device_node *node)
 #endif
 	return clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
 }
-CLOCKSOURCE_OF_DECLARE_RET(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
+CLOCKSOURCE_OF_DECLARE(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
 		       clksrc_dbx500_prcmu_init);
diff --git a/drivers/clocksource/clksrc-probe.c b/drivers/clocksource/clksrc-probe.c
index 5fa6a55..bc62be9 100644
--- a/drivers/clocksource/clksrc-probe.c
+++ b/drivers/clocksource/clksrc-probe.c
@@ -20,19 +20,14 @@
 #include <linux/clocksource.h>
 
 extern struct of_device_id __clksrc_of_table[];
-extern struct of_device_id __clksrc_ret_of_table[];
 
 static const struct of_device_id __clksrc_of_table_sentinel
 	__used __section(__clksrc_of_table_end);
 
-static const struct of_device_id __clksrc_ret_of_table_sentinel
-	__used __section(__clksrc_ret_of_table_end);
-
 void __init clocksource_probe(void)
 {
 	struct device_node *np;
 	const struct of_device_id *match;
-	of_init_fn_1 init_func;
 	of_init_fn_1_ret init_func_ret;
 	unsigned clocksources = 0;
 	int ret;
@@ -41,15 +36,6 @@ void __init clocksource_probe(void)
 		if (!of_device_is_available(np))
 			continue;
 
-		init_func = match->data;
-		init_func(np);
-		clocksources++;
-	}
-
-	for_each_matching_node_and_match(np, __clksrc_ret_of_table, &match) {
-		if (!of_device_is_available(np))
-			continue;
-
 		init_func_ret = match->data;
 
 		ret = init_func_ret(np);
diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c
index c9022a9..03cc492 100644
--- a/drivers/clocksource/clksrc_st_lpc.c
+++ b/drivers/clocksource/clksrc_st_lpc.c
@@ -132,4 +132,4 @@ static int __init st_clksrc_of_register(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ddata, "st,stih407-lpc", st_clksrc_of_register);
+CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index 3b66198..84aed78 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np)
 		return -EINVAL;
 	}
 }
-CLOCKSOURCE_OF_DECLARE_RET(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
+CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
 #endif
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 4985a2c..aee6c0d 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -167,7 +167,7 @@ static int __init dw_apb_timer_init(struct device_node *timer)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index f6caed0..0d18dd4b 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -627,5 +627,5 @@ static int __init mct_init_ppi(struct device_node *np)
 {
 	return mct_init_dt(np, MCT_INT_PPI);
 }
-CLOCKSOURCE_OF_DECLARE_RET(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
-CLOCKSOURCE_OF_DECLARE_RET(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
+CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
+CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
index 9ad4ca3..738515b 100644
--- a/drivers/clocksource/fsl_ftm_timer.c
+++ b/drivers/clocksource/fsl_ftm_timer.c
@@ -369,4 +369,4 @@ err:
 	kfree(priv);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(flextimer, "fsl,ftm-timer", ftm_timer_init);
+CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
index 9d99fc8..07d9d5b 100644
--- a/drivers/clocksource/h8300_timer16.c
+++ b/drivers/clocksource/h8300_timer16.c
@@ -187,5 +187,5 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_16bit, "renesas,16bit-timer",
+CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
 			   h8300_16timer_init);
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
index 0292a19..546bb18 100644
--- a/drivers/clocksource/h8300_timer8.c
+++ b/drivers/clocksource/h8300_timer8.c
@@ -215,4 +215,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
+CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
index 4faf718..7bdf199 100644
--- a/drivers/clocksource/h8300_tpu.c
+++ b/drivers/clocksource/h8300_tpu.c
@@ -154,4 +154,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_tpu, "renesas,tpu", h8300_tpu_init);
+CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
index 3a6e78f..52af591 100644
--- a/drivers/clocksource/meson6_timer.c
+++ b/drivers/clocksource/meson6_timer.c
@@ -174,5 +174,5 @@ static int __init meson6_timer_init(struct device_node *node)
 					1, 0xfffe);
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(meson6, "amlogic,meson6-timer",
+CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
 		       meson6_timer_init);
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index b164b87..1572c7a 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -222,5 +222,5 @@ static void __init gic_clocksource_of_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mips_gic_timer, "mti,gic-timer",
+CLOCKSOURCE_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
 		       gic_clocksource_of_init);
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
index b9c30cd..8414544 100644
--- a/drivers/clocksource/moxart_timer.c
+++ b/drivers/clocksource/moxart_timer.c
@@ -178,4 +178,4 @@ static int __init moxart_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(moxart, "moxa,moxart-timer", moxart_timer_init);
+CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
index c303fa9..3e4431e 100644
--- a/drivers/clocksource/mps2-timer.c
+++ b/drivers/clocksource/mps2-timer.c
@@ -274,4 +274,4 @@ static int __init mps2_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(mps2_timer, "arm,mps2-timer", mps2_timer_init);
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 432a2c0..9065949 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -265,4 +265,4 @@ err_kzalloc:
 
 	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c
index 17b9d19..630a8d3 100644
--- a/drivers/clocksource/mxs_timer.c
+++ b/drivers/clocksource/mxs_timer.c
@@ -295,4 +295,4 @@ static int __init mxs_timer_init(struct device_node *np)
 
 	return setup_irq(irq, &mxs_timer_irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(mxs, "fsl,timrot", mxs_timer_init);
+CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
index d2be5b3..3c124d1 100644
--- a/drivers/clocksource/nomadik-mtu.c
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -284,5 +284,5 @@ static int __init nmdk_timer_of_init(struct device_node *node)
 
 	return nmdk_timer_init(base, irq, pclk, clk);
 }
-CLOCKSOURCE_OF_DECLARE_RET(nomadik_mtu, "st,nomadik-mtu",
+CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
 		       nmdk_timer_of_init);
diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c
index 59af75c..937e10b 100644
--- a/drivers/clocksource/pxa_timer.c
+++ b/drivers/clocksource/pxa_timer.c
@@ -213,7 +213,7 @@ static int __init pxa_timer_dt_init(struct device_node *np)
 
 	return pxa_timer_common_init(irq, clk_get_rate(clk));
 }
-CLOCKSOURCE_OF_DECLARE_RET(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
+CLOCKSOURCE_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
 
 /*
  * Legacy timer init for non device-tree boards.
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index 79f73bd..6625763 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -273,5 +273,5 @@ static int __init msm_dt_timer_init(struct device_node *np)
 
 	return msm_timer_init(freq, 32, irq, !!percpu_offset);
 }
-CLOCKSOURCE_OF_DECLARE_RET(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index d10bdee..d10e7a5 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -200,7 +200,7 @@ static int __init rk3399_timer_init(struct device_node *np)
 	return rk_timer_init(np, TIMER_CONTROL_REG3399);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
-			   rk3288_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
-			   rk3399_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
+		       rk3288_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
+		       rk3399_timer_init);
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
index 27a9797..54565bd 100644
--- a/drivers/clocksource/samsung_pwm_timer.c
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -466,7 +466,7 @@ static int __init s3c2410_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c24xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s3c64xx_variant = {
 	.bits		= 32,
@@ -479,7 +479,7 @@ static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c64xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p64x0_variant = {
 	.bits		= 32,
@@ -492,7 +492,7 @@ static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p64x0_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p_variant = {
 	.bits		= 32,
@@ -505,5 +505,5 @@ static int __init s5p_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
 #endif
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index 4453730..97669ee 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -226,5 +226,5 @@ static int __init sun4i_timer_init(struct device_node *node)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun4i, "allwinner,sun4i-a10-timer",
+CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
 		       sun4i_timer_init);
diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
index 7dc716c..12fcef8 100644
--- a/drivers/clocksource/tango_xtal.c
+++ b/drivers/clocksource/tango_xtal.c
@@ -53,4 +53,4 @@ static int __init tango_clocksource_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(tango, "sigma,tick-counter", tango_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
index 543c37e..f960891 100644
--- a/drivers/clocksource/tegra20_timer.c
+++ b/drivers/clocksource/tegra20_timer.c
@@ -237,7 +237,7 @@ static int __init tegra20_init_timer(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
+CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
 
 static int __init tegra20_init_rtc(struct device_node *np)
 {
@@ -261,4 +261,4 @@ static int __init tegra20_init_rtc(struct device_node *np)
 
 	return register_persistent_clock(NULL, tegra_read_persistent_clock64);
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
+CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index bc4ab48..a4e5923 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -371,7 +371,7 @@ static int __init armada_xp_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_xp, "marvell,armada-xp-timer",
+CLOCKSOURCE_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
 		       armada_xp_timer_init);
 
 static int __init armada_375_timer_init(struct device_node *np)
@@ -409,7 +409,7 @@ static int __init armada_375_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_375, "marvell,armada-375-timer",
+CLOCKSOURCE_OF_DECLARE(armada_375, "marvell,armada-375-timer",
 		       armada_375_timer_init);
 
 static int __init armada_370_timer_init(struct device_node *np)
@@ -432,5 +432,5 @@ static int __init armada_370_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_370, "marvell,armada-370-timer",
+CLOCKSOURCE_OF_DECLARE(armada_370, "marvell,armada-370-timer",
 		       armada_370_timer_init);
diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c
index b71ffc6..10a0521 100644
--- a/drivers/clocksource/time-efm32.c
+++ b/drivers/clocksource/time-efm32.c
@@ -282,5 +282,5 @@ static int __init efm32_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(efm32compat, "efm32,timer", efm32_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(efm32, "energymicro,efm32-timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
index cb5b866..9649cfd 100644
--- a/drivers/clocksource/time-lpc32xx.c
+++ b/drivers/clocksource/time-lpc32xx.c
@@ -311,4 +311,4 @@ static int __init lpc32xx_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
+CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
index 5fdeb5d..a28f496 100644
--- a/drivers/clocksource/time-orion.c
+++ b/drivers/clocksource/time-orion.c
@@ -167,4 +167,4 @@ static int __init orion_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(orion_timer, "marvell,orion-timer", orion_timer_init);
+CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
index adaaec5..a7d9a08 100644
--- a/drivers/clocksource/time-pistachio.c
+++ b/drivers/clocksource/time-pistachio.c
@@ -214,5 +214,5 @@ static int __init pistachio_clksrc_of_init(struct device_node *node)
 	sched_clock_register(pistachio_read_sched_clock, 32, rate);
 	return clocksource_register_hz(&pcs_gpt.cs, rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(pistachio_gptimer, "img,pistachio-gptimer",
+CLOCKSOURCE_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer",
 		       pistachio_clksrc_of_init);
diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c
index 7b1a007..90f8fbc 100644
--- a/drivers/clocksource/timer-atlas7.c
+++ b/drivers/clocksource/timer-atlas7.c
@@ -304,4 +304,4 @@ static int __init sirfsoc_of_timer_init(struct device_node *np)
 
 	return sirfsoc_atlas7_timer_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index ffaca7c..1ffac0c 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -270,5 +270,5 @@ static int __init at91sam926x_pit_dt_init(struct device_node *node)
 
 	return at91sam926x_pit_common_init(data);
 }
-CLOCKSOURCE_OF_DECLARE_RET(at91sam926x_pit, "atmel,at91sam9260-pit",
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
 		       at91sam926x_pit_dt_init);
diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c
index e9331d3..e90ab5b 100644
--- a/drivers/clocksource/timer-atmel-st.c
+++ b/drivers/clocksource/timer-atmel-st.c
@@ -260,5 +260,5 @@ static int __init atmel_st_timer_init(struct device_node *node)
 	/* register clocksource */
 	return clocksource_register_hz(&clk32k, sclk_rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(atmel_st_timer, "atmel,at91rm9200-st",
+CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
 		       atmel_st_timer_init);
diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c
index b929061..10318cc 100644
--- a/drivers/clocksource/timer-digicolor.c
+++ b/drivers/clocksource/timer-digicolor.c
@@ -202,5 +202,5 @@ static int __init digicolor_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(conexant_digicolor, "cnxt,cx92755-timer",
+CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
 		       digicolor_timer_init);
diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index d5640a74..f595460 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -545,15 +545,15 @@ static int __init imx6dl_timer_init_dt(struct device_node *np)
 	return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
index 675face..df6e672 100644
--- a/drivers/clocksource/timer-integrator-ap.c
+++ b/drivers/clocksource/timer-integrator-ap.c
@@ -232,5 +232,5 @@ static int __init integrator_ap_timer_init_of(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(integrator_ap_timer, "arm,integrator-timer",
+CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
 		       integrator_ap_timer_init_of);
diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c
index 4199823..ab68a47 100644
--- a/drivers/clocksource/timer-keystone.c
+++ b/drivers/clocksource/timer-keystone.c
@@ -226,5 +226,5 @@ err:
 	return error;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(keystone_timer, "ti,keystone-timer",
+CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
 			   keystone_timer_init);
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index b5c7b2b..70c149a 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -96,5 +96,5 @@ static int __init nps_timer_init(struct device_node *node)
 	return nps_setup_clocksource(node, clk);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ezchip_nps400_clksrc, "ezchip,nps400-timer",
-			   nps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
+		       nps_timer_init);
diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
index 0d99f40..bd887e2 100644
--- a/drivers/clocksource/timer-oxnas-rps.c
+++ b/drivers/clocksource/timer-oxnas-rps.c
@@ -293,5 +293,5 @@ err_alloc:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ox810se_rps,
-			   "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ox810se_rps,
+		       "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 7b1084d..dae8a66 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -246,5 +246,5 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_prima2_timer,
+CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
 	"sirf,prima2-tick", sirfsoc_prima2_timer_init);
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
index 7c2944ad..a2b28da 100644
--- a/drivers/clocksource/timer-sp804.c
+++ b/drivers/clocksource/timer-sp804.c
@@ -287,7 +287,7 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sp804, "arm,sp804", sp804_of_init);
+CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
 
 static int __init integrator_cp_of_init(struct device_node *np)
 {
@@ -336,4 +336,4 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
+CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index d5bf352..1b2574c 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -187,4 +187,4 @@ err_clk_get:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(stm32, "st,stm32-timer", stm32_clockevent_init);
+CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index f0a3ffb..c184eb8 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -346,7 +346,7 @@ static int __init sun5i_timer_init(struct device_node *node)
 
 	return sun5i_setup_clockevent(node, timer_base, clk, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun5i_a13, "allwinner,sun5i-a13-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
 			   sun5i_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(sun7i_a20, "allwinner,sun7i-a20-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
 			   sun5i_timer_init);
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index e4ad3c6e..92b7e39 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -124,5 +124,5 @@ static int __init ti_32k_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ti_32k_timer, "ti,omap-counter32k",
+CLOCKSOURCE_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
 		ti_32k_timer_init);
diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
index a6a0dec..704e40c 100644
--- a/drivers/clocksource/timer-u300.c
+++ b/drivers/clocksource/timer-u300.c
@@ -458,5 +458,5 @@ static int __init u300_timer_init_of(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(u300_timer, "stericsson,u300-apptimer",
+CLOCKSOURCE_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
 		       u300_timer_init_of);
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
index 8daeffa..220b490 100644
--- a/drivers/clocksource/versatile.c
+++ b/drivers/clocksource/versatile.c
@@ -38,7 +38,7 @@ static int __init versatile_sched_clock_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(vexpress, "arm,vexpress-sysreg",
+CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg",
 		       versatile_sched_clock_init);
-CLOCKSOURCE_OF_DECLARE_RET(versatile, "arm,versatile-sysreg",
+CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg",
 		       versatile_sched_clock_init);
diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
index ca4dff4..55d8d84 100644
--- a/drivers/clocksource/vf_pit_timer.c
+++ b/drivers/clocksource/vf_pit_timer.c
@@ -201,4 +201,4 @@ static int __init pit_timer_init(struct device_node *np)
 
 	return pit_clockevent_init(clk_rate, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(vf610, "fsl,vf610-pit", pit_timer_init);
+CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
index 1bc8707..b150694 100644
--- a/drivers/clocksource/vt8500_timer.c
+++ b/drivers/clocksource/vt8500_timer.c
@@ -165,4 +165,4 @@ static int __init vt8500_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(vt8500, "via,vt8500-timer", vt8500_timer_init);
+CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
index cb4cf056..9a53f5e 100644
--- a/drivers/clocksource/zevio-timer.c
+++ b/drivers/clocksource/zevio-timer.c
@@ -215,4 +215,4 @@ static int __init zevio_timer_init(struct device_node *node)
 	return zevio_timer_add(node);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
+CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 8c6c626..6a67ab9 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -173,7 +173,6 @@
 	*(__##name##_of_table_end)
 
 #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
-#define CLKSRC_RET_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc_ret)
 #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
 #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
 #define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
@@ -532,7 +531,6 @@
 	CLK_OF_TABLES()							\
 	RESERVEDMEM_OF_TABLES()						\
 	CLKSRC_OF_TABLES()						\
-	CLKSRC_RET_OF_TABLES()						\
 	IOMMU_OF_TABLES()						\
 	CPU_METHOD_OF_TABLES()						\
 	CPUIDLE_METHOD_OF_TABLES()					\
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 15c3839..0839818 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -244,10 +244,7 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
 extern int clocksource_i8253_init(void);
 
 #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
-	OF_DECLARE_1(clksrc, name, compat, fn)
-
-#define CLOCKSOURCE_OF_DECLARE_RET(name, compat, fn) \
-	OF_DECLARE_1_RET(clksrc_ret, name, compat, fn)
+	OF_DECLARE_1_RET(clksrc, name, compat, fn)
 
 #ifdef CONFIG_CLKSRC_PROBE
 extern void clocksource_probe(void);
-- 
1.9.1



From davem at davemloft.net  Thu Jun 16 17:27:26 2016
From: davem at davemloft.net (David Miller)
Date: Thu, 16 Jun 2016 17:27:26 -0700 (PDT)
Subject: [PATCH 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
In-Reply-To: <5dbb935d-2d9a-48fe-79c7-8e8f57b27678@phrozen.org>
References: <1466002730-58476-1-git-send-email-john@phrozen.org>
 <20160615.222050.2210130284846212716.davem@davemloft.net>
 <5dbb935d-2d9a-48fe-79c7-8e8f57b27678@phrozen.org>
Message-ID: <20160616.172726.2001945071088559824.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Thu, 16 Jun 2016 11:44:44 +0200

> 
> 
> On 16/06/2016 07:20, David Miller wrote:
>> From: John Crispin <john at phrozen.org>
>> Date: Wed, 15 Jun 2016 16:58:46 +0200
>> 
>>> This series contains 2 small code cleanups that are leftovers from the
>>> MIPS support. There is also a small fix that adds proper locking to the
>>> code accessing the IRQ registers. Without this fix we saw deadlocks caused
>>> by the last patch of the series, which adds IRQ grouping. The grouping
>>> feature allows us to use different IRQs for TX and RX. By doing so we can
>>> use affinity to let the SoC handle the IRQs on different cores.
>> 
>> This patch series doesn't apply cleanly to the net-next tree, I get rejects
>> on patch #4.
>> 
> 
> it depends on the series with the 11 fixes that i sent last week which
> is however in the net tree and not the next tree (i also still had the
> DQL hack in my tree).

Then you need to tell me about such dependencies in your submission.



From bibby.hsieh at mediatek.com  Thu Jun 16 18:58:12 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Fri, 17 Jun 2016 09:58:12 +0800
Subject: [PATCH v2 0/2] drm/mediatek: MT8173 gamma & dither support
Message-ID: <1466128694-11208-1-git-send-email-bibby.hsieh@mediatek.com>

This is MT8173 gamma & dither support PATCH v2, based on 4.7-rc1.

Changes since v1:
 -According to the suggestion from Daniel,
  we used the new atomic color management.
 -Applied gamma function at GAMMA engine (path 2).
 -Made dithering function support hardware mirroring well.
 -Removed the bpc variable from mtk_drm_crtc struct.

Bibby Hsieh (2):
  drm/mediatek: Add gamma correction
  drm/mediatek: set mt8173 dithering function

 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   26 ++++-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |  147 ++++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   16 ++-
 6 files changed, 184 insertions(+), 12 deletions(-)

-- 
1.7.9.5



From bibby.hsieh at mediatek.com  Thu Jun 16 18:58:14 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Fri, 17 Jun 2016 09:58:14 +0800
Subject: [PATCH v2 2/2] drm/mediatek: set mt8173 dithering function
In-Reply-To: <1466128694-11208-1-git-send-email-bibby.hsieh@mediatek.com>
References: <1466128694-11208-1-git-send-email-bibby.hsieh@mediatek.com>
Message-ID: <1466128694-11208-3-git-send-email-bibby.hsieh@mediatek.com>

Some panels only accept bpc (bit per color) 6-bit.
But, the default bpc in mt8173 display data path is 8-bit.
If we didn't enable dithering function to convert bpc,
display cannot show the smooth grayscale image.

In mt8173, the dithering function in OD (OverDrive) and
GAMMA module, we have to config them with
connector->display_mode.bpc when CRTC initial.

1. Clear the default value at *_DITHER_5 and *_DITHER_7 register.
2. Calculate the LSB_ERR_SHIFT bits and ADD_LSHIFT bits two values.
i.e. Input bpc of OD is 10 bits, we assume the bpc of panel is 6-bit,
so, we need to set 4-bit to LSB_ERR_SHIFT and ADD_LSHIFT bits respectively.
3. Then, set the OD or GAMMA to dithering mode depends on path-1 or path-2.

Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   18 ++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   62 +++++++++++++++++++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    6 +--
 5 files changed, 80 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index 8f62671f..019b7ca 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -103,7 +103,8 @@ static void mtk_ovl_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_ovl_config(struct mtk_ddp_comp *comp, unsigned int w,
-			   unsigned int h, unsigned int vrefresh)
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc)
 {
 	if (w != 0 && h != 0)
 		writel_relaxed(h << 16 | w, comp->regs + DISP_REG_OVL_ROI_SIZE);
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index 5fb80cb..0df05f9 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -106,7 +106,8 @@ static void mtk_rdma_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_rdma_config(struct mtk_ddp_comp *comp, unsigned int width,
-			    unsigned int height, unsigned int vrefresh)
+			    unsigned int height, unsigned int vrefresh,
+			    unsigned int bpc)
 {
 	unsigned int threshold;
 	unsigned int reg;
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index ee219bb..9f78e6a 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -222,7 +222,9 @@ static void mtk_crtc_ddp_clk_disable(struct mtk_drm_crtc *mtk_crtc)
 static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 {
 	struct drm_crtc *crtc = &mtk_crtc->base;
-	unsigned int width, height, vrefresh;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	unsigned int width, height, vrefresh, bpc = 0;
 	int ret;
 	int i;
 
@@ -233,6 +235,16 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 	width = crtc->state->adjusted_mode.hdisplay;
 	height = crtc->state->adjusted_mode.vdisplay;
 	vrefresh = crtc->state->adjusted_mode.vrefresh;
+	drm_for_each_encoder(encoder, crtc->dev) {
+		if (encoder->crtc != crtc)
+			continue;
+		drm_for_each_connector(connector, crtc->dev) {
+			if (connector->encoder != encoder)
+				continue;
+			if (bpc == 0 || bpc > connector->display_info.bpc)
+				bpc = connector->display_info.bpc;
+		}
+	}
 
 	ret = pm_runtime_get_sync(crtc->dev->dev);
 	if (ret < 0) {
@@ -266,7 +278,7 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc)
 	for (i = 0; i < mtk_crtc->ddp_comp_nr; i++) {
 		struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[i];
 
-		mtk_ddp_comp_config(comp, width, height, vrefresh);
+		mtk_ddp_comp_config(comp, width, height, vrefresh, bpc);
 		mtk_ddp_comp_start(comp);
 	}
 
@@ -469,7 +481,7 @@ void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
 	if (state->pending_config) {
 		mtk_ddp_comp_config(ovl, state->pending_width,
 				    state->pending_height,
-				    state->pending_vrefresh);
+				    state->pending_vrefresh, 0);
 
 		state->pending_config = false;
 	}
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 56c5894..50c08b9 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -47,22 +47,45 @@
 #define DISP_GAMMA_SIZE				0x030
 #define DISP_GAMMA_LUT				0x700
 
+#define DISP_DITHER_5				0x0114
+#define DISP_DITHER_7				0x011c
+#define DISP_DITHER_15				0x013c
+#define DISP_DITHER_16				0x0140
+
 #define LUT_10BIT_MASK				0x3ff
 
 #define AAL_EN			BIT(0)
 
 #define GAMMA_EN		BIT(0)
 #define GAMMA_LUT_EN		BIT(1)
+#define GAMMA_DITHERING		BIT(2)
 
 #define	OD_RELAY_MODE		BIT(0)
+#define OD_DITHERING		BIT(2)
 
 #define	UFO_BYPASS		BIT(2)
 
 #define	COLOR_BYPASS_ALL	BIT(7)
 #define	COLOR_SEQ_SEL		BIT(13)
 
+#define DITHERING_ALGORITHM_EN	BIT(0)
+
+#define DITHERING_LSB_ERR_SHIFT_R(bits)		((bits) << 28)
+#define DITHERING_OVFLW_R(bits)			((bits) << 24)
+#define DITHERING_ADD_LSHIFT_R(bits)		((bits) << 20)
+#define DITHERING_INPUT_RSHIFT_R(bits)		((bits) << 16)
+#define DITHERING_LSB_ERR_SHIFT_B(bits)		((bits) << 28)
+#define DITHERING_OVFLW_B(bits)			((bits) << 24)
+#define DITHERING_ADD_LSHIFT_B(bits)		((bits) << 20)
+#define DITHERING_INPUT_RSHIFT_B(bits)		((bits) << 16)
+#define DITHERING_LSB_ERR_SHIFT_G(bits)		((bits) << 12)
+#define DITHERING_OVFLW_G(bits)			((bits) << 8)
+#define DITHERING_ADD_LSHIFT_G(bits)		((bits) << 4)
+#define DITHERING_INPUT_RSHIFT_G(bits)		(bits)
+
 static void mtk_color_config(struct mtk_ddp_comp *comp, unsigned int w,
-			     unsigned int h, unsigned int vrefresh)
+			     unsigned int h, unsigned int vrefresh,
+			     unsigned int bpc)
 {
 	writel(w, comp->regs + DISP_COLOR_WIDTH);
 	writel(h, comp->regs + DISP_COLOR_HEIGHT);
@@ -76,9 +99,25 @@ static void mtk_color_start(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_od_config(struct mtk_ddp_comp *comp, unsigned int w,
-			  unsigned int h, unsigned int vrefresh)
+			  unsigned int h, unsigned int vrefresh,
+			  unsigned int bpc)
 {
 	writel(w << 16 | h, comp->regs + DISP_OD_SIZE);
+	if (bpc < 10) {
+		writel(0, comp->regs + DISP_DITHER_5);
+		writel(0, comp->regs + DISP_DITHER_7);
+		writel(DITHERING_LSB_ERR_SHIFT_R(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_R(10 - bpc) |
+		       DITHERING_ALGORITHM_EN, comp->regs + DISP_DITHER_15);
+		writel(DITHERING_LSB_ERR_SHIFT_B(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_B(10 - bpc) |
+		       DITHERING_LSB_ERR_SHIFT_G(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_G(10 - bpc),
+		       comp->regs + DISP_DITHER_16);
+		writel(OD_DITHERING, comp->regs + DISP_OD_CFG);
+	} else {
+		writel(OD_RELAY_MODE, comp->regs + DISP_OD_CFG);
+	}
 }
 
 static void mtk_od_start(struct mtk_ddp_comp *comp)
@@ -93,7 +132,8 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
-			   unsigned int h, unsigned int vrefresh)
+			   unsigned int h, unsigned int vrefresh,
+			   unsigned int bpc)
 {
 	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
 }
@@ -109,9 +149,23 @@ static void mtk_aal_stop(struct mtk_ddp_comp *comp)
 }
 
 static void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
-			     unsigned int h, unsigned int vrefresh)
+			     unsigned int h, unsigned int vrefresh,
+			     unsigned int bpc)
 {
 	writel(h << 16 | w, comp->regs + DISP_GAMMA_SIZE);
+	if (bpc < 10) {
+		writel(0, comp->regs + DISP_DITHER_5);
+		writel(0, comp->regs + DISP_DITHER_7);
+		writel(DITHERING_LSB_ERR_SHIFT_R(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_R(10 - bpc) |
+		       DITHERING_ALGORITHM_EN, comp->regs + DISP_DITHER_15);
+		writel(DITHERING_LSB_ERR_SHIFT_B(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_B(10 - bpc) |
+		       DITHERING_LSB_ERR_SHIFT_G(10 - bpc) |
+		       DITHERING_ADD_LSHIFT_G(10 - bpc),
+		       comp->regs + DISP_DITHER_16);
+		writel(GAMMA_DITHERING, comp->regs + DISP_GAMMA_CFG);
+	}
 }
 
 static void mtk_gamma_start(struct mtk_ddp_comp *comp)
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 07e17fe..c7c2e92 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -65,7 +65,7 @@ struct mtk_ddp_comp;
 
 struct mtk_ddp_comp_funcs {
 	void (*config)(struct mtk_ddp_comp *comp, unsigned int w,
-		       unsigned int h, unsigned int vrefresh);
+		       unsigned int h, unsigned int vrefresh, unsigned int bpc);
 	void (*start)(struct mtk_ddp_comp *comp);
 	void (*stop)(struct mtk_ddp_comp *comp);
 	void (*enable_vblank)(struct mtk_ddp_comp *comp, struct drm_crtc *crtc);
@@ -89,10 +89,10 @@ struct mtk_ddp_comp {
 
 static inline void mtk_ddp_comp_config(struct mtk_ddp_comp *comp,
 				       unsigned int w, unsigned int h,
-				       unsigned int vrefresh)
+				       unsigned int vrefresh, unsigned int bpc)
 {
 	if (comp->funcs && comp->funcs->config)
-		comp->funcs->config(comp, w, h, vrefresh);
+		comp->funcs->config(comp, w, h, vrefresh, bpc);
 }
 
 static inline void mtk_ddp_comp_start(struct mtk_ddp_comp *comp)
-- 
1.7.9.5



From bibby.hsieh at mediatek.com  Thu Jun 16 18:58:13 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Fri, 17 Jun 2016 09:58:13 +0800
Subject: [PATCH v2 1/2] drm/mediatek: Add gamma correction
In-Reply-To: <1466128694-11208-1-git-send-email-bibby.hsieh@mediatek.com>
References: <1466128694-11208-1-git-send-email-bibby.hsieh@mediatek.com>
Message-ID: <1466128694-11208-2-git-send-email-bibby.hsieh@mediatek.com>

Apply gamma function to correct brightness values.
It applies arbitrary mapping curve to compensate the
incorrect transfer function of the panel.

Signed-off-by: Bibby Hsieh <bibby.hsieh at mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |    8 +++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |   89 ++++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   10 +++
 4 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 24aa3ba..ee219bb 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -409,6 +409,10 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
 	}
 	if (pending_planes)
 		mtk_crtc->pending_planes = true;
+	if (crtc->state->color_mgmt_changed)
+		for (i = 0; i < mtk_crtc->ddp_comp_nr; i++)
+			mtk_ddp_gamma_set(mtk_crtc->ddp_comp[i], crtc->state);
+
 }
 
 static const struct drm_crtc_funcs mtk_crtc_funcs = {
@@ -418,6 +422,7 @@ static const struct drm_crtc_funcs mtk_crtc_funcs = {
 	.reset			= mtk_drm_crtc_reset,
 	.atomic_duplicate_state	= mtk_drm_crtc_duplicate_state,
 	.atomic_destroy_state	= mtk_drm_crtc_destroy_state,
+	.gamma_set		= drm_atomic_helper_legacy_gamma_set,
 };
 
 static const struct drm_crtc_helper_funcs mtk_crtc_helper_funcs = {
@@ -569,6 +574,9 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
 	if (ret < 0)
 		goto unprepare;
 
+	drm_mode_crtc_set_gamma_size(&mtk_crtc->base, MTK_LUT_SIZE);
+	drm_helper_crtc_enable_color_mgmt(&mtk_crtc->base, MTK_LUT_SIZE,
+					  MTK_LUT_SIZE);
 	priv->crtc[pipe] = &mtk_crtc->base;
 	priv->num_pipes++;
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
index 81e5566..d332564 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.h
@@ -19,6 +19,7 @@
 #include "mtk_drm_plane.h"
 
 #define OVL_LAYER_NR	4
+#define MTK_LUT_SIZE	512
 
 int mtk_drm_crtc_enable_vblank(struct drm_device *drm, unsigned int pipe);
 void mtk_drm_crtc_disable_vblank(struct drm_device *drm, unsigned int pipe);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 3970fcf..56c5894 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -24,6 +24,7 @@
 #include "mtk_drm_drv.h"
 #include "mtk_drm_plane.h"
 #include "mtk_drm_ddp_comp.h"
+#include "mtk_drm_crtc.h"
 
 #define DISP_OD_EN				0x0000
 #define DISP_OD_INTEN				0x0008
@@ -38,6 +39,21 @@
 #define DISP_COLOR_WIDTH			0x0c50
 #define DISP_COLOR_HEIGHT			0x0c54
 
+#define DISP_AAL_EN				0x000
+#define DISP_AAL_SIZE				0x030
+
+#define DISP_GAMMA_EN				0x000
+#define DISP_GAMMA_CFG				0x020
+#define DISP_GAMMA_SIZE				0x030
+#define DISP_GAMMA_LUT				0x700
+
+#define LUT_10BIT_MASK				0x3ff
+
+#define AAL_EN			BIT(0)
+
+#define GAMMA_EN		BIT(0)
+#define GAMMA_LUT_EN		BIT(1)
+
 #define	OD_RELAY_MODE		BIT(0)
 
 #define	UFO_BYPASS		BIT(2)
@@ -76,6 +92,61 @@ static void mtk_ufoe_start(struct mtk_ddp_comp *comp)
 	writel(UFO_BYPASS, comp->regs + DISP_REG_UFO_START);
 }
 
+static void mtk_aal_config(struct mtk_ddp_comp *comp, unsigned int w,
+			   unsigned int h, unsigned int vrefresh)
+{
+	writel(h << 16 | w, comp->regs + DISP_AAL_SIZE);
+}
+
+static void mtk_aal_start(struct mtk_ddp_comp *comp)
+{
+	writel(AAL_EN, comp->regs  + DISP_AAL_EN);
+}
+
+static void mtk_aal_stop(struct mtk_ddp_comp *comp)
+{
+	writel_relaxed(0x0, comp->regs  + DISP_AAL_EN);
+}
+
+static void mtk_gamma_config(struct mtk_ddp_comp *comp, unsigned int w,
+			     unsigned int h, unsigned int vrefresh)
+{
+	writel(h << 16 | w, comp->regs + DISP_GAMMA_SIZE);
+}
+
+static void mtk_gamma_start(struct mtk_ddp_comp *comp)
+{
+	writel(GAMMA_EN, comp->regs  + DISP_GAMMA_EN);
+}
+
+static void mtk_gamma_stop(struct mtk_ddp_comp *comp)
+{
+	writel_relaxed(0x0, comp->regs  + DISP_GAMMA_EN);
+}
+
+static void mtk_gamma_set(struct mtk_ddp_comp *comp,
+			  struct drm_crtc_state *state)
+{
+	unsigned int i, reg;
+	struct drm_color_lut *lut;
+	void __iomem *lut_base;
+	u32 word;
+
+	if (state->gamma_lut) {
+		reg = readl(comp->regs + DISP_GAMMA_CFG);
+		reg = reg | GAMMA_LUT_EN;
+		writel(reg, comp->regs + DISP_GAMMA_CFG);
+		lut_base = comp->regs + DISP_GAMMA_LUT;
+		lut = (struct drm_color_lut *)state->gamma_lut->data;
+		for (i = 0; i < MTK_LUT_SIZE; i++) {
+			word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) +
+			      (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) +
+			      ((lut[i].blue >> 6) & LUT_10BIT_MASK);
+			writel(word, (lut_base + i * 4));
+		}
+	}
+}
+
 static const struct mtk_ddp_comp_funcs ddp_color = {
 	.config = mtk_color_config,
 	.start = mtk_color_start,
@@ -90,6 +161,20 @@ static const struct mtk_ddp_comp_funcs ddp_ufoe = {
 	.start = mtk_ufoe_start,
 };
 
+static const struct mtk_ddp_comp_funcs ddp_aal = {
+	.gamma_set = mtk_gamma_set,
+	.config = mtk_aal_config,
+	.start = mtk_aal_start,
+	.stop = mtk_aal_stop,
+};
+
+static const struct mtk_ddp_comp_funcs ddp_gamma = {
+	.gamma_set = mtk_gamma_set,
+	.config = mtk_gamma_config,
+	.start = mtk_gamma_start,
+	.stop = mtk_gamma_stop,
+};
+
 static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
 	[MTK_DISP_OVL] = "ovl",
 	[MTK_DISP_RDMA] = "rdma",
@@ -112,13 +197,13 @@ struct mtk_ddp_comp_match {
 };
 
 static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
-	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
+	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, &ddp_aal },
 	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
 	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
 	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
 	[DDP_COMPONENT_DSI0]	= { MTK_DSI,		0, NULL },
 	[DDP_COMPONENT_DSI1]	= { MTK_DSI,		1, NULL },
-	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, NULL },
+	[DDP_COMPONENT_GAMMA]	= { MTK_DISP_GAMMA,	0, &ddp_gamma },
 	[DDP_COMPONENT_OD]	= { MTK_DISP_OD,	0, &ddp_od },
 	[DDP_COMPONENT_OVL0]	= { MTK_DISP_OVL,	0, NULL },
 	[DDP_COMPONENT_OVL1]	= { MTK_DISP_OVL,	1, NULL },
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 6b13ba9..07e17fe 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -20,6 +20,7 @@ struct device;
 struct device_node;
 struct drm_crtc;
 struct drm_device;
+struct drm_crtc_state;
 struct mtk_plane_state;
 
 enum mtk_ddp_comp_type {
@@ -73,6 +74,8 @@ struct mtk_ddp_comp_funcs {
 	void (*layer_off)(struct mtk_ddp_comp *comp, unsigned int idx);
 	void (*layer_config)(struct mtk_ddp_comp *comp, unsigned int idx,
 			     struct mtk_plane_state *state);
+	void (*gamma_set)(struct mtk_ddp_comp *comp,
+			  struct drm_crtc_state *state);
 };
 
 struct mtk_ddp_comp {
@@ -139,6 +142,13 @@ static inline void mtk_ddp_comp_layer_config(struct mtk_ddp_comp *comp,
 		comp->funcs->layer_config(comp, idx, state);
 }
 
+static inline void mtk_ddp_gamma_set(struct mtk_ddp_comp *comp,
+				     struct drm_crtc_state *state)
+{
+	if (comp->funcs && comp->funcs->gamma_set)
+		comp->funcs->gamma_set(comp, state);
+}
+
 int mtk_ddp_comp_get_id(struct device_node *node,
 			enum mtk_ddp_comp_type comp_type);
 int mtk_ddp_comp_init(struct device *dev, struct device_node *comp_node,
-- 
1.7.9.5



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:51 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:51 +0800
Subject: [alsa-devel] [PATCH v5 0/9] ASoC: Mediatek: Add support for MT2701 SOC
Message-ID: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>

This patch adds basic support for Mediatek AFE for MT2701 SoC.
The patch is based on broonie tree "for-next" branch.

Change since v4:

* remove SND_SOC_MEDIATEK config prompt
* fix config prompt to meaningful one for SND_SOC_MT2701_CS42448
* remove some unnecessary random space change

Change since v3:

* fix comments style.

Change since v2:

* fix comments style.
* replace EXPORT_SYMBOL with EXPORT_SYMBOL_GPL
* reduce debug message
* move mutex lock for irq-allocated to AFE struct
* remove func mtk_simple_isr and call snd_pcm_period_elapsed directly
* mt2701 driver probe will fail if clock initial error
* fix dapm on/off switch control naming

Change since v1:

implement common private structure, fe dai and platform driver.
let mt8173 and mt2701 use the common structure.


Garlic Tseng (9):
  ASoC: mediatek: Refine mt8173 driver and change config option
  ASoC: mediatek: implement mediatek common structure
  ASoC: mediatek: let mt8173 use mediatek common structure
  ASoC: mediatek: add documents for mt2701
  ASoC: mediatek: add structure define and clock control for 2701
  ASoC: mediatek: add mt2701 platform driver implementation.
  ASoC: bt-sco: extend rate and add a general compatible string
  ASoC: mediatek: add BT implementation
  ASoC: mediatek: Add mt2701-cs42448 driver and config option.

 Documentation/devicetree/bindings/sound/bt-sco.txt |    2 +-
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   |  150 ++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |   43 +
 sound/soc/codecs/bt-sco.c                          |    5 +-
 sound/soc/mediatek/Kconfig                         |   37 +-
 sound/soc/mediatek/Makefile                        |   10 +-
 sound/soc/mediatek/common/Makefile                 |   16 +
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         |  379 +++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |   45 +
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |   90 ++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |   23 +
 sound/soc/mediatek/common/mtk-base-afe.h           |  104 ++
 sound/soc/mediatek/mt2701/Makefile                 |   19 +
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c  |  464 ++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h  |   38 +
 sound/soc/mediatek/mt2701/mt2701-afe-common.h      |  181 +++
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c         | 1698 ++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c         |  422 +++++
 sound/soc/mediatek/mt2701/mt2701-reg.h             |  186 +++
 sound/soc/mediatek/mt8173/Makefile                 |    7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      |   73 +
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     |  921 +++++------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |    2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |    4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |    2 +-
 sound/soc/mediatek/mtk-afe-common.h                |  101 --
 27 files changed, 4390 insertions(+), 634 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
 create mode 100644 sound/soc/mediatek/common/Makefile
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (51%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:55 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:55 +0800
Subject: [alsa-devel] [PATCH v5 4/9] ASoC: mediatek: add documents for mt2701
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-5-git-send-email-garlic.tseng@mediatek.com>

add mt2701-afe-pcm.txt and mt2701-cs42448.txt for mt2701

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   | 150 +++++++++++++++++++++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |  43 ++++++
 2 files changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 0000000..3e623a7
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm at 11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 0000000..0557444
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:53 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:53 +0800
Subject: [alsa-devel] [PATCH v5 2/9] ASoC: mediatek: implement mediatek common
 structure
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-3-git-send-email-garlic.tseng@mediatek.com>

implement mediatek basic structure, include common private data,
afe fe dai operator and afe platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         | 379 +++++++++++++++++++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |  45 +++
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |  90 +++++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |  23 ++
 sound/soc/mediatek/common/mtk-base-afe.h           | 104 ++++++
 5 files changed, 641 insertions(+)
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h

diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 0000000..b788791b
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,379 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+			   unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	/* enable agent */
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       0 << memif->data->agent_disable_shift);
+
+	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+	/*
+	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+	 * smaller than period_size due to AFE's internal buffer.
+	 * This easily leads to overrun when avail_min is period_size.
+	 * One more period can hold the possible unread buffer.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		int periods_max = mtk_afe_hardware->periods_max;
+
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+						   SNDRV_PCM_HW_PARAM_PERIODS,
+						   3, periods_max);
+		if (ret < 0) {
+			dev_err(afe->dev, "hw_constraint_minmax failed\n");
+			return ret;
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	/* dynamic allocate irq to memif */
+	if (memif->irq_usage < 0) {
+		int irq_id = mtk_dynamic_irq_acquire(afe);
+
+		if (irq_id != afe->irqs_size) {
+			/* link */
+			memif->irq_usage = irq_id;
+		} else {
+			dev_err(afe->dev, "%s() error: no more asys irq\n",
+				__func__);
+			ret = -EBUSY;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int irq_id;
+
+	irq_id = memif->irq_usage;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       1 << memif->data->agent_disable_shift);
+
+	if (!memif->const_irq) {
+		mtk_dynamic_irq_release(afe, irq_id);
+		memif->irq_usage = -1;
+		memif->substream = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int msb_at_bit33 = 0;
+	int ret, fs = 0;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+			 memif->phys_buf_addr);
+	/* end */
+	mtk_regmap_write(afe->regmap,
+			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+			 memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set MSB to 33-bit */
+	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+			       1 << memif->data->msb_shift,
+			       msb_at_bit33 << memif->data->msb_shift);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+				       1 << memif->data->mono_shift,
+				       mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+			       memif->data->fs_maskbit << memif->data->fs_shift,
+			       fs << memif->data->fs_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+	unsigned int counter = runtime->period_size;
+	int fs;
+
+	dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (memif->data->enable_shift >= 0)
+			mtk_regmap_update_bits(afe->regmap,
+					       memif->data->enable_reg,
+					       1 << memif->data->enable_shift,
+					       1 << memif->data->enable_shift);
+
+		/* set irq counter */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+				       irq_data->irq_cnt_maskbit
+				       << irq_data->irq_cnt_shift,
+				       counter << irq_data->irq_cnt_shift);
+
+		/* set irq fs */
+		fs = afe->irq_fs(substream, runtime->rate);
+
+		if (fs < 0)
+			return -EINVAL;
+
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+				       irq_data->irq_fs_maskbit
+				       << irq_data->irq_fs_shift,
+				       fs << irq_data->irq_fs_shift);
+
+		/* enable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       1 << irq_data->irq_en_shift);
+
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+				       1 << memif->data->enable_shift, 0);
+		/* disable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       0 << irq_data->irq_en_shift);
+		/* and clear pending IRQ */
+		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+				 1 << irq_data->irq_clr_shift);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int hd_audio = 0;
+
+	/* set hd mode */
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hd_audio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hd_audio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		hd_audio = 1;
+		break;
+	default:
+		dev_err(afe->dev, "%s() error: unsupported format %d\n",
+			__func__, substream->runtime->format);
+		break;
+	}
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+			       1 << memif->data->hd_shift,
+			       hd_audio << memif->data->hd_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+	.startup	= mtk_afe_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mtk_afe_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+	int i;
+
+	mutex_lock(&afe->irq_alloc_lock);
+	for (i = 0; i < afe->irqs_size; ++i) {
+		if (afe->irqs[i].irq_occupyed == 0) {
+			afe->irqs[i].irq_occupyed = 1;
+			mutex_unlock(&afe->irq_alloc_lock);
+			return i;
+		}
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+	mutex_lock(&afe->irq_alloc_lock);
+	if (irq_id >= 0 && irq_id < afe->irqs_size) {
+		afe->irqs[irq_id].irq_occupyed = 0;
+		mutex_unlock(&afe->irq_alloc_lock);
+		return 0;
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	afe->suspended = true;
+	afe->runtime_suspend(dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	afe->runtime_resume(dev);
+
+	if (!afe->reg_back_up)
+		dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+				 afe->reg_back_up[i]);
+
+	afe->suspended = false;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 0000000..28cb178
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 0000000..82d439c
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+			 (struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	const struct mtk_base_memif_data *memif_data = memif->data;
+	struct regmap *regmap = afe->regmap;
+	struct device *dev = afe->dev;
+	int reg_ofs_base = memif_data->reg_ofs_base;
+	int reg_ofs_cur = memif_data->reg_ofs_cur;
+	unsigned int hw_ptr = 0, hw_base = 0;
+	int ret, pcm_ptr_bytes;
+
+	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+	if (ret || hw_ptr == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+	if (ret || hw_base == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+	.ioctl = snd_pcm_lib_ioctl,
+	.pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	size_t size;
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+	size = afe->mtk_afe_hardware->buffer_bytes_max;
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+	.ops = &mtk_afe_pcm_ops,
+	.pcm_new = mtk_afe_pcm_new,
+	.pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 0000000..a973fc9
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 0000000..3a78f6f
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_reg;
+	int fs_shift;
+	int fs_maskbit;
+	int mono_reg;
+	int mono_shift;
+	int enable_reg;
+	int enable_shift;
+	int hd_reg;
+	int hd_shift;
+	int msb_reg;
+	int msb_shift;
+	int agent_disable_reg;
+	int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+	int id;
+	int irq_cnt_reg;
+	int irq_cnt_shift;
+	int irq_cnt_maskbit;
+	int irq_fs_reg;
+	int irq_fs_shift;
+	int irq_fs_maskbit;
+	int irq_en_reg;
+	int irq_en_shift;
+	int irq_clr_reg;
+	int irq_clr_shift;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+	void __iomem *base_addr;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+	unsigned int const *reg_back_up_list;
+	unsigned int *reg_back_up;
+	unsigned int reg_back_up_list_num;
+
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	bool suspended;
+
+	struct mtk_base_afe_memif *memif;
+	int memif_size;
+	struct mtk_base_afe_irq *irqs;
+	int irqs_size;
+
+	const struct snd_pcm_hardware *mtk_afe_hardware;
+	int (*memif_fs)(struct snd_pcm_substream *substream,
+			unsigned int rate);
+	int (*irq_fs)(struct snd_pcm_substream *substream,
+		      unsigned int rate);
+
+	void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mtk_base_memif_data *data;
+	int irq_usage;
+	int const_irq;
+};
+
+struct mtk_base_afe_irq {
+	const struct mtk_base_irq_data *irq_data;
+	int irq_occupyed;
+};
+
+#endif
+
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:52 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:52 +0800
Subject: [alsa-devel] [PATCH v5 1/9] ASoC: mediatek: Refine mt8173 driver and
 change config option
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-2-git-send-email-garlic.tseng@mediatek.com>

move mt8173 driver to another folder and add prefix.
add config option SND_SOC_MT8173

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                         |  14 +-
 sound/soc/mediatek/Makefile                        |   9 +-
 sound/soc/mediatek/mt8173/Makefile                 |   7 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h      | 101 +++++
 .../{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c}     | 494 ++++++++++-----------
 sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c  |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c   |   2 +-
 .../mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c   |   4 +-
 sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c    |   2 +-
 sound/soc/mediatek/mtk-afe-common.h                | 101 -----
 10 files changed, 367 insertions(+), 369 deletions(-)
 create mode 100644 sound/soc/mediatek/mt8173/Makefile
 create mode 100644 sound/soc/mediatek/mt8173/mt8173-afe-common.h
 rename sound/soc/mediatek/{mtk-afe-pcm.c => mt8173/mt8173-afe-pcm.c} (66%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-max98090.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5514.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650-rt5676.c (99%)
 rename sound/soc/mediatek/{ => mt8173}/mt8173-rt5650.c (99%)
 delete mode 100644 sound/soc/mediatek/mtk-afe-common.h

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3abf51c..ae9f664 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,15 @@
-config SND_SOC_MEDIATEK
-	tristate "ASoC support for Mediatek chip"
+config SND_SOC_MT8173
+	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
 	help
-	  This adds ASoC platform driver support for Mediatek chip
+	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
 	  Select Y if you have such device.
 	  Ex: MT8173
 
 config SND_SOC_MT8173_MAX98090
 	tristate "ASoC Audio driver for MT8173 with MAX98090 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_MAX98090
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -19,7 +19,7 @@ config SND_SOC_MT8173_MAX98090
 
 config SND_SOC_MT8173_RT5650
 	tristate "ASoC Audio driver for MT8173 with RT5650 codec"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	help
 	  This adds ASoC driver for Mediatek MT8173 boards
@@ -29,7 +29,7 @@ config SND_SOC_MT8173_RT5650
 
 config SND_SOC_MT8173_RT5650_RT5514
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5514
 	help
@@ -40,7 +40,7 @@ config SND_SOC_MT8173_RT5650_RT5514
 
 config SND_SOC_MT8173_RT5650_RT5676
 	tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
-	depends on SND_SOC_MEDIATEK && I2C
+	depends on SND_SOC_MT8173 && I2C
 	select SND_SOC_RT5645
 	select SND_SOC_RT5677
 	select SND_SOC_HDMI_CODEC
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index d486860..240dfc70 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,7 +1,2 @@
-# MTK Platform Support
-obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
-# Machine support
-obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
-obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
+# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 0000000..0357b27
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,7 @@
+# MTK Platform Support
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173-afe-pcm.o
+# Machine support
+obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
new file mode 100644
index 0000000..8f2936d
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,101 @@
+/*
+ * mt8173_afe_common.h  --  Mediatek 8173 audio driver common definitions
+ *
+ * Copyright (c) 2015 MediaTek Inc.
+ * Author: Koro Chen <koro.chen at mediatek.com>
+ *             Sascha Hauer <s.hauer at pengutronix.de>
+ *             Hidalgo Huang <hidalgo.huang at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT8173_AFE_COMMON_H_
+#define _MT8173_AFE_COMMON_H_
+
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+enum {
+	MT8173_AFE_MEMIF_DL1,
+	MT8173_AFE_MEMIF_DL2,
+	MT8173_AFE_MEMIF_VUL,
+	MT8173_AFE_MEMIF_DAI,
+	MT8173_AFE_MEMIF_AWB,
+	MT8173_AFE_MEMIF_MOD_DAI,
+	MT8173_AFE_MEMIF_HDMI,
+	MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM1 = MT8173_AFE_MEMIF_NUM,
+	MT8173_AFE_IO_MOD_PCM2,
+	MT8173_AFE_IO_PMIC,
+	MT8173_AFE_IO_I2S,
+	MT8173_AFE_IO_2ND_I2S,
+	MT8173_AFE_IO_HW_GAIN1,
+	MT8173_AFE_IO_HW_GAIN2,
+	MT8173_AFE_IO_MRG_O,
+	MT8173_AFE_IO_MRG_I,
+	MT8173_AFE_IO_DAIBT,
+	MT8173_AFE_IO_HDMI,
+};
+
+enum {
+	MT8173_AFE_IRQ_1,
+	MT8173_AFE_IRQ_2,
+	MT8173_AFE_IRQ_3,
+	MT8173_AFE_IRQ_4,
+	MT8173_AFE_IRQ_5,
+	MT8173_AFE_IRQ_6,
+	MT8173_AFE_IRQ_7,
+	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_NUM,
+};
+
+enum {
+	MT8173_CLK_INFRASYS_AUD,
+	MT8173_CLK_TOP_PDN_AUD,
+	MT8173_CLK_TOP_PDN_AUD_BUS,
+	MT8173_CLK_I2S0_M,
+	MT8173_CLK_I2S1_M,
+	MT8173_CLK_I2S2_M,
+	MT8173_CLK_I2S3_M,
+	MT8173_CLK_I2S3_B,
+	MT8173_CLK_BCK0,
+	MT8173_CLK_BCK1,
+	MT8173_CLK_NUM
+};
+
+struct mt8173_afe;
+struct snd_pcm_substream;
+
+struct mt8173_afe_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_shift;
+	int mono_shift;
+	int enable_shift;
+	int irq_reg_cnt;
+	int irq_cnt_shift;
+	int irq_en_shift;
+	int irq_fs_shift;
+	int irq_clr_shift;
+	int msb_shift;
+};
+
+struct mt8173_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mt8173_afe_memif_data *data;
+	const struct mt8173_afe_irq_data *irqdata;
+};
+
+#endif
diff --git a/sound/soc/mediatek/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
similarity index 66%
rename from sound/soc/mediatek/mtk-afe-pcm.c
rename to sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 793d7e2..4fc52bc 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1,5 +1,5 @@
 /*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
  *
  * Copyright (c) 2015 MediaTek Inc.
  * Author: Koro Chen <koro.chen at mediatek.com>
@@ -24,7 +24,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
-#include "mtk-afe-common.h"
+#include "mt8173-afe-common.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -135,7 +135,7 @@ enum afe_tdm_ch_start {
 	AFE_TDM_CH_ZERO,
 };
 
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
 	AUDIO_TOP_CON0,
 	AFE_CONN1,
 	AFE_CONN2,
@@ -152,18 +152,18 @@ static const unsigned int mtk_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mtk_afe {
+struct mt8173_afe {
 	/* address for ioremap audio hardware register */
 	void __iomem *base_addr;
 	struct device *dev;
 	struct regmap *regmap;
-	struct mtk_afe_memif memif[MTK_AFE_MEMIF_NUM];
-	struct clk *clocks[MTK_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mtk_afe_backup_list)];
+	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+	struct clk *clocks[MT8173_CLK_NUM];
+	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
 	bool suspended;
 };
 
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 		 SNDRV_PCM_INFO_MMAP_VALID),
 	.buffer_bytes_max = 256 * 1024,
@@ -174,12 +174,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mtk_afe_pcm_pointer
+static snd_pcm_uframes_t mt8173_afe_pcm_pointer
 			 (struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int hw_ptr;
 	int ret;
 
@@ -193,40 +193,40 @@ static snd_pcm_uframes_t mtk_afe_pcm_pointer
 			       hw_ptr - memif->phys_buf_addr);
 }
 
-static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
 	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mtk_afe_pcm_pointer,
+	.pointer = mt8173_afe_pcm_pointer,
 };
 
-static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
 	size_t size;
 	struct snd_card *card = rtd->card->snd_card;
 	struct snd_pcm *pcm = rtd->pcm;
 
-	size = mtk_afe_hardware.buffer_bytes_max;
+	size = mt8173_afe_hardware.buffer_bytes_max;
 
 	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 						     card->dev, size, size);
 }
 
-static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
 {
 	snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-static const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
-	.ops = &mtk_afe_pcm_ops,
-	.pcm_new = mtk_afe_pcm_new,
-	.pcm_free = mtk_afe_pcm_free,
+static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
+	.ops = &mt8173_afe_pcm_ops,
+	.pcm_new = mt8173_afe_pcm_new,
+	.pcm_free = mt8173_afe_pcm_free,
 };
 
-struct mtk_afe_rate {
+struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
 };
 
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
 	{ .rate = 8000, .regvalue = 0 },
 	{ .rate = 11025, .regvalue = 1 },
 	{ .rate = 12000, .regvalue = 2 },
@@ -242,21 +242,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
 	{ .rate = 192000, .regvalue = 14 },
 };
 
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 {
 	int i;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_i2s_rates); i++)
-		if (mtk_afe_i2s_rates[i].rate == sample_rate)
-			return mtk_afe_i2s_rates[i].regvalue;
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_i2s_rates); i++)
+		if (mt8173_afe_i2s_rates[i].rate == sample_rate)
+			return mt8173_afe_i2s_rates[i].regvalue;
 
 	return -EINVAL;
 }
 
-static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 {
 	unsigned int val;
-	int fs = mtk_afe_i2s_fs(rate);
+	int fs = mt8173_afe_i2s_fs(rate);
 
 	if (fs < 0)
 		return -EINVAL;
@@ -281,7 +281,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,8 +296,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
-				    struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
 
@@ -319,9 +319,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
-				 struct clk *m_ck, unsigned int mck_rate,
-				 struct clk *b_ck, unsigned int bck_rate)
+static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+				    struct clk *m_ck, unsigned int mck_rate,
+				    struct clk *b_ck, unsigned int bck_rate)
 {
 	int ret;
 
@@ -343,8 +343,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
 	return 0;
 }
 
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
-				      struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
 		clk_disable_unprepare(m_ck);
@@ -352,11 +352,11 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
 		clk_disable_unprepare(b_ck);
 }
 
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -366,84 +366,82 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
-				 struct snd_soc_dai *dai)
+static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_set_i2s_enable(afe, false);
+	mt8173_afe_set_i2s_enable(afe, false);
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
 			   AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
 }
 
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	int ret;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
-			      NULL, 0);
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
-			      NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+				 runtime->rate * 256, NULL, 0);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
-	ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
+	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
 	if (ret)
 		return ret;
 
-	mtk_afe_set_i2s_enable(afe, true);
+	mt8173_afe_set_i2s_enable(afe, true);
 
 	return 0;
 }
 
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
 
-	mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				 afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				    afe->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
 
-	mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
-				  afe->clocks[MTK_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				     afe->clocks[MT8173_CLK_I2S3_B]);
 }
 
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	unsigned int val;
 
-	mtk_afe_dais_set_clks(afe,
-			      afe->clocks[MTK_CLK_I2S3_M], runtime->rate * 128,
-			      afe->clocks[MTK_CLK_I2S3_B],
-			      runtime->rate * runtime->channels * 32);
+	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+				 runtime->rate * 128,
+				 afe->clocks[MT8173_CLK_I2S3_B],
+				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
 	      AFE_TDM_CON1_LRCK_INV |
@@ -494,11 +492,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -540,18 +538,18 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 	}
 }
 
-static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int ret;
 
 	memif->substream = substream;
 
-	snd_soc_set_runtime_hwparams(substream, &mtk_afe_hardware);
+	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
 
 	/*
 	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
@@ -563,7 +561,7 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 		ret = snd_pcm_hw_constraint_minmax(runtime,
 						   SNDRV_PCM_HW_PARAM_PERIODS,
 						   3,
-						   mtk_afe_hardware.periods_max);
+						   mt8173_afe_hardware.periods_max);
 		if (ret < 0) {
 			dev_err(afe->dev, "hw_constraint_minmax failed\n");
 			return ret;
@@ -576,23 +574,23 @@ static int mtk_afe_dais_startup(struct snd_pcm_substream *substream,
 	return ret;
 }
 
-static void mtk_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				  struct snd_soc_dai *dai)
+static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 
 	memif->substream = NULL;
 }
 
-static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params,
-				  struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params,
+				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	int msb_at_bit33 = 0;
 	int ret;
 
@@ -634,8 +632,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	/* set rate */
 	if (memif->data->fs_shift < 0)
 		return 0;
-	if (memif->data->id == MTK_AFE_MEMIF_DAI ||
-	    memif->data->id == MTK_AFE_MEMIF_MOD_DAI) {
+	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
 		unsigned int val;
 
 		switch (params_rate(params)) {
@@ -652,7 +650,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 			return -EINVAL;
 		}
 
-		if (memif->data->id == MTK_AFE_MEMIF_DAI)
+		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
 			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
 					   0x3 << memif->data->fs_shift,
 					   val << memif->data->fs_shift);
@@ -662,7 +660,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 					   val << memif->data->fs_shift);
 
 	} else {
-		int fs = mtk_afe_i2s_fs(params_rate(params));
+		int fs = mt8173_afe_i2s_fs(params_rate(params));
 
 		if (fs < 0)
 			return -EINVAL;
@@ -675,19 +673,19 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
 	return 0;
 }
 
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
 {
 	return snd_pcm_lib_free_pages(substream);
 }
 
-static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
+static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
+				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
 	unsigned int counter = runtime->period_size;
 
 	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
@@ -708,7 +706,7 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set irq fs */
 		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mtk_afe_i2s_fs(runtime->rate);
+			int fs = mt8173_afe_i2s_fs(runtime->rate);
 
 			if (fs < 0)
 				return -EINVAL;
@@ -743,76 +741,76 @@ static int mtk_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
 }
 
 /* FE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_dai_ops = {
-	.startup	= mtk_afe_dais_startup,
-	.shutdown	= mtk_afe_dais_shutdown,
-	.hw_params	= mtk_afe_dais_hw_params,
-	.hw_free	= mtk_afe_dais_hw_free,
-	.trigger	= mtk_afe_dais_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
+	.startup	= mt8173_afe_dais_startup,
+	.shutdown	= mt8173_afe_dais_shutdown,
+	.hw_params	= mt8173_afe_dais_hw_params,
+	.hw_free	= mt8173_afe_dais_hw_free,
+	.trigger	= mt8173_afe_dais_trigger,
 };
 
 /* BE DAIs */
-static const struct snd_soc_dai_ops mtk_afe_i2s_ops = {
-	.startup	= mtk_afe_i2s_startup,
-	.shutdown	= mtk_afe_i2s_shutdown,
-	.prepare	= mtk_afe_i2s_prepare,
+static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
+	.startup	= mt8173_afe_i2s_startup,
+	.shutdown	= mt8173_afe_i2s_shutdown,
+	.prepare	= mt8173_afe_i2s_prepare,
 };
 
-static const struct snd_soc_dai_ops mtk_afe_hdmi_ops = {
-	.startup	= mtk_afe_hdmi_startup,
-	.shutdown	= mtk_afe_hdmi_shutdown,
-	.prepare	= mtk_afe_hdmi_prepare,
-	.trigger	= mtk_afe_hdmi_trigger,
+static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
+	.startup	= mt8173_afe_hdmi_startup,
+	.shutdown	= mt8173_afe_hdmi_shutdown,
+	.prepare	= mt8173_afe_hdmi_prepare,
+	.trigger	= mt8173_afe_hdmi_trigger,
 
 };
 
-static int mtk_afe_runtime_suspend(struct device *dev);
-static int mtk_afe_runtime_resume(struct device *dev);
+static int mt8173_afe_runtime_suspend(struct device *dev);
+static int mt8173_afe_runtime_resume(struct device *dev);
 
-static int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_read(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
 			    &afe->backup_regs[i]);
 
 	afe->suspended = true;
-	mtk_afe_runtime_suspend(afe->dev);
+	mt8173_afe_runtime_suspend(afe->dev);
 	return 0;
 }
 
-static int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
 {
-	struct mtk_afe *afe = snd_soc_dai_get_drvdata(dai);
+	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
 	int i = 0;
 
 	dev_dbg(afe->dev, "%s\n", __func__);
 	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
 		return 0;
 
-	mtk_afe_runtime_resume(afe->dev);
+	mt8173_afe_runtime_resume(afe->dev);
 
-	for (i = 0; i < ARRAY_SIZE(mtk_afe_backup_list); i++)
-		regmap_write(afe->regmap, mtk_afe_backup_list[i],
+	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
+		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
 			     afe->backup_regs[i]);
 
 	afe->suspended = false;
 	return 0;
 }
 
-static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
-		.id = MTK_AFE_MEMIF_DL1,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_DL1,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -820,12 +818,12 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
-		.id = MTK_AFE_MEMIF_VUL,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_VUL,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -833,11 +831,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
-		.id = MTK_AFE_IO_I2S,
+		.id = MT8173_AFE_IO_I2S,
 		.playback = {
 			.stream_name = "I2S Playback",
 			.channels_min = 1,
@@ -852,18 +850,18 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_i2s_ops,
+		.ops = &mt8173_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
 };
 
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	/* FE DAIs */
 	{
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
-		.suspend = mtk_afe_dai_suspend,
-		.resume = mtk_afe_dai_resume,
+		.id = MT8173_AFE_MEMIF_HDMI,
+		.suspend = mt8173_afe_dai_suspend,
+		.resume = mt8173_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -874,11 +872,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_dai_ops,
+		.ops = &mt8173_afe_dai_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
-		.id = MTK_AFE_IO_HDMI,
+		.id = MT8173_AFE_IO_HDMI,
 		.playback = {
 			.stream_name = "HDMIO Playback",
 			.channels_min = 2,
@@ -889,29 +887,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mtk_afe_hdmi_ops,
+		.ops = &mt8173_afe_hdmi_ops,
 	},
 };
 
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o03_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I05 Switch", AFE_CONN1, 21, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o04_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I06 Switch", AFE_CONN2, 6, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o09_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
-static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+static const struct snd_kcontrol_new mt8173_afe_o10_mix[] = {
 	SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
 	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
-static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
+static const struct snd_soc_dapm_widget mt8173_afe_pcm_widgets[] = {
 	/* inter-connections */
 	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
 	SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -921,16 +919,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
 	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o03_mix, ARRAY_SIZE(mtk_afe_o03_mix)),
+			   mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
 	SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+			   mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
 	SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+			   mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
 	SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
-			   mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+			   mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
 };
 
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
 	{"I05", NULL, "DL1"},
 	{"I06", NULL, "DL1"},
 	{"I2S Playback", NULL, "O03"},
@@ -949,41 +947,41 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
 	{ "O10", "I04 Switch", "I04" },
 };
 
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_hdmi_routes[] = {
 	{"HDMIO Playback", NULL, "HDMI"},
 };
 
-static const struct snd_soc_component_driver mtk_afe_pcm_dai_component = {
-	.name = "mtk-afe-pcm-dai",
-	.dapm_widgets = mtk_afe_pcm_widgets,
-	.num_dapm_widgets = ARRAY_SIZE(mtk_afe_pcm_widgets),
-	.dapm_routes = mtk_afe_pcm_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_pcm_routes),
+static const struct snd_soc_component_driver mt8173_afe_pcm_dai_component = {
+	.name = "mt8173-afe-pcm-dai",
+	.dapm_widgets = mt8173_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt8173_afe_pcm_widgets),
+	.dapm_routes = mt8173_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_pcm_routes),
 };
 
-static const struct snd_soc_component_driver mtk_afe_hdmi_dai_component = {
-	.name = "mtk-afe-hdmi-dai",
-	.dapm_routes = mtk_afe_hdmi_routes,
-	.num_dapm_routes = ARRAY_SIZE(mtk_afe_hdmi_routes),
+static const struct snd_soc_component_driver mt8173_afe_hdmi_dai_component = {
+	.name = "mt8173-afe-hdmi-dai",
+	.dapm_routes = mt8173_afe_hdmi_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt8173_afe_hdmi_routes),
 };
 
-static const char *aud_clks[MTK_CLK_NUM] = {
-	[MTK_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
-	[MTK_CLK_TOP_PDN_AUD] = "top_pdn_audio",
-	[MTK_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
-	[MTK_CLK_I2S0_M] =  "i2s0_m",
-	[MTK_CLK_I2S1_M] =  "i2s1_m",
-	[MTK_CLK_I2S2_M] =  "i2s2_m",
-	[MTK_CLK_I2S3_M] =  "i2s3_m",
-	[MTK_CLK_I2S3_B] =  "i2s3_b",
-	[MTK_CLK_BCK0] =  "bck0",
-	[MTK_CLK_BCK1] =  "bck1",
+static const char *aud_clks[MT8173_CLK_NUM] = {
+	[MT8173_CLK_INFRASYS_AUD] = "infra_sys_audio_clk",
+	[MT8173_CLK_TOP_PDN_AUD] = "top_pdn_audio",
+	[MT8173_CLK_TOP_PDN_AUD_BUS] = "top_pdn_aud_intbus",
+	[MT8173_CLK_I2S0_M] =  "i2s0_m",
+	[MT8173_CLK_I2S1_M] =  "i2s1_m",
+	[MT8173_CLK_I2S2_M] =  "i2s2_m",
+	[MT8173_CLK_I2S3_M] =  "i2s3_m",
+	[MT8173_CLK_I2S3_B] =  "i2s3_b",
+	[MT8173_CLK_BCK0] =  "bck0",
+	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
-		.id = MTK_AFE_MEMIF_DL1,
+		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
 		.fs_shift = 0,
@@ -997,7 +995,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 0,
 	}, {
 		.name = "DL2",
-		.id = MTK_AFE_MEMIF_DL2,
+		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
 		.fs_shift = 4,
@@ -1011,7 +1009,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 1,
 	}, {
 		.name = "VUL",
-		.id = MTK_AFE_MEMIF_VUL,
+		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
 		.fs_shift = 16,
@@ -1025,7 +1023,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 6,
 	}, {
 		.name = "DAI",
-		.id = MTK_AFE_MEMIF_DAI,
+		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
 		.fs_shift = 24,
@@ -1039,7 +1037,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 5,
 	}, {
 		.name = "AWB",
-		.id = MTK_AFE_MEMIF_AWB,
+		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
 		.fs_shift = 12,
@@ -1053,7 +1051,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 3,
 	}, {
 		.name = "MOD_DAI",
-		.id = MTK_AFE_MEMIF_MOD_DAI,
+		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
 		.fs_shift = 30,
@@ -1067,7 +1065,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 		.msb_shift = 4,
 	}, {
 		.name = "HDMI",
-		.id = MTK_AFE_MEMIF_HDMI,
+		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
 		.fs_shift = -1,
@@ -1082,7 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
 	},
 };
 
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
 	.reg_bits = 32,
 	.reg_stride = 4,
 	.val_bits = 32,
@@ -1090,9 +1088,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
 	.cache_type = REGCACHE_NONE,
 };
 
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mtk_afe *afe = dev_id;
+	struct mt8173_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1103,8 +1101,8 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
 		goto err_irq;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
-		struct mtk_afe_memif *memif = &afe->memif[i];
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+		struct mt8173_afe_memif *memif = &afe->memif[i];
 
 		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
 			continue;
@@ -1119,9 +1117,9 @@ err_irq:
 	return IRQ_HANDLED;
 }
 
-static int mtk_afe_runtime_suspend(struct device *dev)
+static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1129,45 +1127,44 @@ static int mtk_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
-static int mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mtk_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe *afe = dev_get_drvdata(dev);
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MTK_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1184,23 +1181,22 @@ static int mtk_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
-
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S1_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_I2S2_M]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_BCK0]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
 {
 	size_t i;
 
@@ -1212,16 +1208,16 @@ static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
 			return PTR_ERR(afe->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
-static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mtk_afe *afe;
+	struct mt8173_afe *afe;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1239,7 +1235,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name);
 		return -ENXIO;
 	}
-	ret = devm_request_irq(afe->dev, irq_id, mtk_afe_irq_handler,
+	ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
 			       0, "Afe_ISR_Handle", (void *)afe);
 	if (ret) {
 		dev_err(afe->dev, "could not request_irq\n");
@@ -1252,48 +1248,48 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
 		return PTR_ERR(afe->base_addr);
 
 	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
-		&mtk_afe_regmap_config);
+		&mt8173_afe_regmap_config);
 	if (IS_ERR(afe->regmap))
 		return PTR_ERR(afe->regmap);
 
 	/* initial audio related clock */
-	ret = mtk_afe_init_audio_clk(afe);
+	ret = mt8173_afe_init_audio_clk(afe);
 	if (ret) {
-		dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+		dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
 		return ret;
 	}
 
-	for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
 		afe->memif[i].data = &memif_data[i];
 
 	platform_set_drvdata(pdev, afe);
 
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = mtk_afe_runtime_resume(&pdev->dev);
+		ret = mt8173_afe_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_pcm_dai_component,
-					 mtk_afe_pcm_dais,
-					 ARRAY_SIZE(mtk_afe_pcm_dais));
+					 &mt8173_afe_pcm_dai_component,
+					 mt8173_afe_pcm_dais,
+					 ARRAY_SIZE(mt8173_afe_pcm_dais));
 	if (ret)
 		goto err_platform;
 
 	ret = snd_soc_register_component(&pdev->dev,
-					 &mtk_afe_hdmi_dai_component,
-					 mtk_afe_hdmi_dais,
-					 ARRAY_SIZE(mtk_afe_hdmi_dais));
+					 &mt8173_afe_hdmi_dai_component,
+					 mt8173_afe_hdmi_dais,
+					 ARRAY_SIZE(mt8173_afe_hdmi_dais));
 	if (ret)
 		goto err_comp;
 
-	dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+	dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
 	return 0;
 
 err_comp:
@@ -1305,38 +1301,38 @@ err_pm_disable:
 	return ret;
 }
 
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
 {
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
-		mtk_afe_runtime_suspend(&pdev->dev);
+		mt8173_afe_runtime_suspend(&pdev->dev);
 	snd_soc_unregister_component(&pdev->dev);
 	snd_soc_unregister_platform(&pdev->dev);
 	return 0;
 }
 
-static const struct of_device_id mtk_afe_pcm_dt_match[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
 	{ .compatible = "mediatek,mt8173-afe-pcm", },
 	{ }
 };
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_afe_pcm_dt_match);
 
-static const struct dev_pm_ops mtk_afe_pm_ops = {
-	SET_RUNTIME_PM_OPS(mtk_afe_runtime_suspend, mtk_afe_runtime_resume,
-			   NULL)
+static const struct dev_pm_ops mt8173_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+			   mt8173_afe_runtime_resume, NULL)
 };
 
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
 	.driver = {
-		   .name = "mtk-afe-pcm",
-		   .of_match_table = mtk_afe_pcm_dt_match,
-		   .pm = &mtk_afe_pm_ops,
+		   .name = "mt8173-afe-pcm",
+		   .of_match_table = mt8173_afe_pcm_dt_match,
+		   .pm = &mt8173_afe_pm_ops,
 	},
-	.probe = mtk_afe_pcm_dev_probe,
-	.remove = mtk_afe_pcm_dev_remove,
+	.probe = mt8173_afe_pcm_dev_probe,
+	.remove = mt8173_afe_pcm_dev_remove,
 };
 
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
 
 MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
 MODULE_AUTHOR("Koro Chen <koro.chen at mediatek.com>");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-max98090.c
rename to sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35..5524a2c 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -18,7 +18,7 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <linux/gpio.h>
-#include "../codecs/max98090.h"
+#include "../../codecs/max98090.h"
 
 static struct snd_soc_jack mt8173_max98090_jack;
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5514.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e0836..467f704 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650-rt5676.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb59392..1b8b2a7 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -19,8 +19,8 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
-#include "../codecs/rt5677.h"
+#include "../../codecs/rt5645.h"
+#include "../../codecs/rt5677.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
similarity index 99%
rename from sound/soc/mediatek/mt8173-rt5650.c
rename to sound/soc/mediatek/mt8173/mt8173-rt5650.c
index 072934b..d478976 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -19,7 +19,7 @@
 #include <linux/of_gpio.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
 
 #define MCLK_FOR_CODECS		12288000
 
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index f341f62..0000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * mtk_afe_common.h  --  Mediatek audio driver common definitions
- *
- * Copyright (c) 2015 MediaTek Inc.
- * Author: Koro Chen <koro.chen at mediatek.com>
- *             Sascha Hauer <s.hauer at pengutronix.de>
- *             Hidalgo Huang <hidalgo.huang at mediatek.com>
- *             Ir Lian <ir.lian at mediatek.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _MTK_AFE_COMMON_H_
-#define _MTK_AFE_COMMON_H_
-
-#include <linux/clk.h>
-#include <linux/regmap.h>
-
-enum {
-	MTK_AFE_MEMIF_DL1,
-	MTK_AFE_MEMIF_DL2,
-	MTK_AFE_MEMIF_VUL,
-	MTK_AFE_MEMIF_DAI,
-	MTK_AFE_MEMIF_AWB,
-	MTK_AFE_MEMIF_MOD_DAI,
-	MTK_AFE_MEMIF_HDMI,
-	MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM1 = MTK_AFE_MEMIF_NUM,
-	MTK_AFE_IO_MOD_PCM2,
-	MTK_AFE_IO_PMIC,
-	MTK_AFE_IO_I2S,
-	MTK_AFE_IO_2ND_I2S,
-	MTK_AFE_IO_HW_GAIN1,
-	MTK_AFE_IO_HW_GAIN2,
-	MTK_AFE_IO_MRG_O,
-	MTK_AFE_IO_MRG_I,
-	MTK_AFE_IO_DAIBT,
-	MTK_AFE_IO_HDMI,
-};
-
-enum {
-	MTK_AFE_IRQ_1,
-	MTK_AFE_IRQ_2,
-	MTK_AFE_IRQ_3,
-	MTK_AFE_IRQ_4,
-	MTK_AFE_IRQ_5,
-	MTK_AFE_IRQ_6,
-	MTK_AFE_IRQ_7,
-	MTK_AFE_IRQ_8,
-	MTK_AFE_IRQ_NUM,
-};
-
-enum {
-	MTK_CLK_INFRASYS_AUD,
-	MTK_CLK_TOP_PDN_AUD,
-	MTK_CLK_TOP_PDN_AUD_BUS,
-	MTK_CLK_I2S0_M,
-	MTK_CLK_I2S1_M,
-	MTK_CLK_I2S2_M,
-	MTK_CLK_I2S3_M,
-	MTK_CLK_I2S3_B,
-	MTK_CLK_BCK0,
-	MTK_CLK_BCK1,
-	MTK_CLK_NUM
-};
-
-struct mtk_afe;
-struct snd_pcm_substream;
-
-struct mtk_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mtk_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mtk_afe_memif_data *data;
-	const struct mtk_afe_irq_data *irqdata;
-};
-
-#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:59 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:59 +0800
Subject: [alsa-devel] [PATCH v5 8/9] ASoC: mediatek: add BT implementation
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-9-git-send-email-garlic.tseng@mediatek.com>

Add BT implementation for mt2701 platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 139 +++++++++++++++++++++++++++++
 1 file changed, 139 insertions(+)

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index deb5f82..5fd2caa 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -377,6 +377,86 @@ static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 	return 0;
 }
 
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+	afe_priv->mrg_enable[substream->stream] = 1;
+	return 0;
+}
+
+static int mt2701_btmrg_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_fs;
+	u32 val, msk;
+
+	stream_fs = params_rate(params);
+
+	if ((stream_fs != 8000) && (stream_fs != 16000)) {
+		dev_err(afe->dev, "%s() btmgr not supprt this stream_fs %d\n",
+			__func__, stream_fs);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_I2S_MODE_MASK,
+			   AFE_MRGIF_CON_I2S_MODE_32K);
+
+	val = AFE_DAIBT_CON0_BT_FUNC_EN | AFE_DAIBT_CON0_BT_FUNC_RDY
+	      | AFE_DAIBT_CON0_MRG_USE;
+	msk = val;
+
+	if (stream_fs == 16000)
+		val |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	msk |= AFE_DAIBT_CON0_BT_WIDE_MODE_EN;
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0, msk, val);
+
+	regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+			   AFE_DAIBT_CON0_DAIBT_EN,
+			   AFE_DAIBT_CON0_DAIBT_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_I2S_EN,
+			   AFE_MRGIF_CON_MRG_I2S_EN);
+	regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+			   AFE_MRGIF_CON_MRG_EN,
+			   AFE_MRGIF_CON_MRG_EN);
+	return 0;
+}
+
+static void mt2701_btmrg_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	/* if the other direction stream is not occupied */
+	if (!afe_priv->mrg_enable[!substream->stream]) {
+		regmap_update_bits(afe->regmap, AFE_DAIBT_CON0,
+				   AFE_DAIBT_CON0_DAIBT_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_EN, 0);
+		regmap_update_bits(afe->regmap, AFE_MRGIF_CON,
+				   AFE_MRGIF_CON_MRG_I2S_EN, 0);
+		regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+				   AUDIO_TOP_CON4_PDN_MRGIF,
+				   AUDIO_TOP_CON4_PDN_MRGIF);
+	}
+	afe_priv->mrg_enable[substream->stream] = 0;
+}
+
 static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
@@ -560,6 +640,13 @@ static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
 	.set_fmt	= mt2701_afe_i2s_set_fmt,
 };
 
+/* MRG BE DAIs */
+static struct snd_soc_dai_ops mt2701_btmrg_ops = {
+	.startup = mt2701_btmrg_startup,
+	.shutdown = mt2701_btmrg_shutdown,
+	.hw_params = mt2701_btmrg_hw_params,
+};
+
 static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
@@ -610,6 +697,36 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		},
 		.ops = &mt2701_single_memif_dai_ops,
 	},
+	{
+		.name = "PCM_BT_DL",
+		.id = MT2701_MEMIF_DLBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM_BT_UL",
+		.id = MT2701_MEMIF_ULBT,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "ULBT",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
 	/* BE DAIs */
 	{
 		.name = "I2S0",
@@ -709,6 +826,28 @@ static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
 		.ops = &mt2701_afe_i2s_ops,
 		.symmetric_rates = 1,
 	},
+	{
+		.name = "MRG BT",
+		.id = MT2701_IO_MRG,
+		.playback = {
+			.stream_name = "BT Playback",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.stream_name = "BT Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = (SNDRV_PCM_RATE_8000
+				| SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_btmrg_ops,
+		.symmetric_rates = 1,
+	}
 };
 
 static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:58 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:58 +0800
Subject: [alsa-devel] [PATCH v5 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-8-git-send-email-garlic.tseng@mediatek.com>

Add supports for 16k (wideband BT) and add a general compatible
string "linux,bt-sco"

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 Documentation/devicetree/bindings/sound/bt-sco.txt | 2 +-
 sound/soc/codecs/bt-sco.c                          | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/bt-sco.txt b/Documentation/devicetree/bindings/sound/bt-sco.txt
index 29b8e5d..641edf7 100644
--- a/Documentation/devicetree/bindings/sound/bt-sco.txt
+++ b/Documentation/devicetree/bindings/sound/bt-sco.txt
@@ -4,7 +4,7 @@ This device support generic Bluetooth SCO link.
 
 Required properties:
 
-  - compatible : "delta,dfbmcs320"
+  - compatible : "delta,dfbmcs320" or "linux,bt-sco"
 
 Example:
 
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad1..101b384 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -31,14 +31,14 @@ static struct snd_soc_dai_driver bt_sco_dai = {
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	.capture = {
 		 .stream_name = "Capture",
 		.channels_min = 1,
 		.channels_max = 1,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 };
@@ -77,6 +77,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
 #if defined(CONFIG_OF)
 static const struct of_device_id bt_sco_codec_of_match[] = {
 	{ .compatible = "delta,dfbmcs320", },
+	{ .compatible = "linux,bt-sco", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:56 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:56 +0800
Subject: [alsa-devel] [PATCH v5 5/9] ASoC: mediatek: add structure define and
 clock control for 2701
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-6-git-send-email-garlic.tseng@mediatek.com>

add structure define and clock control function for 2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c | 464 ++++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h |  38 ++
 sound/soc/mediatek/mt2701/mt2701-afe-common.h     | 181 +++++++++
 sound/soc/mediatek/mt2701/mt2701-reg.h            | 186 +++++++++
 4 files changed, 869 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 0000000..b815ecc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+	[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+	[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+	[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+	[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+	[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+	[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+	[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+	[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+	[MT2701_AUD_APLL_SEL] = "top_apll_sel",
+	[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+	[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+	[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+	[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+	[MT2701_AUD_AUDPLL] = "top_audpll",
+	[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+	[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+	[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+	[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+	[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+	[MT2701_AUD_CLK_26M] = "clk_26m",
+	[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+	[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+	[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+	[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+	[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+	[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+	[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+	[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+	[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+	[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+	[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+	[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+	[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+	[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+	[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+	[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+	[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+	[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+	[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+	[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+	[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+	[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+	[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+	[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i = 0;
+
+	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(aud_clks[i])) {
+			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+				 __func__, aud_clks[i]);
+			return PTR_ERR(aud_clks[i]);
+		}
+	}
+
+	return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+	int ret = 0;
+
+	ret = mt2701_turn_on_a1sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_a2sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_afe_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		mt2701_turn_off_a2sys_clock(afe);
+		return ret;
+	}
+
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON,
+			   AFE_DAC_CON0_AFE_ON);
+	regmap_write(afe->regmap, PWR2_TOP_CON,
+		     PWR2_TOP_CON_INIT_VAL);
+	regmap_write(afe->regmap, PWR1_ASM_CON1,
+		     PWR1_ASM_CON1_INIT_VAL);
+	regmap_write(afe->regmap, PWR2_ASM_CON1,
+		     PWR2_ASM_CON1_INIT_VAL);
+
+	return 0;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+	mt2701_turn_off_afe_clock(afe);
+	mt2701_turn_off_a1sys_clock(afe);
+	mt2701_turn_off_a2sys_clock(afe);
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+			aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+			   MT2701_AUD_AUD_MUX1_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+		goto A1SYS_CLK_AUD_48K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A1SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A1SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+			   MT2701_AUD_AUD_MUX2_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+		goto A2SYS_CLK_AUD_44K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A2SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A2SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	/* enable INFRA_SYS */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto AFE_AUD_INFRA_ERR;
+	}
+
+	/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+			     afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUDINTBUS],
+			aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_M_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+	return 0;
+
+AFE_AUD_ASM_M_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK,
+			   AUDIO_TOP_CON0_PDN_APLL_CK);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS,
+			   AUDIO_TOP_CON4_PDN_A1SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS,
+			   AUDIO_TOP_CON4_PDN_A2SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+	int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+	int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+	/* Set MCLK Kx_SRC_SEL(domain) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_clk_id], ret);
+
+	if (domain == 0) {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+	} else {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+	}
+	clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+	/* Set MCLK Kx_SRC_DIV(divider) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_div_id], ret);
+
+	ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+			aud_clks[aud_src_div_id], mclk, ret);
+	clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 0000000..6497d57
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 0000000..c77166e
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,181 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE	98304000
+#define MT2701_PLL_DOMAIN_1_RATE	90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+	MT2701_I2S_1,
+	MT2701_I2S_2,
+	MT2701_I2S_3,
+	MT2701_I2S_4,
+	MT2701_I2S_NUM,
+};
+
+enum {
+	MT2701_MEMIF_DL1,
+	MT2701_MEMIF_DL2,
+	MT2701_MEMIF_DL3,
+	MT2701_MEMIF_DL4,
+	MT2701_MEMIF_DL5,
+	MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_UL1,
+	MT2701_MEMIF_UL2,
+	MT2701_MEMIF_UL3,
+	MT2701_MEMIF_UL4,
+	MT2701_MEMIF_UL5,
+	MT2701_MEMIF_DLBT,
+	MT2701_MEMIF_ULBT,
+	MT2701_MEMIF_NUM,
+	MT2701_IO_I2S = MT2701_MEMIF_NUM,
+	MT2701_IO_2ND_I2S,
+	MT2701_IO_3RD_I2S,
+	MT2701_IO_4TH_I2S,
+	MT2701_IO_5TH_I2S,
+	MT2701_IO_6TH_I2S,
+	MT2701_IO_MRG,
+};
+
+enum {
+	MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ2,
+	MT2701_IRQ_ASYS_IRQ3,
+	MT2701_IRQ_ASYS_END,
+};
+
+enum {
+	DIV_ID_MCLK_TO_BCK,
+	DIV_ID_BCK_TO_LRCK,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+	MT2701_AUD_INFRA_SYS_AUDIO,
+	MT2701_AUD_AUD_MUX1_SEL,
+	MT2701_AUD_AUD_MUX2_SEL,
+	MT2701_AUD_AUD_MUX1_DIV,
+	MT2701_AUD_AUD_MUX2_DIV,
+	MT2701_AUD_AUD_48K_TIMING,
+	MT2701_AUD_AUD_44K_TIMING,
+	MT2701_AUD_AUDPLL_MUX_SEL,
+	MT2701_AUD_APLL_SEL,
+	MT2701_AUD_AUD1PLL_98M,
+	MT2701_AUD_AUD2PLL_90M,
+	MT2701_AUD_HADDS2PLL_98M,
+	MT2701_AUD_HADDS2PLL_294M,
+	MT2701_AUD_AUDPLL,
+	MT2701_AUD_AUDPLL_D4,
+	MT2701_AUD_AUDPLL_D8,
+	MT2701_AUD_AUDPLL_D16,
+	MT2701_AUD_AUDPLL_D24,
+	MT2701_AUD_AUDINTBUS,
+	MT2701_AUD_CLK_26M,
+	MT2701_AUD_SYSPLL1_D4,
+	MT2701_AUD_AUD_K1_SRC_SEL,
+	MT2701_AUD_AUD_K2_SRC_SEL,
+	MT2701_AUD_AUD_K3_SRC_SEL,
+	MT2701_AUD_AUD_K4_SRC_SEL,
+	MT2701_AUD_AUD_K5_SRC_SEL,
+	MT2701_AUD_AUD_K6_SRC_SEL,
+	MT2701_AUD_AUD_K1_SRC_DIV,
+	MT2701_AUD_AUD_K2_SRC_DIV,
+	MT2701_AUD_AUD_K3_SRC_DIV,
+	MT2701_AUD_AUD_K4_SRC_DIV,
+	MT2701_AUD_AUD_K5_SRC_DIV,
+	MT2701_AUD_AUD_K6_SRC_DIV,
+	MT2701_AUD_AUD_I2S1_MCLK,
+	MT2701_AUD_AUD_I2S2_MCLK,
+	MT2701_AUD_AUD_I2S3_MCLK,
+	MT2701_AUD_AUD_I2S4_MCLK,
+	MT2701_AUD_AUD_I2S5_MCLK,
+	MT2701_AUD_AUD_I2S6_MCLK,
+	MT2701_AUD_ASM_M_SEL,
+	MT2701_AUD_ASM_H_SEL,
+	MT2701_AUD_UNIVPLL2_D4,
+	MT2701_AUD_UNIVPLL2_D2,
+	MT2701_AUD_SYSPLL_D5,
+	MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AUDIO_TOP_CON4,
+	AUDIO_TOP_CON5,
+	ASYS_TOP_CON,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN2,
+	AFE_CONN3,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN22,
+	AFE_DAC_CON0,
+	AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+	int i2s_ctrl_reg;
+	int i2s_pwn_shift;
+	int i2s_asrc_fs_shift;
+	int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+	I2S_OUT,
+	I2S_IN,
+	I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+	int dai_id;
+	int mclk_rate;
+	int div_mclk_to_bck;
+	int div_bck_to_lrck;
+	int format;
+	snd_pcm_format_t stream_fmt;
+	int on[I2S_DIR_NUM];
+	int occupied[I2S_DIR_NUM];
+	const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+	struct clk *clocks[MT2701_CLOCK_NUM];
+	struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+	bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 0000000..bb62b1c
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON	(0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE		(0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK	(0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN	(0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS	(0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS	(0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN	(0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF	(0x1 << 25)
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN		(0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN	(0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY	(0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN	(0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE		(0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL		(0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN		(0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN	(0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK	(0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K	(0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS		(0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)	((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH		(0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN		(0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN		(0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE	(0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)	((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL		(0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL		(0xffffffff)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL		(0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON		(0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK		(0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE	(0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE	(0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK			(0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK		(0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)		((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK	(0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES		(0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS			(0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)		((x) << 8)
+#define ASYS_I2S_CON_RESET		(0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN		(0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE	(0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE		(0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)	((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX		(0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:44:00 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:44:00 +0800
Subject: [alsa-devel] [PATCH v5 9/9] ASoC: mediatek: Add mt2701-cs42448 driver
 and config option.
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-10-git-send-email-garlic.tseng@mediatek.com>

Add machine driver and config option for MT2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                 |  21 ++
 sound/soc/mediatek/Makefile                |   1 +
 sound/soc/mediatek/mt2701/Makefile         |  19 ++
 sound/soc/mediatek/mt2701/mt2701-cs42448.c | 422 +++++++++++++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/Makefile
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-cs42448.c

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 705904b..2fbe543 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,27 @@
 config SND_SOC_MEDIATEK
 	tristate
 
+config SND_SOC_MT2701
+	tristate "ASoC support for Mediatek MT2701 chip"
+	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  that can be used with other codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
+config SND_SOC_MT2701_CS42448
+	tristate "ASoc Audio driver for MT2701 with CS42448 codec"
+	depends on SND_SOC_MT2701
+	select SND_SOC_CS42XX8_I2C
+	select SND_SOC_BT_SCO
+	help
+	  This adds ASoC driver for Mediatek MT2701 boards
+	  with the CS42448 codecs.
+	  Select Y if you have such device.
+	  If unsure select "N".
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 4fe8068..6bcab35 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 0000000..31c3d04
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := mt2701-afe-pcm.o mt2701-afe-clock-ctrl.o
+obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
+
+# machine driver
+obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
new file mode 100644
index 0000000..0e48cb9
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,422 @@
+/*
+ * mt2701-cs42448.c  --  MT2701 CS42448 ALSA SoC machine driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Ir Lian <ir.lian at mediatek.com>
+ *              Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+	int i2s1_in_mux;
+	int i2s1_in_mux_gpio_sel_1;
+	int i2s1_in_mux_gpio_sel_2;
+};
+
+static const char * const i2sin_mux_switch_text[] = {
+	"ADC_SDOUT2",
+	"ADC_SDOUT3",
+	"I2S_IN_1",
+	"I2S_IN_2",
+};
+
+static const struct soc_enum i2sin_mux_enum =
+	SOC_ENUM_SINGLE_EXT(4, i2sin_mux_switch_text);
+
+static int mt2701_cs42448_i2sin1_mux_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	ucontrol->value.integer.value[0] = priv->i2s1_in_mux;
+	return 0;
+}
+
+static int mt2701_cs42448_i2sin1_mux_set(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
+	struct mt2701_cs42448_private *priv = snd_soc_card_get_drvdata(card);
+
+	if (ucontrol->value.integer.value[0] == priv->i2s1_in_mux)
+		return 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 1:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+		break;
+	case 2:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	case 3:
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+		gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+		break;
+	default:
+		dev_warn(card->dev, "%s invalid setting\n", __func__);
+	}
+
+	priv->i2s1_in_mux = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget
+			mt2701_cs42448_asoc_card_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Line Out Jack", NULL),
+	SND_SOC_DAPM_MIC("AMIC", NULL),
+	SND_SOC_DAPM_LINE("Tuner In", NULL),
+	SND_SOC_DAPM_LINE("Satellite Tuner In", NULL),
+	SND_SOC_DAPM_LINE("AUX In", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_cs42448_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Line Out Jack"),
+	SOC_DAPM_PIN_SWITCH("AMIC"),
+	SOC_DAPM_PIN_SWITCH("Tuner In"),
+	SOC_DAPM_PIN_SWITCH("Satellite Tuner In"),
+	SOC_DAPM_PIN_SWITCH("AUX In"),
+	SOC_ENUM_EXT("I2SIN1_MUX_Switch", i2sin_mux_enum,
+		     mt2701_cs42448_i2sin1_mux_get,
+		     mt2701_cs42448_i2sin1_mux_set),
+};
+
+static const unsigned int mt2701_cs42448_sampling_rates[] = {48000};
+
+static struct snd_pcm_hw_constraint_list mt2701_cs42448_constraints_rates = {
+		.count = ARRAY_SIZE(mt2701_cs42448_sampling_rates),
+		.list = mt2701_cs42448_sampling_rates,
+		.mask = 0,
+};
+
+static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
+{
+	int err;
+
+	err = snd_pcm_hw_constraint_list(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_RATE,
+					 &mt2701_cs42448_constraints_rates);
+	if (err < 0) {
+		dev_err(substream->pcm->card->dev,
+			"%s snd_pcm_hw_constraint_list failed: 0x%x\n",
+			__func__, err);
+		return err;
+	}
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+	.startup = mt2701_cs42448_fe_ops_startup,
+};
+
+static int mt2701_cs42448_be_ops_hw_params(struct snd_pcm_substream *substream,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	/* codec slave, mt2701 master */
+	unsigned int fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			    | SND_SOC_DAIFMT_CONT;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+	unsigned int div_mclk_to_bck = rate > 192000 ? 2 : 4;
+	unsigned int div_bck_to_lrck = 64;
+
+	mclk_rate = rate * div_bck_to_lrck * div_mclk_to_bck;
+
+	/* mt2701 mclk */
+	snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+	/* mt2701 bck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_MCLK_TO_BCK, div_mclk_to_bck);
+	/* mt2701 lrck */
+	snd_soc_dai_set_clkdiv(cpu_dai, DIV_ID_BCK_TO_LRCK, div_bck_to_lrck);
+	/* mt2701 master */
+	snd_soc_dai_set_fmt(cpu_dai, fmt);
+
+	/* codec mclk */
+	snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+	/* codec slave */
+	snd_soc_dai_set_fmt(codec_dai, fmt);
+	return 0;
+}
+
+static struct snd_soc_ops mt2701_cs42448_be_ops = {
+	.hw_params = mt2701_cs42448_be_ops_hw_params
+};
+
+enum {
+	DAI_LINK_FE_MULTI_CH_OUT,
+	DAI_LINK_FE_PCM0_IN,
+	DAI_LINK_FE_PCM1_IN,
+	DAI_LINK_FE_BT_OUT,
+	DAI_LINK_FE_BT_IN,
+	DAI_LINK_BE_I2S0,
+	DAI_LINK_BE_I2S1,
+	DAI_LINK_BE_I2S2,
+	DAI_LINK_BE_I2S3,
+	DAI_LINK_BE_MRG_BT,
+};
+
+static struct snd_soc_dai_link mt2701_cs42448_dai_links[] = {
+	/* FE */
+	[DAI_LINK_FE_MULTI_CH_OUT] = {
+		.name = "mt2701-cs42448-multi-ch-out",
+		.stream_name = "mt2701-cs42448-multi-ch-out",
+		.cpu_dai_name = "PCM_multi",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_PCM0_IN] = {
+		.name = "mt2701-cs42448-pcm0",
+		.stream_name = "mt2701-cs42448-pcm0-data-UL",
+		.cpu_dai_name = "PCM0",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_PCM1_IN] = {
+		.name = "mt2701-cs42448-pcm1-data-UL",
+		.stream_name = "mt2701-cs42448-pcm1-data-UL",
+		.cpu_dai_name = "PCM1",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ops = &mt2701_cs42448_48k_fe_ops,
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_FE_BT_OUT] = {
+		.name = "mt2701-cs42448-pcm-BT-out",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_DL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_playback = 1,
+	},
+	[DAI_LINK_FE_BT_IN] = {
+		.name = "mt2701-cs42448-pcm-BT-in",
+		.stream_name = "mt2701-cs42448-pcm-BT",
+		.cpu_dai_name = "PCM_BT_UL",
+		.codec_name = "snd-soc-dummy",
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.dynamic = 1,
+		.dpcm_capture = 1,
+	},
+	/* BE */
+	[DAI_LINK_BE_I2S0] = {
+		.name = "mt2701-cs42448-I2S0",
+		.cpu_dai_name = "I2S0",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S1] = {
+		.name = "mt2701-cs42448-I2S1",
+		.cpu_dai_name = "I2S1",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S2] = {
+		.name = "mt2701-cs42448-I2S2",
+		.cpu_dai_name = "I2S2",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_I2S3] = {
+		.name = "mt2701-cs42448-I2S3",
+		.cpu_dai_name = "I2S3",
+		.no_pcm = 1,
+		.codec_dai_name = "cs42448",
+		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+			 | SND_SOC_DAIFMT_GATED,
+		.ops = &mt2701_cs42448_be_ops,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+	[DAI_LINK_BE_MRG_BT] = {
+		.name = "mt2701-cs42448-MRG-BT",
+		.cpu_dai_name = "MRG BT",
+		.no_pcm = 1,
+		.codec_dai_name = "bt-sco-pcm",
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+	},
+};
+
+static struct snd_soc_card mt2701_cs42448_soc_card = {
+	.name = "mt2701-cs42448",
+	.owner = THIS_MODULE,
+	.dai_link = mt2701_cs42448_dai_links,
+	.num_links = ARRAY_SIZE(mt2701_cs42448_dai_links),
+	.controls = mt2701_cs42448_controls,
+	.num_controls = ARRAY_SIZE(mt2701_cs42448_controls),
+	.dapm_widgets = mt2701_cs42448_asoc_card_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_cs42448_asoc_card_dapm_widgets),
+};
+
+static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &mt2701_cs42448_soc_card;
+	int ret;
+	int i;
+	struct device_node *platform_node, *codec_node, *codec_node_bt_mrg;
+	struct mt2701_cs42448_private *priv =
+		devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+			     GFP_KERNEL);
+	struct device *dev = &pdev->dev;
+
+	if (!priv)
+		return -ENOMEM;
+
+	platform_node = of_parse_phandle(pdev->dev.of_node,
+					 "mediatek,platform", 0);
+	if (!platform_node) {
+		dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].platform_name)
+			continue;
+		mt2701_cs42448_dai_links[i].platform_of_node = platform_node;
+	}
+
+	card->dev = dev;
+
+	codec_node = of_parse_phandle(pdev->dev.of_node,
+				      "mediatek,audio-codec", 0);
+	if (!codec_node) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec' missing or invalid\n");
+		return -EINVAL;
+	}
+	for (i = 0; i < card->num_links; i++) {
+		if (mt2701_cs42448_dai_links[i].codec_name)
+			continue;
+		mt2701_cs42448_dai_links[i].codec_of_node = codec_node;
+	}
+
+	codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node,
+					     "mediatek,audio-codec-bt-mrg", 0);
+	if (!codec_node_bt_mrg) {
+		dev_err(&pdev->dev,
+			"Property 'audio-codec-bt-mrg' missing or invalid\n");
+		return -EINVAL;
+	}
+	mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+							= codec_node_bt_mrg;
+
+	ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+		return ret;
+	}
+
+	priv->i2s1_in_mux_gpio_sel_1 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+					"i2s1_in_mux_gpio_sel_1");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+	}
+
+	priv->i2s1_in_mux_gpio_sel_2 =
+		of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+	if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+		ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+					"i2s1_in_mux_gpio_sel_2");
+		if (ret)
+			dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+				 __func__, ret);
+		gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+	}
+	snd_soc_card_set_drvdata(card, priv);
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+
+	if (ret)
+		dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+			__func__, ret);
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+	{.compatible = "mediatek,mt2701-cs42448-machine",},
+	{}
+};
+#endif
+
+static struct platform_driver mt2701_cs42448_machine = {
+	.driver = {
+		.name = "mt2701-cs42448",
+		   #ifdef CONFIG_OF
+		   .of_match_table = mt2701_cs42448_machine_dt_match,
+		   #endif
+	},
+	.probe = mt2701_cs42448_machine_probe,
+};
+
+module_platform_driver(mt2701_cs42448_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 CS42448 ALSA SoC machine driver");
+MODULE_AUTHOR("Ir Lian <ir.lian at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 cs42448 soc card");
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:54 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:54 +0800
Subject: [alsa-devel] [PATCH v5 3/9] ASoC: mediatek: let mt8173 use mediatek
 common structure
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-4-git-send-email-garlic.tseng@mediatek.com>

Modify mt8173 driver implementation to use common structure.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/Kconfig                    |   4 +
 sound/soc/mediatek/Makefile                   |   2 +-
 sound/soc/mediatek/common/Makefile            |  16 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
 sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 679 +++++++++++---------------
 5 files changed, 318 insertions(+), 425 deletions(-)
 create mode 100644 sound/soc/mediatek/common/Makefile

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ae9f664..705904b 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,10 @@
+config SND_SOC_MEDIATEK
+	tristate
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
 	help
 	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 240dfc70..4fe8068 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,2 @@
-# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 0000000..a55d33b
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
index 8f2936d..9a4837c 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -46,14 +46,13 @@ enum {
 };
 
 enum {
-	MT8173_AFE_IRQ_1,
-	MT8173_AFE_IRQ_2,
-	MT8173_AFE_IRQ_3,
-	MT8173_AFE_IRQ_4,
-	MT8173_AFE_IRQ_5,
-	MT8173_AFE_IRQ_6,
-	MT8173_AFE_IRQ_7,
-	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_DL1,
+	MT8173_AFE_IRQ_DL2,
+	MT8173_AFE_IRQ_VUL,
+	MT8173_AFE_IRQ_DAI,
+	MT8173_AFE_IRQ_AWB,
+	MT8173_AFE_IRQ_MOD_DAI,
+	MT8173_AFE_IRQ_HDMI,
 	MT8173_AFE_IRQ_NUM,
 };
 
@@ -71,31 +70,4 @@ enum {
 	MT8173_CLK_NUM
 };
 
-struct mt8173_afe;
-struct snd_pcm_substream;
-
-struct mt8173_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mt8173_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mt8173_afe_memif_data *data;
-	const struct mt8173_afe_irq_data *irqdata;
-};
-
 #endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 4fc52bc..8a643a3 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -25,6 +25,9 @@
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1		0x0548
 #define AFE_TDM_CON2		0x054c
 
-#define AFE_BASE_END_OFFSET	8
 #define AFE_IRQ_STATUS_BITS	0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -152,15 +154,8 @@ static const unsigned int mt8173_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mt8173_afe {
-	/* address for ioremap audio hardware register */
-	void __iomem *base_addr;
-	struct device *dev;
-	struct regmap *regmap;
-	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+struct mt8173_afe_private {
 	struct clk *clocks[MT8173_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
-	bool suspended;
 };
 
 static const struct snd_pcm_hardware mt8173_afe_hardware = {
@@ -174,53 +169,6 @@ static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mt8173_afe_pcm_pointer
-			 (struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int hw_ptr;
-	int ret;
-
-	ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-	if (ret || hw_ptr == 0) {
-		dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-		hw_ptr = memif->phys_buf_addr;
-	}
-
-	return bytes_to_frames(substream->runtime,
-			       hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
-	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mt8173_afe_pcm_pointer,
-};
-
-static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-	size_t size;
-	struct snd_card *card = rtd->card->snd_card;
-	struct snd_pcm *pcm = rtd->pcm;
-
-	size = mt8173_afe_hardware.buffer_bytes_max;
-
-	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-						     card->dev, size, size);
-}
-
-static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
-{
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
-	.ops = &mt8173_afe_pcm_ops,
-	.pcm_new = mt8173_afe_pcm_new,
-	.pcm_free = mt8173_afe_pcm_free,
-};
-
 struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
@@ -253,7 +201,7 @@ static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 	return -EINVAL;
 }
 
-static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
 	unsigned int val;
 	int fs = mt8173_afe_i2s_fs(rate);
@@ -281,7 +229,7 @@ static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,7 +244,7 @@ static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
 				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
@@ -319,7 +267,7 @@ static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
 				    struct clk *m_ck, unsigned int mck_rate,
 				    struct clk *b_ck, unsigned int bck_rate)
 {
@@ -343,7 +291,7 @@ static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
 					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
@@ -356,7 +304,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
 				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -370,7 +318,7 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
@@ -386,12 +334,13 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
 				 runtime->rate * 256, NULL, 0);
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
 				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
 	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
@@ -407,13 +356,14 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return 0;
 
-	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				    afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
@@ -421,13 +371,14 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
 				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return;
 
-	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				     afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				     afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
 static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
@@ -435,12 +386,14 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
 	unsigned int val;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
 				 runtime->rate * 128,
-				 afe->clocks[MT8173_CLK_I2S3_B],
+				 afe_priv->clocks[MT8173_CLK_I2S3_B],
 				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
@@ -496,7 +449,7 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -508,10 +461,14 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
 		regmap_write(afe->regmap, AFE_HDMI_CONN0,
-			     AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-			     AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-			     AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-			     AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+				 AFE_HDMI_CONN0_O30_I30 |
+				 AFE_HDMI_CONN0_O31_I31 |
+				 AFE_HDMI_CONN0_O32_I34 |
+				 AFE_HDMI_CONN0_O33_I35 |
+				 AFE_HDMI_CONN0_O34_I32 |
+				 AFE_HDMI_CONN0_O35_I33 |
+				 AFE_HDMI_CONN0_O36_I36 |
+				 AFE_HDMI_CONN0_O37_I37);
 
 		/* enable Out control */
 		regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -531,224 +488,46 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int ret;
-
-	memif->substream = substream;
-
-	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
-
-	/*
-	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-	 * smaller than period_size due to AFE's internal buffer.
-	 * This easily leads to overrun when avail_min is period_size.
-	 * One more period can hold the possible unread buffer.
-	 */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		ret = snd_pcm_hw_constraint_minmax(runtime,
-						   SNDRV_PCM_HW_PARAM_PERIODS,
-						   3,
-						   mt8173_afe_hardware.periods_max);
-		if (ret < 0) {
-			dev_err(afe->dev, "hw_constraint_minmax failed\n");
-			return ret;
-		}
-	}
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-	return ret;
-}
-
-static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				     struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-	memif->substream = NULL;
-}
-
-static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				     struct snd_pcm_hw_params *params,
-				     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int msb_at_bit33 = 0;
-	int ret;
-
-	dev_dbg(afe->dev,
-		"%s period = %u, rate= %u, channels=%u\n",
-		__func__, params_period_size(params), params_rate(params),
-		params_channels(params));
-
-	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-	if (ret < 0)
-		return ret;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int fs;
 
-	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-	memif->buffer_size = substream->runtime->dma_bytes;
-
-	/* start */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base, memif->phys_buf_addr);
-	/* end */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-		     memif->phys_buf_addr + memif->buffer_size - 1);
-
-	/* set MSB to 33-bit */
-	regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-			   1 << memif->data->msb_shift,
-			   msb_at_bit33 << memif->data->msb_shift);
-
-	/* set channel */
-	if (memif->data->mono_shift >= 0) {
-		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   1 << memif->data->mono_shift,
-				   mono << memif->data->mono_shift);
-	}
-
-	/* set rate */
-	if (memif->data->fs_shift < 0)
-		return 0;
 	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
 	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
-		unsigned int val;
-
-		switch (params_rate(params)) {
+		switch (rate) {
 		case 8000:
-			val = 0;
+			fs = 0;
 			break;
 		case 16000:
-			val = 1;
+			fs = 1;
 			break;
 		case 32000:
-			val = 2;
+			fs = 2;
 			break;
 		default:
 			return -EINVAL;
 		}
-
-		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-		else
-			regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-
 	} else {
-		int fs = mt8173_afe_i2s_fs(params_rate(params));
-
-		if (fs < 0)
-			return -EINVAL;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   0xf << memif->data->fs_shift,
-				   fs << memif->data->fs_shift);
+		fs = mt8173_afe_i2s_fs(rate);
 	}
-
-	return 0;
+	return fs;
 }
 
-static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-	return snd_pcm_lib_free_pages(substream);
+	return mt8173_afe_i2s_fs(rate);
 }
 
-static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int counter = runtime->period_size;
-
-	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift,
-					   1 << memif->data->enable_shift);
-
-		/* set irq counter */
-		regmap_update_bits(afe->regmap,
-				   memif->data->irq_reg_cnt,
-				   0x3ffff << memif->data->irq_cnt_shift,
-				   counter << memif->data->irq_cnt_shift);
-
-		/* set irq fs */
-		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mt8173_afe_i2s_fs(runtime->rate);
-
-			if (fs < 0)
-				return -EINVAL;
-
-			regmap_update_bits(afe->regmap,
-					   AFE_IRQ_MCU_CON,
-					   0xf << memif->data->irq_fs_shift,
-					   fs << memif->data->irq_fs_shift);
-		}
-		/* enable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   1 << memif->data->irq_en_shift);
-
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift, 0);
-		/* disable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   0 << memif->data->irq_en_shift);
-		/* and clear pending IRQ */
-		regmap_write(afe->regmap, AFE_IRQ_CLR,
-			     1 << memif->data->irq_clr_shift);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
-	.startup	= mt8173_afe_dais_startup,
-	.shutdown	= mt8173_afe_dais_shutdown,
-	.hw_params	= mt8173_afe_dais_hw_params,
-	.hw_free	= mt8173_afe_dais_hw_free,
-	.trigger	= mt8173_afe_dais_trigger,
-};
-
 /* BE DAIs */
 static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
 	.startup	= mt8173_afe_i2s_startup,
@@ -761,56 +540,15 @@ static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
 	.shutdown	= mt8173_afe_hdmi_shutdown,
 	.prepare	= mt8173_afe_hdmi_prepare,
 	.trigger	= mt8173_afe_hdmi_trigger,
-
 };
 
-static int mt8173_afe_runtime_suspend(struct device *dev);
-static int mt8173_afe_runtime_resume(struct device *dev);
-
-static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
-			    &afe->backup_regs[i]);
-
-	afe->suspended = true;
-	mt8173_afe_runtime_suspend(afe->dev);
-	return 0;
-}
-
-static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i = 0;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-		return 0;
-
-	mt8173_afe_runtime_resume(afe->dev);
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
-			     afe->backup_regs[i]);
-
-	afe->suspended = false;
-	return 0;
-}
-
 static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
 		.id = MT8173_AFE_MEMIF_DL1,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -818,12 +556,12 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
 		.id = MT8173_AFE_MEMIF_VUL,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -831,7 +569,7 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
@@ -860,8 +598,8 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	{
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -872,7 +610,7 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
@@ -978,105 +716,222 @@ static const char *aud_clks[MT8173_CLK_NUM] = {
 	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
 		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 21,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 1,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 0,
-		.irq_fs_shift = 4,
-		.irq_clr_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 0,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DL2",
 		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 22,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 2,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 2,
-		.irq_fs_shift = 16,
-		.irq_clr_shift = 2,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "VUL",
 		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 27,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 3,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 1,
-		.irq_fs_shift = 8,
-		.irq_clr_shift = 1,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 6,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DAI",
 		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON0,
 		.fs_shift = 24,
+		.fs_maskbit = 0x3,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 4,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 5,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "AWB",
 		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 24,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 6,
-		.irq_reg_cnt = AFE_IRQ_CNT7,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 14,
-		.irq_fs_shift = 24,
-		.irq_clr_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 3,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "MOD_DAI",
 		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 30,
+		.fs_maskbit = 0x3,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 30,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 7,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 4,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
 		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = -1,
 		.enable_shift = -1,
-		.irq_reg_cnt = AFE_IRQ_CNT5,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 8,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+	{
+		.id = MT8173_AFE_IRQ_DL1,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8173_AFE_IRQ_DL2,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 2,
+
+	}, {
+		.id = MT8173_AFE_IRQ_VUL,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_AWB,
+		.irq_cnt_reg = AFE_IRQ_CNT7,
 		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_HDMI,
+		.irq_cnt_reg = AFE_IRQ_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
 		.irq_en_shift = 12,
+		.irq_fs_reg = -1,
 		.irq_fs_shift = -1,
+		.irq_fs_maskbit = -1,
+		.irq_clr_reg = AFE_IRQ_CLR,
 		.irq_clr_shift = 4,
-		.msb_shift = 8,
 	},
 };
 
@@ -1090,7 +945,7 @@ static const struct regmap_config mt8173_afe_regmap_config = {
 
 static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mt8173_afe *afe = dev_id;
+	struct mtk_base_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1102,9 +957,15 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 	}
 
 	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
-		struct mt8173_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *irq;
 
-		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+		if (memif->irq_usage < 0)
+			continue;
+
+		irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
 			continue;
 
 		snd_pcm_period_elapsed(memif->substream);
@@ -1112,14 +973,16 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 
 err_irq:
 	/* clear irq */
-	regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+	regmap_write(afe->regmap, AFE_IRQ_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
 
 	return IRQ_HANDLED;
 }
 
 static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1127,44 +990,46 @@ static int mt8173_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
 static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1181,35 +1046,37 @@ static int mt8173_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
+
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
 	size_t i;
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-		afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-		if (IS_ERR(afe->clocks[i])) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(afe_priv->clocks[i])) {
 			dev_err(afe->dev, "%s devm_clk_get %s fail\n",
 				__func__, aud_clks[i]);
-			return PTR_ERR(afe->clocks[i]);
+			return PTR_ERR(afe_priv->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
@@ -1217,7 +1084,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mt8173_afe *afe;
+	struct mtk_base_afe *afe;
+	struct mt8173_afe_private *afe_priv;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1228,6 +1096,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 	if (!afe)
 		return -ENOMEM;
 
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe_priv)
+		return -ENOMEM;
+
 	afe->dev = &pdev->dev;
 
 	irq_id = platform_get_irq(pdev, 0);
@@ -1259,8 +1133,30 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
+	/* memif % irq initialize*/
+	afe->memif_size = MT8173_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8173_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++) {
 		afe->memif[i].data = &memif_data[i];
+		afe->irqs[i].irq_data = &irq_data[i];
+		afe->irqs[i].irq_occupyed = true;
+		afe->memif[i].irq_usage = i;
+		afe->memif[i].const_irq = 1;
+	}
+
+	afe->mtk_afe_hardware = &mt8173_afe_hardware;
+	afe->memif_fs = mt8173_memif_fs;
+	afe->irq_fs = mt8173_irq_fs;
 
 	platform_set_drvdata(pdev, afe);
 
@@ -1271,7 +1167,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
+	afe->reg_back_up_list = mt8173_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+	afe->runtime_resume = mt8173_afe_runtime_resume;
+	afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
-- 
1.9.1



From garlic.tseng at mediatek.com  Fri Jun 17 00:43:57 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 17 Jun 2016 15:43:57 +0800
Subject: [alsa-devel] [PATCH v5 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <1466149440-23889-7-git-send-email-garlic.tseng@mediatek.com>

Add mt2701 platform driver implementation for playback and capture.
The implement follow DAPM structure (memory interface as FE and I2S
as BE).
Because of the hardware design, i2s out required to be enabled when
we need to enable i2s in. This patch includes the implementation.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
---
 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c | 1559 ++++++++++++++++++++++++++++
 1 file changed, 1559 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-pcm.c

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
new file mode 100644
index 0000000..deb5f82
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1559 @@
+/*
+ * Mediatek ALSA SoC AFE platform driver for 2701
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *             Ir Lian <ir.lian at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+
+#include "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS	0xff
+
+static const struct snd_pcm_hardware mt2701_afe_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED
+		| SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+		   | SNDRV_PCM_FMTBIT_S32_LE,
+	.period_bytes_min = 1024,
+	.period_bytes_max = 1024 * 256,
+	.periods_min = 4,
+	.periods_max = 1024,
+	.buffer_bytes_max = 1024 * 1024 * 16,
+	.fifo_size = 0,
+};
+
+struct mt2701_afe_rate {
+	unsigned int rate;
+	unsigned int regvalue;
+};
+
+static const struct mt2701_afe_rate mt2701_afe_i2s_rates[] = {
+	{ .rate = 8000, .regvalue = 0 },
+	{ .rate = 12000, .regvalue = 1 },
+	{ .rate = 16000, .regvalue = 2 },
+	{ .rate = 24000, .regvalue = 3 },
+	{ .rate = 32000, .regvalue = 4 },
+	{ .rate = 48000, .regvalue = 5 },
+	{ .rate = 96000, .regvalue = 6 },
+	{ .rate = 192000, .regvalue = 7 },
+	{ .rate = 384000, .regvalue = 8 },
+	{ .rate = 7350, .regvalue = 16 },
+	{ .rate = 11025, .regvalue = 17 },
+	{ .rate = 14700, .regvalue = 18 },
+	{ .rate = 22050, .regvalue = 19 },
+	{ .rate = 29400, .regvalue = 20 },
+	{ .rate = 44100, .regvalue = 21 },
+	{ .rate = 88200, .regvalue = 22 },
+	{ .rate = 176400, .regvalue = 23 },
+	{ .rate = 352800, .regvalue = 24 },
+};
+
+int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+	int val = num - MT2701_IO_I2S;
+
+	if (val < 0 || val >= MT2701_I2S_NUM) {
+		dev_err(afe->dev, "%s, num not available, num %d, val %d\n",
+			__func__, num, val);
+		return -EINVAL;
+	}
+	return val;
+}
+
+static int mt2701_afe_i2s_fs(unsigned int sample_rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mt2701_afe_i2s_rates); i++)
+		if (mt2701_afe_i2s_rates[i].rate == sample_rate)
+			return mt2701_afe_i2s_rates[i].regvalue;
+
+	return -EINVAL;
+}
+
+static int mt2701_afe_i2s_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+	int ret = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* enable mclk */
+	ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+	if (ret)
+		dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+			i2s_num);
+
+	return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+					struct snd_soc_dai *dai,
+					int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	int stream_dir = substream->stream;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert)	{
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	i2s_path->on[stream_dir]--;
+	if (i2s_path->on[stream_dir] < 0) {
+		dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+			 i2s_path->on[stream_dir], stream_dir);
+		i2s_path->on[stream_dir] = 0;
+	}
+	if (i2s_path->on[stream_dir])
+		return 0;
+
+	/* disable i2s */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   1 << i2s_data->i2s_pwn_shift);
+	return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+	if (i2s_num < 0)
+		return;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (i2s_path->occupied[substream->stream])
+		i2s_path->occupied[substream->stream] = 0;
+	else
+		goto I2S_UNSTART;
+
+	mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+	/* need to disable i2s-out path when disable i2s-in */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+	/* disable mclk */
+	clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+					  struct snd_soc_dai *dai,
+					  int dir_invert)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	const struct mt2701_i2s_data *i2s_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	int reg, fs, w_len = 1;
+	int stream_dir = substream->stream;
+	unsigned int mask = 0, val = 0;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+
+	if (dir_invert) {
+		if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+			stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+		else
+			stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+	}
+	i2s_data = i2s_path->i2s_data[stream_dir];
+
+	/* no need to enable if already done */
+	i2s_path->on[stream_dir]++;
+
+	if (i2s_path->on[stream_dir] != 1)
+		return 0;
+
+	fs = mt2701_afe_i2s_fs(runtime->rate);
+
+	if (i2s_path->div_bck_to_lrck == 32)
+		w_len = 0;
+	else if (i2s_path->div_bck_to_lrck == 64)
+		w_len = 1;
+	else
+		dev_warn(dai->dev, "%s() bad bit count %d\n", __func__,
+			 afe_priv->i2s_path[i2s_num].div_bck_to_lrck);
+
+	mask = ASYS_I2S_CON_FS |
+	       ASYS_I2S_CON_I2S_COUPLE_MODE | /* 0 */
+	       ASYS_I2S_CON_I2S_MODE |
+	       ASYS_I2S_CON_WIDE_MODE;
+
+	val = ASYS_I2S_CON_FS_SET(fs) |
+	      ASYS_I2S_CON_I2S_MODE |
+	      ASYS_I2S_CON_WIDE_MODE_SET(w_len);
+
+	if (stream_dir == SNDRV_PCM_STREAM_CAPTURE) {
+		mask |= ASYS_I2S_IN_PHASE_FIX;
+		val |= ASYS_I2S_IN_PHASE_FIX;
+	}
+
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = ASMO_TIMING_CON1;
+	else
+		reg = ASMI_TIMING_CON1;
+
+	regmap_update_bits(afe->regmap, reg,
+			   i2s_data->i2s_asrc_fs_mask
+			   << i2s_data->i2s_asrc_fs_shift,
+			   fs << i2s_data->i2s_asrc_fs_shift);
+
+	/* enable i2s */
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   1 << i2s_data->i2s_pwn_shift,
+			   0 << i2s_data->i2s_pwn_shift);
+
+	/* reset i2s hw status before enable */
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, ASYS_I2S_CON_RESET);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_RESET, 0);
+	udelay(1);
+	regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg,
+			   ASYS_I2S_CON_I2S_EN, ASYS_I2S_CON_I2S_EN);
+	return 0;
+}
+
+static int mt2701_afe_i2s_prepare(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	int clk_domain;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+	struct mt2701_i2s_path *i2s_path;
+	int mclk_rate;
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	i2s_path = &afe_priv->i2s_path[i2s_num];
+	mclk_rate = i2s_path->mclk_rate;
+
+	if (i2s_path->occupied[substream->stream])
+		return -EBUSY;
+	i2s_path->occupied[substream->stream] = 1;
+
+	if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+		clk_domain = 0;
+	} else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+		clk_domain = 1;
+	} else {
+		dev_err(dai->dev, "%s() bad mclk rate %d\n",
+			__func__, mclk_rate);
+		return -EINVAL;
+	}
+	mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	} else {
+		/* need to enable i2s-out path when enable i2s-in */
+		/* prepare for another direction "out" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 1);
+		/* prepare for "in" */
+		mt2701_i2s_path_prepare_enable(substream, dai, 0);
+	}
+
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+				     unsigned int freq, int dir)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	/* mclk */
+	if (dir == SND_SOC_CLOCK_IN) {
+		dev_warn(dai->dev,
+			 "%s() warning: mt2701 doesn't support mclk input\n",
+			__func__);
+		return -EINVAL;
+	}
+	afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
+				     int div)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	switch (div_id) {
+	case DIV_ID_MCLK_TO_BCK:
+		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
+		break;
+	case DIV_ID_BCK_TO_LRCK:
+		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int mt2701_afe_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+	if (i2s_num < 0)
+		return i2s_num;
+
+	afe_priv->i2s_path[i2s_num].format = fmt;
+	return 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif_tmp;
+
+	/* can't run single DL & DLM at the same time */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		memif_tmp = &afe->memif[MT2701_MEMIF_DLM];
+		if (memif_tmp->substream) {
+			dev_warn(afe->dev, "%s memif is not available, stream_dir %d, memif_num %d\n",
+				 __func__, stream_dir, memif_num);
+			return -EBUSY;
+		}
+	}
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static int mt2701_simple_fe_hw_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int stream_dir = substream->stream;
+
+	/* single DL use PAIR_INTERLEAVE */
+	if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK) {
+		regmap_update_bits(afe->regmap,
+				   AFE_MEMIF_PBUF_SIZE,
+				   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+				   AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE);
+	}
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_startup(struct snd_pcm_substream *substream,
+				 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp;
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_tmp = &afe->memif[i];
+		if (memif_tmp->substream)
+			return -EBUSY;
+	}
+
+	/* enable agent for all signal DL (due to hw design) */
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   0 << memif_data->agent_disable_shift);
+	}
+
+	return mtk_afe_fe_startup(substream, dai);
+}
+
+static void mt2701_dlm_fe_shutdown(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	const struct mtk_base_memif_data *memif_data;
+	int i;
+
+	for (i = MT2701_MEMIF_DL1; i < MT2701_MEMIF_DL_SINGLE_NUM; ++i) {
+		memif_data = afe->memif[i].data;
+		regmap_update_bits(afe->regmap,
+				   memif_data->agent_disable_reg,
+				   1 << memif_data->agent_disable_shift,
+				   1 << memif_data->agent_disable_shift);
+	}
+	return mtk_afe_fe_shutdown(substream, dai);
+}
+
+static int mt2701_dlm_fe_hw_params(struct snd_pcm_substream *substream,
+				   struct snd_pcm_hw_params *params,
+				   struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	int channels = params_channels(params);
+
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_MASK,
+			   AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_32BYTES);
+	regmap_update_bits(afe->regmap,
+			   AFE_MEMIF_PBUF_SIZE,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK,
+			   AFE_MEMIF_PBUF_SIZE_DLM_CH(channels));
+
+	return mtk_afe_fe_hw_params(substream, params, dai);
+}
+
+static int mt2701_dlm_fe_trigger(struct snd_pcm_substream *substream,
+				 int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif_tmp = &afe->memif[MT2701_MEMIF_DL1];
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift,
+				   1 << memif_tmp->data->enable_shift);
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_afe_fe_trigger(substream, cmd, dai);
+		regmap_update_bits(afe->regmap, memif_tmp->data->enable_reg,
+				   1 << memif_tmp->data->enable_shift, 0);
+
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt2701_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int fs;
+
+	if (rtd->cpu_dai->id != MT2701_MEMIF_ULBT)
+		fs = mt2701_afe_i2s_fs(rate);
+	else
+		fs = (rate == 16000 ? 1 : 0);
+	return fs;
+}
+
+static int mt2701_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+	return mt2701_afe_i2s_fs(rate);
+}
+
+/* FE DAIs */
+static const struct snd_soc_dai_ops mt2701_single_memif_dai_ops = {
+	.startup	= mt2701_simple_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mt2701_simple_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+
+};
+
+static const struct snd_soc_dai_ops mt2701_dlm_memif_dai_ops = {
+	.startup	= mt2701_dlm_fe_startup,
+	.shutdown	= mt2701_dlm_fe_shutdown,
+	.hw_params	= mt2701_dlm_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mt2701_dlm_fe_trigger,
+};
+
+/* I2S BE DAIs */
+static const struct snd_soc_dai_ops mt2701_afe_i2s_ops = {
+	.startup	= mt2701_afe_i2s_startup,
+	.shutdown	= mt2701_afe_i2s_shutdown,
+	.prepare	= mt2701_afe_i2s_prepare,
+	.set_sysclk	= mt2701_afe_i2s_set_sysclk,
+	.set_clkdiv	= mt2701_afe_i2s_set_clkdiv,
+	.set_fmt	= mt2701_afe_i2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
+	/* FE DAIs: memory intefaces to CPU */
+	{
+		.name = "PCM_multi",
+		.id = MT2701_MEMIF_DLM,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.playback = {
+			.stream_name = "DLM",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_dlm_memif_dai_ops,
+	},
+	{
+		.name = "PCM0",
+		.id = MT2701_MEMIF_UL1,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL1",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	{
+		.name = "PCM1",
+		.id = MT2701_MEMIF_UL2,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
+		.capture = {
+			.stream_name = "UL2",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_single_memif_dai_ops,
+	},
+	/* BE DAIs */
+	{
+		.name = "I2S0",
+		.id = MT2701_IO_I2S,
+		.playback = {
+			.stream_name = "I2S0 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.capture = {
+			.stream_name = "I2S0 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+
+		},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S1",
+		.id = MT2701_IO_2ND_I2S,
+		.playback = {
+			.stream_name = "I2S1 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S1 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S2",
+		.id = MT2701_IO_3RD_I2S,
+		.playback = {
+			.stream_name = "I2S2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "I2S3",
+		.id = MT2701_IO_4TH_I2S,
+		.playback = {
+			.stream_name = "I2S3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.capture = {
+			.stream_name = "I2S3 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE
+				| SNDRV_PCM_FMTBIT_S24_LE
+				| SNDRV_PCM_FMTBIT_S32_LE)
+			},
+		.ops = &mt2701_afe_i2s_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o00_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I00 Switch", AFE_CONN0, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o01_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I01 Switch", AFE_CONN1, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o02_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I02 Switch", AFE_CONN2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o03_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o14_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I26 Switch", AFE_CONN14, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o15_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I12 Switch", AFE_CONN15, 12, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o16_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I13 Switch", AFE_CONN16, 13, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o17_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I14 Switch", AFE_CONN17, 14, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o18_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I15 Switch", AFE_CONN18, 15, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o19_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I16 Switch", AFE_CONN19, 16, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o20_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN20, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o21_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN21, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o22_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I19 Switch", AFE_CONN22, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o23_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o31_mix[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("I35 Switch", AFE_CONN41, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_i02_mix[] = {
+	SOC_DAPM_SINGLE("I2S0 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S0 Out Switch",
+				    ASYS_I2SO1_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S1 Out Switch",
+				    ASYS_I2SO2_CON, 26, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S2 Out Switch",
+				    PWR2_TOP_CON, 17, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S3 Out Switch",
+				    PWR2_TOP_CON, 18, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S4 Out Switch",
+				    PWR2_TOP_CON, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1,
+				    1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+	SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1,
+				    1),
+};
+
+static const struct snd_soc_dapm_widget mt2701_afe_pcm_widgets[] = {
+	/* inter-connections */
+	SND_SOC_DAPM_MIXER("I00", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I01", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I02", SND_SOC_NOPM, 0, 0, mt2701_afe_i02_mix,
+			   ARRAY_SIZE(mt2701_afe_i02_mix)),
+	SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I12", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I13", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I14", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I15", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I16", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I18", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I19", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I26", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("I35", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("O00", SND_SOC_NOPM, 0, 0, mt2701_afe_o00_mix,
+			   ARRAY_SIZE(mt2701_afe_o00_mix)),
+	SND_SOC_DAPM_MIXER("O01", SND_SOC_NOPM, 0, 0, mt2701_afe_o01_mix,
+			   ARRAY_SIZE(mt2701_afe_o01_mix)),
+	SND_SOC_DAPM_MIXER("O02", SND_SOC_NOPM, 0, 0, mt2701_afe_o02_mix,
+			   ARRAY_SIZE(mt2701_afe_o02_mix)),
+	SND_SOC_DAPM_MIXER("O03", SND_SOC_NOPM, 0, 0, mt2701_afe_o03_mix,
+			   ARRAY_SIZE(mt2701_afe_o03_mix)),
+	SND_SOC_DAPM_MIXER("O14", SND_SOC_NOPM, 0, 0, mt2701_afe_o14_mix,
+			   ARRAY_SIZE(mt2701_afe_o14_mix)),
+	SND_SOC_DAPM_MIXER("O15", SND_SOC_NOPM, 0, 0, mt2701_afe_o15_mix,
+			   ARRAY_SIZE(mt2701_afe_o15_mix)),
+	SND_SOC_DAPM_MIXER("O16", SND_SOC_NOPM, 0, 0, mt2701_afe_o16_mix,
+			   ARRAY_SIZE(mt2701_afe_o16_mix)),
+	SND_SOC_DAPM_MIXER("O17", SND_SOC_NOPM, 0, 0, mt2701_afe_o17_mix,
+			   ARRAY_SIZE(mt2701_afe_o17_mix)),
+	SND_SOC_DAPM_MIXER("O18", SND_SOC_NOPM, 0, 0, mt2701_afe_o18_mix,
+			   ARRAY_SIZE(mt2701_afe_o18_mix)),
+	SND_SOC_DAPM_MIXER("O19", SND_SOC_NOPM, 0, 0, mt2701_afe_o19_mix,
+			   ARRAY_SIZE(mt2701_afe_o19_mix)),
+	SND_SOC_DAPM_MIXER("O20", SND_SOC_NOPM, 0, 0, mt2701_afe_o20_mix,
+			   ARRAY_SIZE(mt2701_afe_o20_mix)),
+	SND_SOC_DAPM_MIXER("O21", SND_SOC_NOPM, 0, 0, mt2701_afe_o21_mix,
+			   ARRAY_SIZE(mt2701_afe_o21_mix)),
+	SND_SOC_DAPM_MIXER("O22", SND_SOC_NOPM, 0, 0, mt2701_afe_o22_mix,
+			   ARRAY_SIZE(mt2701_afe_o22_mix)),
+	SND_SOC_DAPM_MIXER("O31", SND_SOC_NOPM, 0, 0, mt2701_afe_o31_mix,
+			   ARRAY_SIZE(mt2701_afe_o31_mix)),
+
+	SND_SOC_DAPM_MIXER("I12I13", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s0)),
+	SND_SOC_DAPM_MIXER("I14I15", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s1)),
+	SND_SOC_DAPM_MIXER("I16I17", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s2)),
+	SND_SOC_DAPM_MIXER("I18I19", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_i2s3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_i2s3)),
+
+	SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc0,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+	SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc1,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+	SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc2,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+	SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+			   mt2701_afe_multi_ch_out_asrc3,
+			   ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+static const struct snd_soc_dapm_route mt2701_afe_pcm_routes[] = {
+	{"I12", NULL, "DL1"},
+	{"I13", NULL, "DL1"},
+	{"I35", NULL, "DLBT"},
+
+	{"I2S0 Playback", NULL, "O15"},
+	{"I2S0 Playback", NULL, "O16"},
+
+	{"I2S1 Playback", NULL, "O17"},
+	{"I2S1 Playback", NULL, "O18"},
+	{"I2S2 Playback", NULL, "O19"},
+	{"I2S2 Playback", NULL, "O20"},
+	{"I2S3 Playback", NULL, "O21"},
+	{"I2S3 Playback", NULL, "O22"},
+	{"BT Playback", NULL, "O31"},
+
+	{"UL1", NULL, "O00"},
+	{"UL1", NULL, "O01"},
+	{"UL2", NULL, "O02"},
+	{"UL2", NULL, "O03"},
+	{"ULBT", NULL, "O14"},
+
+	{"I00", NULL, "I2S0 Capture"},
+	{"I01", NULL, "I2S0 Capture"},
+
+	{"I02", NULL, "I2S1 Capture"},
+	{"I03", NULL, "I2S1 Capture"},
+	/* I02,03 link to UL2, also need to open I2S0 */
+	{"I02", "I2S0 Switch", "I2S0 Capture"},
+
+	{"I26", NULL, "BT Capture"},
+
+	{"ASRC_O0", "Asrc0 out Switch", "DLM"},
+	{"ASRC_O1", "Asrc1 out Switch", "DLM"},
+	{"ASRC_O2", "Asrc2 out Switch", "DLM"},
+	{"ASRC_O3", "Asrc3 out Switch", "DLM"},
+
+	{"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"},
+	{"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"},
+	{"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"},
+	{"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"},
+
+	{ "I12", NULL, "I12I13" },
+	{ "I13", NULL, "I12I13" },
+	{ "I14", NULL, "I14I15" },
+	{ "I15", NULL, "I14I15" },
+	{ "I16", NULL, "I16I17" },
+	{ "I17", NULL, "I16I17" },
+	{ "I18", NULL, "I18I19" },
+	{ "I19", NULL, "I18I19" },
+
+	{ "O00", "I00 Switch", "I00" },
+	{ "O01", "I01 Switch", "I01" },
+	{ "O02", "I02 Switch", "I02" },
+	{ "O03", "I03 Switch", "I03" },
+	{ "O14", "I26 Switch", "I26" },
+	{ "O15", "I12 Switch", "I12" },
+	{ "O16", "I13 Switch", "I13" },
+	{ "O17", "I14 Switch", "I14" },
+	{ "O18", "I15 Switch", "I15" },
+	{ "O19", "I16 Switch", "I16" },
+	{ "O20", "I17 Switch", "I17" },
+	{ "O21", "I18 Switch", "I18" },
+	{ "O22", "I19 Switch", "I19" },
+	{ "O31", "I35 Switch", "I35" },
+
+};
+
+static const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+	.name = "mt2701-afe-pcm-dai",
+	.dapm_widgets = mt2701_afe_pcm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(mt2701_afe_pcm_widgets),
+	.dapm_routes = mt2701_afe_pcm_routes,
+	.num_dapm_routes = ARRAY_SIZE(mt2701_afe_pcm_routes),
+};
+
+static const struct mtk_base_memif_data memif_data[MT2701_MEMIF_NUM] = {
+	{
+		.name = "DL1",
+		.id = MT2701_MEMIF_DL1,
+		.reg_ofs_base = AFE_DL1_BASE,
+		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 16,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 1,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 6,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL2",
+		.id = MT2701_MEMIF_DL2,
+		.reg_ofs_base = AFE_DL2_BASE,
+		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 17,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 2,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 7,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL3",
+		.id = MT2701_MEMIF_DL3,
+		.reg_ofs_base = AFE_DL3_BASE,
+		.reg_ofs_cur = AFE_DL3_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 18,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 3,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 4,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 8,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL4",
+		.id = MT2701_MEMIF_DL4,
+		.reg_ofs_base = AFE_DL4_BASE,
+		.reg_ofs_cur = AFE_DL4_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 19,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 4,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 9,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DL5",
+		.id = MT2701_MEMIF_DL5,
+		.reg_ofs_base = AFE_DL5_BASE,
+		.reg_ofs_cur = AFE_DL5_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 20,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 20,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 5,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 10,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLM",
+		.id = MT2701_MEMIF_DLM,
+		.reg_ofs_base = AFE_DLMCH_BASE,
+		.reg_ofs_cur = AFE_DLMCH_CUR,
+		.fs_reg = AFE_DAC_CON1,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 7,
+		.hd_reg = AFE_MEMIF_PBUF_SIZE,
+		.hd_shift = 28,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 12,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL1",
+		.id = MT2701_MEMIF_UL1,
+		.reg_ofs_base = AFE_VUL_BASE,
+		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 0,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 0,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 10,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 0,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL2",
+		.id = MT2701_MEMIF_UL2,
+		.reg_ofs_base = AFE_UL2_BASE,
+		.reg_ofs_cur = AFE_UL2_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 5,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 2,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 11,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 2,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 1,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL3",
+		.id = MT2701_MEMIF_UL3,
+		.reg_ofs_base = AFE_UL3_BASE,
+		.reg_ofs_cur = AFE_UL3_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 4,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 12,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 0,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 2,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL4",
+		.id = MT2701_MEMIF_UL4,
+		.reg_ofs_base = AFE_UL4_BASE,
+		.reg_ofs_cur = AFE_UL4_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 15,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 6,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 13,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 6,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 3,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "UL5",
+		.id = MT2701_MEMIF_UL5,
+		.reg_ofs_base = AFE_UL5_BASE,
+		.reg_ofs_cur = AFE_UL5_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 20,
+		.mono_reg = AFE_DAC_CON4,
+		.mono_shift = 8,
+		.fs_maskbit = 0x1f,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 14,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 8,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 4,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "DLBT",
+		.id = MT2701_MEMIF_DLBT,
+		.reg_ofs_base = AFE_ARB1_BASE,
+		.reg_ofs_cur = AFE_ARB1_CUR,
+		.fs_reg = AFE_DAC_CON3,
+		.fs_shift = 10,
+		.fs_maskbit = 0x1f,
+		.mono_reg = AFE_DAC_CON3,
+		.mono_shift = 22,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 8,
+		.hd_reg = AFE_MEMIF_HD_CON0,
+		.hd_shift = 14,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 13,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+	{
+		.name = "ULBT",
+		.id = MT2701_MEMIF_ULBT,
+		.reg_ofs_base = AFE_DAI_BASE,
+		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON2,
+		.fs_shift = 30,
+		.fs_maskbit = 0x1,
+		.mono_reg = -1,
+		.mono_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
+		.enable_shift = 17,
+		.hd_reg = AFE_MEMIF_HD_CON1,
+		.hd_shift = 20,
+		.agent_disable_reg = AUDIO_TOP_CON5,
+		.agent_disable_shift = 16,
+		.msb_reg = -1,
+		.msb_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT2701_IRQ_ASYS_END] = {
+	{
+		.id = MT2701_IRQ_ASYS_IRQ1,
+		.irq_cnt_reg = ASYS_IRQ1_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ1_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ1_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 0,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ2,
+		.irq_cnt_reg = ASYS_IRQ2_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ2_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ2_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 1,
+	},
+	{
+		.id = MT2701_IRQ_ASYS_IRQ3,
+		.irq_cnt_reg = ASYS_IRQ3_CON,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0xffffff,
+		.irq_fs_reg = ASYS_IRQ3_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0x1f,
+		.irq_en_reg = ASYS_IRQ3_CON,
+		.irq_en_shift = 31,
+		.irq_clr_reg = ASYS_IRQ_CLR,
+		.irq_clr_shift = 2,
+	}
+};
+
+static const struct mt2701_i2s_data mt2701_i2s_data[MT2701_I2S_NUM][2] = {
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO1_CON,
+			.i2s_pwn_shift = 6,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN1_CON,
+			.i2s_pwn_shift = 0,
+			.i2s_asrc_fs_shift = 0,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO2_CON,
+			.i2s_pwn_shift = 7,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN2_CON,
+			.i2s_pwn_shift = 1,
+			.i2s_asrc_fs_shift = 5,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO3_CON,
+			.i2s_pwn_shift = 8,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN3_CON,
+			.i2s_pwn_shift = 2,
+			.i2s_asrc_fs_shift = 10,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+	{
+		{
+			.i2s_ctrl_reg = ASYS_I2SO4_CON,
+			.i2s_pwn_shift = 9,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+		{
+			.i2s_ctrl_reg = ASYS_I2SIN4_CON,
+			.i2s_pwn_shift = 3,
+			.i2s_asrc_fs_shift = 15,
+			.i2s_asrc_fs_mask = 0x1f,
+
+		},
+	},
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+	.max_register = AFE_END_ADDR,
+	.cache_type = REGCACHE_NONE,
+};
+
+static irqreturn_t mt2701_asys_isr(int irq_id, void *dev)
+{
+	int id;
+	struct mtk_base_afe *afe = dev;
+	struct mtk_base_afe_memif *memif;
+	struct mtk_base_afe_irq *irq;
+	u32 status;
+
+	regmap_read(afe->regmap, ASYS_IRQ_STATUS, &status);
+	regmap_write(afe->regmap, ASYS_IRQ_CLR, status);
+
+	for (id = 0; id < MT2701_MEMIF_NUM; ++id) {
+		memif = &afe->memif[id];
+		if (memif->irq_usage < 0)
+			continue;
+		irq = &afe->irqs[memif->irq_usage];
+		if (status & 1 << (irq->irq_data->irq_clr_shift))
+			snd_pcm_period_elapsed(memif->substream);
+	}
+	return IRQ_HANDLED;
+}
+
+static int mt2701_afe_runtime_suspend(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static int mt2701_afe_runtime_resume(struct device *dev)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+
+	return mt2701_afe_enable_clock(afe);
+}
+
+static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+	int ret, i;
+	unsigned int irq_id;
+	struct mtk_base_afe *afe;
+	struct mt2701_afe_private *afe_priv;
+	struct resource *res;
+	struct device *dev;
+
+	ret = 0;
+	afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe)
+		return -ENOMEM;
+
+	afe->dev = &pdev->dev;
+	dev = afe->dev;
+
+	irq_id = platform_get_irq(pdev, 0);
+	if (!irq_id) {
+		dev_err(dev, "%s no irq found\n", dev->of_node->name);
+		return -ENXIO;
+	}
+	ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
+			       IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+	if (ret) {
+		dev_err(dev, "could not request_irq for asys-isr\n");
+		return ret;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	afe->base_addr = devm_ioremap_resource(&pdev->dev, res);
+
+	if (IS_ERR(afe->base_addr))
+		return PTR_ERR(afe->base_addr);
+
+	afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+		&mt2701_afe_regmap_config);
+	if (IS_ERR(afe->regmap))
+		return PTR_ERR(afe->regmap);
+
+	mutex_init(&afe->irq_alloc_lock);
+
+	/* memif initialize */
+	afe->memif_size = MT2701_MEMIF_NUM;
+	afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+				  GFP_KERNEL);
+
+	if (!afe->memif)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->memif_size; i++) {
+		afe->memif[i].data = &memif_data[i];
+		afe->memif[i].irq_usage = -1;
+	}
+
+	/* irq initialize */
+	afe->irqs_size = MT2701_IRQ_ASYS_END;
+	afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+				 GFP_KERNEL);
+
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++)
+		afe->irqs[i].irq_data = &irq_data[i];
+
+	/* I2S initialize */
+	for (i = 0; i < MT2701_I2S_NUM; i++) {
+		afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+			= &mt2701_i2s_data[i][I2S_OUT];
+		afe_priv->i2s_path[i].i2s_data[I2S_IN]
+			= &mt2701_i2s_data[i][I2S_IN];
+	}
+
+	afe->mtk_afe_hardware = &mt2701_afe_hardware;
+	afe->memif_fs = mt2701_memif_fs;
+	afe->irq_fs = mt2701_irq_fs;
+
+	afe->reg_back_up_list = mt2701_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt2701_afe_backup_list);
+	afe->runtime_resume = mt2701_afe_runtime_resume;
+	afe->runtime_suspend = mt2701_afe_runtime_suspend;
+
+	/* initial audio related clock */
+	ret = mt2701_init_clock(afe);
+	if (ret) {
+		dev_err(dev, "init clock error\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, afe);
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev))
+		goto err_pm_disable;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+	if (ret) {
+		dev_warn(dev, "err_platform\n");
+		goto err_platform;
+	}
+
+	ret = snd_soc_register_component(&pdev->dev,
+					 &mt2701_afe_pcm_dai_component,
+					 mt2701_afe_pcm_dais,
+					 ARRAY_SIZE(mt2701_afe_pcm_dais));
+	if (ret) {
+		dev_warn(dev, "err_dai_component\n");
+		goto err_dai_component;
+	}
+
+	mt2701_afe_runtime_resume(&pdev->dev);
+
+	return 0;
+
+err_dai_component:
+	snd_soc_unregister_component(&pdev->dev);
+
+err_platform:
+	snd_soc_unregister_platform(&pdev->dev);
+
+err_pm_disable:
+	pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int mt2701_afe_pcm_dev_remove(struct platform_device *pdev)
+{
+	struct mtk_base_afe *afe = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	if (!pm_runtime_status_suspended(&pdev->dev))
+		mt2701_afe_runtime_suspend(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+	snd_soc_unregister_platform(&pdev->dev);
+	/* disable afe clock */
+	mt2701_afe_disable_clock(afe);
+	return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+	{ .compatible = "mediatek,mt2701-audio", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+	SET_RUNTIME_PM_OPS(mt2701_afe_runtime_suspend,
+			   mt2701_afe_runtime_resume, NULL)
+};
+
+static struct platform_driver mt2701_afe_pcm_driver = {
+	.driver = {
+		   .name = "mt2701-audio",
+		   .of_match_table = mt2701_afe_pcm_dt_match,
+#ifdef CONFIG_PM
+		   .pm = &mt2701_afe_pm_ops,
+#endif
+	},
+	.probe = mt2701_afe_pcm_dev_probe,
+	.remove = mt2701_afe_pcm_dev_remove,
+};
+
+module_platform_driver(mt2701_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver for 2701");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
-- 
1.9.1



From chris at chris-wilson.co.uk  Fri Jun 17 01:25:21 2016
From: chris at chris-wilson.co.uk (Chris Wilson)
Date: Fri, 17 Jun 2016 09:25:21 +0100
Subject: [PATCH 5/7] drm/mediatek: Remove redundant calls to
 drm_connector_register_all()
In-Reply-To: <1466151923-1572-1-git-send-email-chris@chris-wilson.co.uk>
References: <1466151923-1572-1-git-send-email-chris@chris-wilson.co.uk>
Message-ID: <1466151923-1572-6-git-send-email-chris@chris-wilson.co.uk>

Up to now, the recommendation was for drivers to call drm_dev_register()
followed by drm_connector_register_all(). Now that
drm_connector_register() is safe against multiple invocations, we can
move drm_connector_register_all() to drm_dev_register() and not suffer
from any backwards compatibility issues with drivers not following the
more rigorous init ordering.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
Cc: Daniel Vetter <daniel.vetter at ffwll.ch>
Cc: Matthias Brugger <matthias.bgg at gmail.com>
Cc: David Airlie <airlied at linux.ie>
Cc: dri-devel at lists.freedesktop.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-mediatek at lists.infradead.org
---
 drivers/gpu/drm/mediatek/mtk_drm_drv.c | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
index c33bf98c5d5e..7ab91f4a200f 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@ -293,14 +293,8 @@ static int mtk_drm_bind(struct device *dev)
 	if (ret < 0)
 		goto err_deinit;
 
-	ret = drm_connector_register_all(drm);
-	if (ret < 0)
-		goto err_unregister;
-
 	return 0;
 
-err_unregister:
-	drm_dev_unregister(drm);
 err_deinit:
 	mtk_drm_kms_deinit(drm);
 err_free:
@@ -455,7 +449,6 @@ static int mtk_drm_remove(struct platform_device *pdev)
 	struct drm_device *drm = private->drm;
 	int i;
 
-	drm_connector_unregister_all(drm);
 	drm_dev_unregister(drm);
 	mtk_drm_kms_deinit(drm);
 	drm_dev_unref(drm);
-- 
2.8.1



From k.kozlowski at samsung.com  Fri Jun 17 01:36:59 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Fri, 17 Jun 2016 10:36:59 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <5763B6AB.3010201@samsung.com>

On 06/16/2016 11:27 PM, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  arch/arc/kernel/time.c                    |  6 +++---
>  arch/arm/kernel/smp_twd.c                 |  6 +++---
>  arch/microblaze/kernel/timer.c            |  2 +-
>  arch/mips/ralink/cevt-rt3352.c            |  2 +-
>  arch/nios2/kernel/time.c                  |  2 +-
>  drivers/clocksource/arm_arch_timer.c      |  6 +++---
>  drivers/clocksource/arm_global_timer.c    |  2 +-
>  drivers/clocksource/armv7m_systick.c      |  2 +-
>  drivers/clocksource/asm9260_timer.c       |  2 +-
>  drivers/clocksource/bcm2835_timer.c       |  2 +-
>  drivers/clocksource/bcm_kona_timer.c      |  4 ++--
>  drivers/clocksource/cadence_ttc_timer.c   |  2 +-
>  drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
>  drivers/clocksource/clksrc-probe.c        | 14 --------------
>  drivers/clocksource/clksrc_st_lpc.c       |  2 +-
>  drivers/clocksource/clps711x-timer.c      |  2 +-
>  drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
>  drivers/clocksource/exynos_mct.c          |  4 ++--
>  drivers/clocksource/fsl_ftm_timer.c       |  2 +-
>  drivers/clocksource/h8300_timer16.c       |  2 +-
>  drivers/clocksource/h8300_timer8.c        |  2 +-
>  drivers/clocksource/h8300_tpu.c           |  2 +-
>  drivers/clocksource/meson6_timer.c        |  2 +-
>  drivers/clocksource/mips-gic-timer.c      |  2 +-
>  drivers/clocksource/moxart_timer.c        |  2 +-
>  drivers/clocksource/mps2-timer.c          |  2 +-
>  drivers/clocksource/mtk_timer.c           |  2 +-
>  drivers/clocksource/mxs_timer.c           |  2 +-
>  drivers/clocksource/nomadik-mtu.c         |  2 +-
>  drivers/clocksource/pxa_timer.c           |  2 +-
>  drivers/clocksource/qcom-timer.c          |  4 ++--
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----
>  drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----

For exynos_mct and samsung_pwm_timer:
Acked-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

Best regards,
Krzysztof



From hs.liao at mediatek.com  Fri Jun 17 01:28:27 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Fri, 17 Jun 2016 16:28:27 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1465906063.20796.20.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41>
Message-ID: <1466152107.11184.14.camel@mtksdaap41>

Hi Matthias,

On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
> Hi Matthias,
> 
> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
> > 
> > On 14/06/16 09:44, Horng-Shyang Liao wrote:
> > > Hi Matthias,
> > >
> > > On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
> > >>
> > >> On 08/06/16 14:25, Horng-Shyang Liao wrote:
> > >>> Hi Matthias,
> > >>>
> > >>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> > >>>>
> > >>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> > >>>>> Hi Matthias,
> > >>>>>
> > >>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> > >>>>>>
> > >>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
> > >>>>>>>
> > >>>>>>>
> > >>>>>> [...]
> > >>>>>>
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> > >>>>>>>>>>>>>>>> +        }
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> > >>>>>>>>>>>>>>>> task->command_size,
> > >>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> > >>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
> > >>>>>>>>>>>>>>>> +    }
> > >>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> > >>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> > >>>>>>>>>>>>>>>> +}
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> > >>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> > >>>>>>>>>>>>>>>> +{
> > >>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> > >>>>>>>>>>>>>>>> +    u32 curr_pa;
> > >>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> > >>>>>>>>>>>>>>>> +    bool err;
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> > >>>>>>>>>>>>>>>> +        err = true;
> > >>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> > >>>>>>>>>>>>>>>> +        err = false;
> > >>>>>>>>>>>>>>>> +    else
> > >>>>>>>>>>>>>>>> +        return;
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> > >>>>>>>>>>>>>>>> +
> > >>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> > >>>>>>>>>>>>>>>> +                 list_entry) {
> > >>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> > >>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> > >>>>>>>>>>>>>>>
> > >>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> > >>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
> > >>>>>>>>>>>>>>> commands in the
> > >>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> > >>>>>>>>>>>>>>> guarantees
> > >>>>>>>>>>>>>>> about dma_handle?
> > >>>>>>>>>>>>>>
> > >>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> > >>>>>>>>>>>>>> 2. Yes.
> > >>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> > >>>>>>>>>>>>>>
> > >>>>>>>>>>>>>
> > >>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> > >>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> > >>>>>>>>>>>>> calling
> > >>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> > >>>>>>>>>>>>> use this
> > >>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
> > >>>>>>>>>>>>> currently
> > >>>>>>>>>>>>> executing. So I think this mecanism is not working.
> > >>>>>>>>>>>>
> > >>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
> > >>>>>>>>>>>>       From CPU's point of view, tasks are linked by the busy list.
> > >>>>>>>>>>>>       From GCE's point of view, tasks are linked by the JUMP command.
> > >>>>>>>>>>>>
> > >>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
> > >>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> > >>>>>>>>>>>>
> > >>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
> > >>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> > >>>>>>>>>>>> when it is running ISR.
> > >>>>>>>>>>>>
> > >>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> > >>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
> > >>>>>>>>>>>>
> > >>>>>>>>>>>
> > >>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> > >>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> > >>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> > >>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
> > >>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> > >>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> > >>>>>>>>>>> we need to do it in a thread context.
> > >>>>>>>>>>
> > >>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
> > >>>>>>>>>> but display needs CMDQ to return callback function very precisely,
> > >>>>>>>>>> else display will drop frame.
> > >>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> > >>>>>>>>>> and CMDQ needs to call callback function in ISR.
> > >>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
> > >>>>>>>>>> 32 ms.sometimes.
> > >>>>>>>>>>
> > >>>>>>>>>
> > >>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
> > >>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
> > >>>>>>>>> you need the workqueue to be ordered. Can you please explain.
> > >>>>>>>>
> > >>>>>>>> The order should be kept.
> > >>>>>>>> Let me use mouse cursor as an example.
> > >>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> > >>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
> > >>>>>>>> If the order is not kept, the result could become A -> C -> B.
> > >>>>>>>>
> > >>>>>>>
> > >>>>>>> Got it, thanks for the clarification.
> > >>>>>>>
> > >>>>>>
> > >>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
> > >>>>>> programmed to the time a timeout in the first task in the busy list
> > >>>>>> would happen. Everytime we update the busy list (e.g. because of task
> > >>>>>> got finished by the thread), we update the timer. When the timer
> > >>>>>> triggers, which hopefully won't happen too often, we return timeout on
> > >>>>>> the busy list elements, until the time is lower then the actual time.
> > >>>>>>
> > >>>>>> At least with this we can reduce the data structures in this driver and
> > >>>>>> make it more lightweight.
> > >>>>>
> > >>>>>    From my understanding, your proposed method can handle timeout case.
> > >>>>>
> > >>>>> However, the workqueue is also in charge of releasing tasks.
> > >>>>> Do you take releasing tasks into consideration by using the proposed
> > >>>>> timer method?
> > >>>>> Furthermore, I think the code will become more complex if we also use
> > >>>>> timer to implement releasing tasks.
> > >>>>>
> > >>>>
> > >>>> Can't we call
> > >>>>            clk_disable_unprepare(cmdq->clock);
> > >>>>            cmdq_task_release(task);
> > >>>> after invoking the callback?

After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
another BUG.

(Quote some Linux 4.7 source code.)

 605 void clk_unprepare(struct clk *clk)
 606 {
 607         if (IS_ERR_OR_NULL(clk))
 608                 return;
 609
 610         clk_prepare_lock();                      // <-- Here
 611         clk_core_unprepare(clk->core);
 612         clk_prepare_unlock();
 613 }
 614 EXPORT_SYMBOL_GPL(clk_unprepare);

  91 static void clk_prepare_lock(void)
  92 {
  93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
  94                 if (prepare_owner == current) {
  95                         prepare_refcnt++;
  96                         return;
  97                 }
  98                 mutex_lock(&prepare_lock);
  99         }
 100         WARN_ON_ONCE(prepare_owner != NULL);
 101         WARN_ON_ONCE(prepare_refcnt != 0);
 102         prepare_owner = current;
 103         prepare_refcnt = 1;
 104 }

So, 'unprepare' can sleep and cannot be put into ISR.
I also try to put it into a timer, but the error is the same
since timer callback is executed by softirq.

We need clk_disable_unprepare() since it can save power consumption
in idle.
Therefore, I plan to
(1) move releasing buffer and task into ISR,
(2) move timeout into timer, and
(3) keep workqueue for clk_disable_unprepare().

What do you think?

Thanks,
HS

> > >>>
> > >>> Do you mean just call these two functions in ISR?
> > >>> My major concern is dma_free_coherent() and kfree() in
> > >>> cmdq_task_release(task).
> > >>
> > >> Why do we need the dma calls at all? Can't we just calculate the
> > >> physical address using __pa(x)?
> > >
> > > I prefer to use dma_map_single/dma_unmap_single.
> > >
> > 
> > Can you please elaborate why you need this. We don't do dma, so we 
> > should not use dma memory for this.
> 
> We need a buffer to share between CPU and GCE, so we do need DMA.
> CPU is in charge of writing GCE commands into this buffer.
> GCE is in charge of reading and running GCE commands from this buffer.
> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
> 
> However, if we want to use timer to handle timeout, we need to release
> memory in ISR.
> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
> instead of dma_alloc_coherent/dma_free_coherent is an alternative
> solution, but taking care the synchronization between cache and memory
> is the expected overhead.
> 
> > >>> Therefore, your suggestion is to use GFP_ATOMIC for both
> > >>> dma_alloc_coherent() and kzalloc(). Right?
> > >>
> > >> I don't think we need GFP_ATOMIC, the critical path will just free the
> > >> memory.
> > >
> > > I tested these two functions, and kfree was safe.
> > > However, dma_free_coherent raised BUG.
> > > BUG: failure at
> > > /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
> > 
> > Just a general hint. Please try to evaluate on a recent kernel. It looks 
> > like as if you tried this on a v3.18 based one.
> 
> This driver should be backward compatible to v3.18 for a MTK project.
> 
> > Best regards,
> > Matthias
> 
> Thanks,
> HS
> 
> > > 1512 void vunmap(const void *addr)
> > > 1513 {
> > > 1514         BUG_ON(in_interrupt());		// <-- here
> > > 1515         might_sleep();
> > > 1516         if (addr)
> > > 1517                 __vunmap(addr, 0);
> > > 1518 }
> > > 1519 EXPORT_SYMBOL(vunmap);
> > >
> > > Therefore, I plan to use kmalloc + dma_map_single instead of
> > > dma_alloc_coherent, and dma_unmap_single + kfree instead of
> > > dma_free_coherent.
> > >
> > > What do you think about the function replacement?
> > >
> > >>> If so, I can try to implement timeout by timer, and discuss with you
> > >>> if I have further questions.
> > >>>
> > >>
> > >> Sounds good :)
> > >>
> > >> Thanks,
> > >> Matthias
> > >
> > > Thanks,
> > > HS
> > >
> > >>>> Regrading the clock, wouldn't it be easier to handle the clock
> > >>>> enable/disable depending on the state of task_busy_list? I suppose we
> > >>>> can't as we would need to check the task_busy_list of all threads, right?
> > >>>>
> > >>>> Regards,
> > >>>> Matthias
> > >>>
> > >>> Thanks,
> > >>> HS
> > >>>
> > >
> > >
> 
> 




From matthias.bgg at gmail.com  Fri Jun 17 08:57:53 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 17 Jun 2016 17:57:53 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466152107.11184.14.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41> <1466152107.11184.14.camel@mtksdaap41>
Message-ID: <57641E01.3070205@gmail.com>



On 17/06/16 10:28, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
>> Hi Matthias,
>>
>> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
>>>
>>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
>>>> Hi Matthias,
>>>>
>>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
>>>>>
>>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
>>>>>> Hi Matthias,
>>>>>>
>>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>>>>>>
>>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>>>>>>> Hi Matthias,
>>>>>>>>
>>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>>>>>>
>>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> [...]
>>>>>>>>>
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>>>>>>> calling
>>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>>>>>>> use this
>>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>>>>>>> currently
>>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>>>>>>        From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>>>>>>        From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>>>>>>> when it is running ISR.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>>>>>>> we need to do it in a thread context.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>>>>>>> else display will drop frame.
>>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>>>>>>> 32 ms.sometimes.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>>>>>>
>>>>>>>>>>> The order should be kept.
>>>>>>>>>>> Let me use mouse cursor as an example.
>>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Got it, thanks for the clarification.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>>>>>>> programmed to the time a timeout in the first task in the busy list
>>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>>>>>>> got finished by the thread), we update the timer. When the timer
>>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>>>>>>> the busy list elements, until the time is lower then the actual time.
>>>>>>>>>
>>>>>>>>> At least with this we can reduce the data structures in this driver and
>>>>>>>>> make it more lightweight.
>>>>>>>>
>>>>>>>>     From my understanding, your proposed method can handle timeout case.
>>>>>>>>
>>>>>>>> However, the workqueue is also in charge of releasing tasks.
>>>>>>>> Do you take releasing tasks into consideration by using the proposed
>>>>>>>> timer method?
>>>>>>>> Furthermore, I think the code will become more complex if we also use
>>>>>>>> timer to implement releasing tasks.
>>>>>>>>
>>>>>>>
>>>>>>> Can't we call
>>>>>>>             clk_disable_unprepare(cmdq->clock);
>>>>>>>             cmdq_task_release(task);
>>>>>>> after invoking the callback?
>
> After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
> another BUG.
>
> (Quote some Linux 4.7 source code.)
>
>   605 void clk_unprepare(struct clk *clk)
>   606 {
>   607         if (IS_ERR_OR_NULL(clk))
>   608                 return;
>   609
>   610         clk_prepare_lock();                      // <-- Here
>   611         clk_core_unprepare(clk->core);
>   612         clk_prepare_unlock();
>   613 }
>   614 EXPORT_SYMBOL_GPL(clk_unprepare);
>
>    91 static void clk_prepare_lock(void)
>    92 {
>    93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
>    94                 if (prepare_owner == current) {
>    95                         prepare_refcnt++;
>    96                         return;
>    97                 }
>    98                 mutex_lock(&prepare_lock);
>    99         }
>   100         WARN_ON_ONCE(prepare_owner != NULL);
>   101         WARN_ON_ONCE(prepare_refcnt != 0);
>   102         prepare_owner = current;
>   103         prepare_refcnt = 1;
>   104 }
>
> So, 'unprepare' can sleep and cannot be put into ISR.
> I also try to put it into a timer, but the error is the same
> since timer callback is executed by softirq.
>
> We need clk_disable_unprepare() since it can save power consumption
> in idle.

We can call clk_prepare in probe and then use clk_enable/clk_disable, 
which don't sleep.

Regards,
Matthias

> Therefore, I plan to
> (1) move releasing buffer and task into ISR,
> (2) move timeout into timer, and
> (3) keep workqueue for clk_disable_unprepare().
>
> What do you think?
>
> Thanks,
> HS
>
>>>>>>
>>>>>> Do you mean just call these two functions in ISR?
>>>>>> My major concern is dma_free_coherent() and kfree() in
>>>>>> cmdq_task_release(task).
>>>>>
>>>>> Why do we need the dma calls at all? Can't we just calculate the
>>>>> physical address using __pa(x)?
>>>>
>>>> I prefer to use dma_map_single/dma_unmap_single.
>>>>
>>>
>>> Can you please elaborate why you need this. We don't do dma, so we
>>> should not use dma memory for this.
>>
>> We need a buffer to share between CPU and GCE, so we do need DMA.
>> CPU is in charge of writing GCE commands into this buffer.
>> GCE is in charge of reading and running GCE commands from this buffer.
>> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
>> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
>>
>> However, if we want to use timer to handle timeout, we need to release
>> memory in ISR.
>> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
>> instead of dma_alloc_coherent/dma_free_coherent is an alternative
>> solution, but taking care the synchronization between cache and memory
>> is the expected overhead.
>>
>>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
>>>>>> dma_alloc_coherent() and kzalloc(). Right?
>>>>>
>>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
>>>>> memory.
>>>>
>>>> I tested these two functions, and kfree was safe.
>>>> However, dma_free_coherent raised BUG.
>>>> BUG: failure at
>>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
>>>
>>> Just a general hint. Please try to evaluate on a recent kernel. It looks
>>> like as if you tried this on a v3.18 based one.
>>
>> This driver should be backward compatible to v3.18 for a MTK project.
>>
>>> Best regards,
>>> Matthias
>>
>> Thanks,
>> HS
>>
>>>> 1512 void vunmap(const void *addr)
>>>> 1513 {
>>>> 1514         BUG_ON(in_interrupt());		// <-- here
>>>> 1515         might_sleep();
>>>> 1516         if (addr)
>>>> 1517                 __vunmap(addr, 0);
>>>> 1518 }
>>>> 1519 EXPORT_SYMBOL(vunmap);
>>>>
>>>> Therefore, I plan to use kmalloc + dma_map_single instead of
>>>> dma_alloc_coherent, and dma_unmap_single + kfree instead of
>>>> dma_free_coherent.
>>>>
>>>> What do you think about the function replacement?
>>>>
>>>>>> If so, I can try to implement timeout by timer, and discuss with you
>>>>>> if I have further questions.
>>>>>>
>>>>>
>>>>> Sounds good :)
>>>>>
>>>>> Thanks,
>>>>> Matthias
>>>>
>>>> Thanks,
>>>> HS
>>>>
>>>>>>> Regrading the clock, wouldn't it be easier to handle the clock
>>>>>>> enable/disable depending on the state of task_busy_list? I suppose we
>>>>>>> can't as we would need to check the task_busy_list of all threads, right?
>>>>>>>
>>>>>>> Regards,
>>>>>>> Matthias
>>>>>>
>>>>>> Thanks,
>>>>>> HS
>>>>>>
>>>>
>>>>
>>
>>
>
>


From santosh.shilimkar at oracle.com  Fri Jun 17 09:04:47 2016
From: santosh.shilimkar at oracle.com (Santosh Shilimkar)
Date: Fri, 17 Jun 2016 09:04:47 -0700
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <afeed273-b0b9-eef6-5e9b-78f069ebe743@oracle.com>

On 6/16/2016 2:27 PM, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
>
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---

>  drivers/clocksource/timer-keystone.c      |  2 +-

Acked-by: Santosh Shilimkar <ssantosh at kernel.org>


From matthias.bgg at gmail.com  Fri Jun 17 09:14:22 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 17 Jun 2016 18:14:22 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1465906063.20796.20.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41>
Message-ID: <576421DE.7080203@gmail.com>



On 14/06/16 14:07, Horng-Shyang Liao wrote:
> Hi Matthias,
>
> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
>>
>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
>>> Hi Matthias,
>>>
>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
>>>>
>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
>>>>> Hi Matthias,
>>>>>
>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>>>>>
>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>>>>>> Hi Matthias,
>>>>>>>
>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>>>>>
>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>>>>>
>>>>>>>>>
>>>>>>>> [...]
>>>>>>>>
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>>>>>> calling
>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>>>>>> use this
>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>>>>>> currently
>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>>>>>        From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>>>>>        From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>>>>>> when it is running ISR.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>>>>>> we need to do it in a thread context.
>>>>>>>>>>>>
>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>>>>>> else display will drop frame.
>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>>>>>> 32 ms.sometimes.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>>>>>
>>>>>>>>>> The order should be kept.
>>>>>>>>>> Let me use mouse cursor as an example.
>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Got it, thanks for the clarification.
>>>>>>>>>
>>>>>>>>
>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>>>>>> programmed to the time a timeout in the first task in the busy list
>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>>>>>> got finished by the thread), we update the timer. When the timer
>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>>>>>> the busy list elements, until the time is lower then the actual time.
>>>>>>>>
>>>>>>>> At least with this we can reduce the data structures in this driver and
>>>>>>>> make it more lightweight.
>>>>>>>
>>>>>>>     From my understanding, your proposed method can handle timeout case.
>>>>>>>
>>>>>>> However, the workqueue is also in charge of releasing tasks.
>>>>>>> Do you take releasing tasks into consideration by using the proposed
>>>>>>> timer method?
>>>>>>> Furthermore, I think the code will become more complex if we also use
>>>>>>> timer to implement releasing tasks.
>>>>>>>
>>>>>>
>>>>>> Can't we call
>>>>>>             clk_disable_unprepare(cmdq->clock);
>>>>>>             cmdq_task_release(task);
>>>>>> after invoking the callback?
>>>>>
>>>>> Do you mean just call these two functions in ISR?
>>>>> My major concern is dma_free_coherent() and kfree() in
>>>>> cmdq_task_release(task).
>>>>
>>>> Why do we need the dma calls at all? Can't we just calculate the
>>>> physical address using __pa(x)?
>>>
>>> I prefer to use dma_map_single/dma_unmap_single.
>>>
>>
>> Can you please elaborate why you need this. We don't do dma, so we
>> should not use dma memory for this.
>
> We need a buffer to share between CPU and GCE, so we do need DMA.
> CPU is in charge of writing GCE commands into this buffer.
> GCE is in charge of reading and running GCE commands from this buffer.
> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
>
> However, if we want to use timer to handle timeout, we need to release
> memory in ISR.
> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
> instead of dma_alloc_coherent/dma_free_coherent is an alternative
> solution, but taking care the synchronization between cache and memory
> is the expected overhead.
>
>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
>>>>> dma_alloc_coherent() and kzalloc(). Right?
>>>>
>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
>>>> memory.
>>>
>>> I tested these two functions, and kfree was safe.
>>> However, dma_free_coherent raised BUG.
>>> BUG: failure at
>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
>>
>> Just a general hint. Please try to evaluate on a recent kernel. It looks
>> like as if you tried this on a v3.18 based one.
>
> This driver should be backward compatible to v3.18 for a MTK project.
>

That is something the Linux community can't use as argument for design 
decisions. If the backporting get's cumbersome, I propose to tell your 
boss, giving him the hint, that if the driver would have been in 
mainline earlier, this would all be easier ;)
No, seriously, that's why it makes a lot of sense for companies like 
Mediatek to have their driver mainlined. A switch to a new kernel 
version comes for free for all the mainlined drivers. Knowing the source 
code of the Mediatek kernel, I suppose doing a kernel version switch 
takes a good bunch of time and nerves. :)

Best regards,
Matthias


From matthias.bgg at gmail.com  Fri Jun 17 09:16:02 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 17 Jun 2016 18:16:02 +0200
Subject: [PATCH V2 04/63] clocksource/drivers/mkt_timer: Convert init
 function to return error
In-Reply-To: <1466112442-31105-5-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-5-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <57642242.2050607@gmail.com>



On 16/06/16 23:26, Daniel Lezcano wrote:
> The init functions do not return any error. They behave as the following:
>
>   - panic, thus leading to a kernel crash while another timer may work and
>     make the system boot up correctly
>
>   or
>
>   - print an error and let the caller unaware if the state of the system
>
> Change that by converting the init functions to return an error conforming
> to the CLOCKSOURCE_OF_RET prototype.
>
> Proper error handling (rollback, errno value) will be changed later case
> by case, thus this change just return back an error or success in the init
> function.
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>   drivers/clocksource/mtk_timer.c | 10 ++++++----
>   1 file changed, 6 insertions(+), 4 deletions(-)
>

Acked-by: Matthias Brugger <matthias.bgg at gmail.com>

> diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
> index 7e583f8..432a2c0 100644
> --- a/drivers/clocksource/mtk_timer.c
> +++ b/drivers/clocksource/mtk_timer.c
> @@ -181,7 +181,7 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
>   			evt->gpt_base + GPT_IRQ_EN_REG);
>   }
>
> -static void __init mtk_timer_init(struct device_node *node)
> +static int __init mtk_timer_init(struct device_node *node)
>   {
>   	struct mtk_clock_event_device *evt;
>   	struct resource res;
> @@ -190,7 +190,7 @@ static void __init mtk_timer_init(struct device_node *node)
>
>   	evt = kzalloc(sizeof(*evt), GFP_KERNEL);
>   	if (!evt)
> -		return;
> +		return -ENOMEM;
>
>   	evt->dev.name = "mtk_tick";
>   	evt->dev.rating = 300;
> @@ -248,7 +248,7 @@ static void __init mtk_timer_init(struct device_node *node)
>
>   	mtk_timer_enable_irq(evt, GPT_CLK_EVT);
>
> -	return;
> +	return 0;
>
>   err_clk_disable:
>   	clk_disable_unprepare(clk);
> @@ -262,5 +262,7 @@ err_mem:
>   	release_mem_region(res.start, resource_size(&res));
>   err_kzalloc:
>   	kfree(evt);
> +
> +	return -EINVAL;
>   }
> -CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
> +CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
>


From matthias.bgg at gmail.com  Fri Jun 17 09:18:12 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Fri, 17 Jun 2016 18:18:12 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <576422C4.2000104@gmail.com>



On 16/06/16 23:27, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
>
> Let's convert back the names:
>   - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>   - clksrc-of-ret              => clksrc-of
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>   arch/arc/kernel/time.c                    |  6 +++---

[...]

>   drivers/clocksource/mtk_timer.c           |  2 +-

For mediatek driver:
Acked-by: Matthias Brugger <matthias.bgg at gmail.com>


From weiyj_lk at 163.com  Fri Jun 17 10:21:16 2016
From: weiyj_lk at 163.com (weiyj_lk at 163.com)
Date: Fri, 17 Jun 2016 17:21:16 +0000
Subject: [PATCH -next] mtd: mtk-nor: remove duplicated include from
 mtk-quadspi.c
Message-ID: <1466184076-11714-1-git-send-email-weiyj_lk@163.com>

From: Wei Yongjun <yongjun_wei at trendmicro.com.cn>

Remove duplicated include.

Signed-off-by: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
---
 drivers/mtd/spi-nor/mtk-quadspi.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/mtd/spi-nor/mtk-quadspi.c b/drivers/mtd/spi-nor/mtk-quadspi.c
index 21c31b3..e661877 100644
--- a/drivers/mtd/spi-nor/mtk-quadspi.c
+++ b/drivers/mtd/spi-nor/mtk-quadspi.c
@@ -21,7 +21,6 @@
 #include <linux/ioport.h>
 #include <linux/math64.h>
 #include <linux/module.h>
-#include <linux/mtd/mtd.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/of_device.h>







From heiko at sntech.de  Sat Jun 18 06:48:49 2016
From: heiko at sntech.de (Heiko =?ISO-8859-1?Q?St=FCbner?=)
Date: Sat, 18 Jun 2016 15:48:49 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <4420923.qQJUdaA1rI@diego>

Am Donnerstag, 16. Juni 2016, 23:27:22 schrieb Daniel Lezcano:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----

> diff --git
> a/drivers/clocksource/rockchip_timer.c
> b/drivers/clocksource/rockchip_timer.c index d10bdee..d10e7a5 100644
> --- a/drivers/clocksource/rockchip_timer.c
> +++ b/drivers/clocksource/rockchip_timer.c
> @@ -200,7 +200,7 @@ static int __init rk3399_timer_init(struct device_node
> *np) return rk_timer_init(np, TIMER_CONTROL_REG3399);
>  }
> 
> -CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
> -			   rk3288_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
> -			   rk3399_timer_init);
> +CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
> +		       rk3288_timer_init);
> +CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
> +		       rk3399_timer_init);

for the Rockchip-part
Acked-by: Heiko Stuebner <heiko at sntech.de>


From robh at kernel.org  Sun Jun 19 07:22:11 2016
From: robh at kernel.org (Rob Herring)
Date: Sun, 19 Jun 2016 09:22:11 -0500
Subject: [PATCH v4,2/5] dt-bindings: mt8173-mtu3: add devicetree bindings
In-Reply-To: <1465960040-768-3-git-send-email-chunfeng.yun@mediatek.com>
References: <1465960040-768-1-git-send-email-chunfeng.yun@mediatek.com>
 <1465960040-768-3-git-send-email-chunfeng.yun@mediatek.com>
Message-ID: <20160619142211.GA6306@rob-hp-laptop>

On Wed, Jun 15, 2016 at 11:07:17AM +0800, Chunfeng Yun wrote:
> add a DT binding doc for MediaTek USB3 DRD driver
> 
> Signed-off-by: Chunfeng Yun <chunfeng.yun at mediatek.com>
> ---
>  .../devicetree/bindings/usb/mt8173-mtu3.txt        |   86 ++++++++++++++++++++
>  1 file changed, 86 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/usb/mt8173-mtu3.txt

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


From mars.cheng at mediatek.com  Sun Jun 19 22:33:05 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Mon, 20 Jun 2016 13:33:05 +0800
Subject: Add mt6755 basic chip support
In-Reply-To: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1466400785.29789.2.camel@mtkswgap22>

On Tue, 2016-06-14 at 10:20 +0800, Mars Cheng wrote:
> This patch adds basic support for Mediatek's new 8-core chip, mt6755.
> It is also named as Helio P10. It is based on 4.7-rc1
> 
> Mars Cheng (2):
>   Document: DT: Add bindings for mediatek MT6755 SoC Platform
>   arm64: dts: mediatek: add mt6755 support
> 
>  Documentation/devicetree/bindings/arm/mediatek.txt |    4 +
>  .../interrupt-controller/mediatek,sysirq.txt       |    1 +
>  .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
>  arch/arm64/boot/dts/mediatek/Makefile              |    1 +
>  arch/arm64/boot/dts/mediatek/mt6755-phone.dts      |   39 ++++++
>  arch/arm64/boot/dts/mediatek/mt6755.dtsi           |  143 ++++++++++++++++++++
>  6 files changed, 189 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
>  create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi
> 
Hi Matthias

Would you like to merge this patch? If there is any suggestions or
issues, I will fix them ASAP.

Thanks a lot.
> 
> _______________________________________________
> Linux-mediatek mailing list
> Linux-mediatek at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-mediatek




From patrice.chotard at st.com  Mon Jun 20 00:43:21 2016
From: patrice.chotard at st.com (Patrice Chotard)
Date: Mon, 20 Jun 2016 09:43:21 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <57679E99.6090607@st.com>



On 06/16/2016 11:27 PM, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
>
> Let's convert back the names:
>   - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>   - clksrc-of-ret              => clksrc-of
>
>
>   drivers/clocksource/clksrc_st_lpc.c       |  2 +-
> diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c
> index c9022a9..03cc492 100644
> --- a/drivers/clocksource/clksrc_st_lpc.c
> +++ b/drivers/clocksource/clksrc_st_lpc.c
> @@ -132,4 +132,4 @@ static int __init st_clksrc_of_register(struct device_node *np)
>   
>   	return ret;
>   }
> -CLOCKSOURCE_OF_DECLARE_RET(ddata, "st,stih407-lpc", st_clksrc_of_register);
> +CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
>


For STi :

Acked-by: Patrice Chotard <patrice.chotard at st.com>

Thanks




From matthias.bgg at gmail.com  Mon Jun 20 01:31:55 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 20 Jun 2016 10:31:55 +0200
Subject: [PATCH 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
 <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>
Message-ID: <5767A9FB.3010007@gmail.com>



On 14/06/16 04:20, Mars Cheng wrote:
> This adds basic chip support for MT6755 SoC.
>
> Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
> ---
>   arch/arm64/boot/dts/mediatek/Makefile         |    1 +
>   arch/arm64/boot/dts/mediatek/mt6755-phone.dts |   39 +++++++
>   arch/arm64/boot/dts/mediatek/mt6755.dtsi      |  143 +++++++++++++++++++++++++
>   3 files changed, 183 insertions(+)
>   create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
>   create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi
>
> diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
> index e0a4bff..9abdb01 100644
> --- a/arch/arm64/boot/dts/mediatek/Makefile
> +++ b/arch/arm64/boot/dts/mediatek/Makefile
> @@ -1,3 +1,4 @@
> +dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-phone.dtb

I suppose this is a evaluation platform? Is this different to the EVB 
for this SoC? Or will the only phone build with this SoC be this one?

Thanks,
Matthias

>   dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
>   dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
>
> diff --git a/arch/arm64/boot/dts/mediatek/mt6755-phone.dts b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
> new file mode 100644
> index 0000000..f551666
> --- /dev/null
> +++ b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
> @@ -0,0 +1,39 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Mars.C <mars.cheng at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +/dts-v1/;
> +#include "mt6755.dtsi"
> +
> +/ {
> +	model = "MediaTek MT6755 phone";
> +	compatible = "mediatek,mt6755-phone", "mediatek,mt6755";
> +
> +	aliases {
> +		serial0 = &uart0;
> +		serial1 = &uart1;

we don't enable uart1, so we don't need the alias.

> +	};
> +
> +	memory at 40000000 {
> +		device_type = "memory";
> +		reg = <0 0x40000000 0 0x1e800000>;
> +	};
> +
> +	chosen {
> +		stdout-path = "serial0:921600n8";
> +	};
> +};
> +
> +&uart0 {
> +	status = "okay";
> +};
> diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
> new file mode 100644
> index 0000000..36c3384
> --- /dev/null
> +++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
> @@ -0,0 +1,143 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Mars.C <mars.cheng at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <dt-bindings/interrupt-controller/irq.h>
> +#include <dt-bindings/interrupt-controller/arm-gic.h>
> +
> +/ {
> +	compatible = "mediatek,mt6755";
> +	interrupt-parent = <&sysirq>;
> +	#address-cells = <2>;
> +	#size-cells = <2>;
> +
> +	psci {
> +		compatible = "arm,psci-0.2";
> +		method = "smc";
> +	};
> +
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		cpu0: cpu at 0 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x000>;
> +		};
> +
> +		cpu1: cpu at 1 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x001>;
> +		};
> +
> +		cpu2: cpu at 2 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x002>;
> +		};
> +
> +		cpu3: cpu at 3 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x003>;
> +		};
> +
> +		cpu4: cpu at 100 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x100>;
> +		};
> +
> +		cpu5: cpu at 101 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x101>;
> +		};
> +
> +		cpu6: cpu at 102 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x102>;
> +		};
> +
> +		cpu7: cpu at 103 {
> +			device_type = "cpu";
> +			compatible = "arm,cortex-a53";
> +			enable-method = "psci";
> +			reg = <0x103>;
> +		};
> +	};
> +
> +	uart_clk: dummy26m {
> +		compatible = "fixed-clock";
> +		clock-frequency = <26000000>;
> +		#clock-cells = <0>;
> +	};
> +

We can do that, but I would prefer to see the clock driver early. So 
that the DTS we carry around as complete as possible.

> +	timer {
> +		compatible = "arm,armv8-timer";
> +		interrupt-parent = <&gic>;
> +		interrupts = <GIC_PPI 13
> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> +			     <GIC_PPI 14
> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> +			     <GIC_PPI 11
> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> +			     <GIC_PPI 10
> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
> +	};
> +
> +	sysirq: intpol-controller at 10200620 {
> +		compatible = "mediatek,mt6755-sysirq",
> +			     "mediatek,mt6577-sysirq";
> +		interrupt-controller;
> +		#interrupt-cells = <3>;
> +		interrupt-parent = <&gic>;
> +		reg = <0 0x10200620 0 0x20>;
> +	};
> +
> +	gic: interrupt-controller at 10231000 {
> +		compatible = "arm,gic-400";
> +		#interrupt-cells = <3>;
> +		interrupt-parent = <&gic>;
> +		interrupt-controller;
> +		reg = <0 0x10231000 0 0x1000>,
> +		      <0 0x10232000 0 0x2000>,
> +		      <0 0x10234000 0 0x2000>,
> +		      <0 0x10236000 0 0x2000>;
> +	};
> +
> +	uart0: serial at 11002000 {
> +		compatible = "mediatek,mt6755-uart",
> +			     "mediatek,mt6577-uart";
> +		reg = <0 0x11002000 0 0x400>;
> +		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
> +		clocks = <&uart_clk>;

status = "disabled";

> +	};
> +
> +	uart1: serial at 11003000 {
> +		compatible = "mediatek,mt6755-uart",
> +			     "mediatek,mt6577-uart";
> +		reg = <0 0x11003000 0 0x400>;
> +		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
> +		clocks = <&uart_clk>;

same here.

Regards,
Matthias


From liviu.dudau at arm.com  Mon Jun 20 02:17:08 2016
From: liviu.dudau at arm.com (Liviu Dudau)
Date: Mon, 20 Jun 2016 10:17:08 +0100
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <20160620091707.GP9711@e106497-lin.cambridge.arm.com>

On Thu, Jun 16, 2016 at 11:27:22PM +0200, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  arch/arc/kernel/time.c                    |  6 +++---
>  arch/arm/kernel/smp_twd.c                 |  6 +++---
>  arch/microblaze/kernel/timer.c            |  2 +-
>  arch/mips/ralink/cevt-rt3352.c            |  2 +-
>  arch/nios2/kernel/time.c                  |  2 +-
>  drivers/clocksource/arm_arch_timer.c      |  6 +++---
>  drivers/clocksource/arm_global_timer.c    |  2 +-
>  drivers/clocksource/armv7m_systick.c      |  2 +-
>  drivers/clocksource/asm9260_timer.c       |  2 +-
>  drivers/clocksource/bcm2835_timer.c       |  2 +-
>  drivers/clocksource/bcm_kona_timer.c      |  4 ++--
>  drivers/clocksource/cadence_ttc_timer.c   |  2 +-
>  drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
>  drivers/clocksource/clksrc-probe.c        | 14 --------------
>  drivers/clocksource/clksrc_st_lpc.c       |  2 +-
>  drivers/clocksource/clps711x-timer.c      |  2 +-
>  drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
>  drivers/clocksource/exynos_mct.c          |  4 ++--
>  drivers/clocksource/fsl_ftm_timer.c       |  2 +-
>  drivers/clocksource/h8300_timer16.c       |  2 +-
>  drivers/clocksource/h8300_timer8.c        |  2 +-
>  drivers/clocksource/h8300_tpu.c           |  2 +-
>  drivers/clocksource/meson6_timer.c        |  2 +-
>  drivers/clocksource/mips-gic-timer.c      |  2 +-
>  drivers/clocksource/moxart_timer.c        |  2 +-
>  drivers/clocksource/mps2-timer.c          |  2 +-
>  drivers/clocksource/mtk_timer.c           |  2 +-
>  drivers/clocksource/mxs_timer.c           |  2 +-
>  drivers/clocksource/nomadik-mtu.c         |  2 +-
>  drivers/clocksource/pxa_timer.c           |  2 +-
>  drivers/clocksource/qcom-timer.c          |  4 ++--
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----
>  drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
>  drivers/clocksource/sun4i_timer.c         |  2 +-
>  drivers/clocksource/tango_xtal.c          |  2 +-
>  drivers/clocksource/tegra20_timer.c       |  4 ++--
>  drivers/clocksource/time-armada-370-xp.c  |  6 +++---
>  drivers/clocksource/time-efm32.c          |  4 ++--
>  drivers/clocksource/time-lpc32xx.c        |  2 +-
>  drivers/clocksource/time-orion.c          |  2 +-
>  drivers/clocksource/time-pistachio.c      |  2 +-
>  drivers/clocksource/timer-atlas7.c        |  2 +-
>  drivers/clocksource/timer-atmel-pit.c     |  2 +-
>  drivers/clocksource/timer-atmel-st.c      |  2 +-
>  drivers/clocksource/timer-digicolor.c     |  2 +-
>  drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
>  drivers/clocksource/timer-integrator-ap.c |  2 +-
>  drivers/clocksource/timer-keystone.c      |  2 +-
>  drivers/clocksource/timer-nps.c           |  4 ++--
>  drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
>  drivers/clocksource/timer-prima2.c        |  2 +-
>  drivers/clocksource/timer-sp804.c         |  4 ++--
>  drivers/clocksource/timer-stm32.c         |  2 +-
>  drivers/clocksource/timer-sun5i.c         |  4 ++--
>  drivers/clocksource/timer-ti-32k.c        |  2 +-
>  drivers/clocksource/timer-u300.c          |  2 +-
>  drivers/clocksource/versatile.c           |  4 ++--
>  drivers/clocksource/vf_pit_timer.c        |  2 +-
>  drivers/clocksource/vt8500_timer.c        |  2 +-
>  drivers/clocksource/zevio-timer.c         |  2 +-
>  include/asm-generic/vmlinux.lds.h         |  2 --
>  include/linux/clocksource.h               |  5 +----
>  62 files changed, 98 insertions(+), 117 deletions(-)

For the mps2-timer.c and versatile.c changes:

Acked-by: Liviu Dudau <Liviu.Dudau at arm.com>

Thanks for all the work!

Liviu

> 
> diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c
> index 36110cd..ee00f2f 100644
> --- a/arch/arc/kernel/time.c
> +++ b/arch/arc/kernel/time.c
> @@ -130,7 +130,7 @@ static int __init arc_cs_setup_gfrc(struct device_node *node)
>  
>  	return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
> +CLOCKSOURCE_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
>  
>  #endif
>  
> @@ -192,7 +192,7 @@ static int __init arc_cs_setup_rtc(struct device_node *node)
>  
>  	return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
> +CLOCKSOURCE_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
>  
>  #endif
>  
> @@ -379,7 +379,7 @@ static int __init arc_of_timer_init(struct device_node *np)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
> +CLOCKSOURCE_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
>  
>  /*
>   * Called from start_kernel() - boot CPU only
> diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
> index 2b24be4..b6ec65e 100644
> --- a/arch/arm/kernel/smp_twd.c
> +++ b/arch/arm/kernel/smp_twd.c
> @@ -412,7 +412,7 @@ out:
>  	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
>  	return err;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
> -CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
> -CLOCKSOURCE_OF_DECLARE_RET(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
> +CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
> +CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
> +CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
>  #endif
> diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
> index 7f35e7b..5bbf38b 100644
> --- a/arch/microblaze/kernel/timer.c
> +++ b/arch/microblaze/kernel/timer.c
> @@ -332,5 +332,5 @@ static int __init xilinx_timer_init(struct device_node *timer)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(xilinx_timer, "xlnx,xps-timer-1.00.a",
> +CLOCKSOURCE_OF_DECLARE(xilinx_timer, "xlnx,xps-timer-1.00.a",
>  		       xilinx_timer_init);
> diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
> index f2d3c79..f24eee0 100644
> --- a/arch/mips/ralink/cevt-rt3352.c
> +++ b/arch/mips/ralink/cevt-rt3352.c
> @@ -150,4 +150,4 @@ static int __init ralink_systick_init(struct device_node *np)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(systick, "ralink,cevt-systick", ralink_systick_init);
> +CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
> diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c
> index 9fa7739..f337eef 100644
> --- a/arch/nios2/kernel/time.c
> +++ b/arch/nios2/kernel/time.c
> @@ -354,4 +354,4 @@ void __init time_init(void)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
> +CLOCKSOURCE_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
> diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
> index d0cda68..9e33309 100644
> --- a/drivers/clocksource/arm_arch_timer.c
> +++ b/drivers/clocksource/arm_arch_timer.c
> @@ -784,8 +784,8 @@ static int __init arch_timer_of_init(struct device_node *np)
>  
>  	return arch_timer_init();
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
> -CLOCKSOURCE_OF_DECLARE_RET(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
> +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
> +CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
>  
>  static int __init arch_timer_mem_init(struct device_node *np)
>  {
> @@ -868,7 +868,7 @@ out:
>  	of_node_put(best_frame);
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer_mem, "arm,armv7-timer-mem",
> +CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
>  		       arch_timer_mem_init);
>  
>  #ifdef CONFIG_ACPI
> diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
> index 40104fc..2a9ceb6 100644
> --- a/drivers/clocksource/arm_global_timer.c
> +++ b/drivers/clocksource/arm_global_timer.c
> @@ -358,5 +358,5 @@ out_unmap:
>  }
>  
>  /* Only tested on r2p2 and r3p0  */
> -CLOCKSOURCE_OF_DECLARE_RET(arm_gt, "arm,cortex-a9-global-timer",
> +CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
>  			global_timer_of_register);
> diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
> index 2b55410..e93af1f 100644
> --- a/drivers/clocksource/armv7m_systick.c
> +++ b/drivers/clocksource/armv7m_systick.c
> @@ -81,5 +81,5 @@ out_unmap:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(arm_systick, "arm,armv7m-systick",
> +CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
>  			system_timer_of_register);
> diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
> index d113c02..1ba871b 100644
> --- a/drivers/clocksource/asm9260_timer.c
> +++ b/drivers/clocksource/asm9260_timer.c
> @@ -238,5 +238,5 @@ static int __init asm9260_timer_init(struct device_node *np)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(asm9260_timer, "alphascale,asm9260-timer",
> +CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer",
>  		asm9260_timer_init);
> diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
> index 2dcf896..e71acf2 100644
> --- a/drivers/clocksource/bcm2835_timer.c
> +++ b/drivers/clocksource/bcm2835_timer.c
> @@ -142,5 +142,5 @@ static int __init bcm2835_timer_init(struct device_node *node)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(bcm2835, "brcm,bcm2835-system-timer",
> +CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
>  			bcm2835_timer_init);
> diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
> index 98bc2a2..ee07e7e3 100644
> --- a/drivers/clocksource/bcm_kona_timer.c
> +++ b/drivers/clocksource/bcm_kona_timer.c
> @@ -200,9 +200,9 @@ static int __init kona_timer_init(struct device_node *node)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(brcm_kona, "brcm,kona-timer", kona_timer_init);
> +CLOCKSOURCE_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
>  /*
>   * bcm,kona-timer is deprecated by brcm,kona-timer
>   * being kept here for driver compatibility
>   */
> -CLOCKSOURCE_OF_DECLARE_RET(bcm_kona, "bcm,kona-timer", kona_timer_init);
> +CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);
> diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
> index e2e7631..388a77b 100644
> --- a/drivers/clocksource/cadence_ttc_timer.c
> +++ b/drivers/clocksource/cadence_ttc_timer.c
> @@ -539,4 +539,4 @@ static int __init ttc_timer_init(struct device_node *timer)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(ttc, "cdns,ttc", ttc_timer_init);
> +CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
> diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
> index 5a59d29..77a365f 100644
> --- a/drivers/clocksource/clksrc-dbx500-prcmu.c
> +++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
> @@ -86,5 +86,5 @@ static int __init clksrc_dbx500_prcmu_init(struct device_node *node)
>  #endif
>  	return clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
> +CLOCKSOURCE_OF_DECLARE(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
>  		       clksrc_dbx500_prcmu_init);
> diff --git a/drivers/clocksource/clksrc-probe.c b/drivers/clocksource/clksrc-probe.c
> index 5fa6a55..bc62be9 100644
> --- a/drivers/clocksource/clksrc-probe.c
> +++ b/drivers/clocksource/clksrc-probe.c
> @@ -20,19 +20,14 @@
>  #include <linux/clocksource.h>
>  
>  extern struct of_device_id __clksrc_of_table[];
> -extern struct of_device_id __clksrc_ret_of_table[];
>  
>  static const struct of_device_id __clksrc_of_table_sentinel
>  	__used __section(__clksrc_of_table_end);
>  
> -static const struct of_device_id __clksrc_ret_of_table_sentinel
> -	__used __section(__clksrc_ret_of_table_end);
> -
>  void __init clocksource_probe(void)
>  {
>  	struct device_node *np;
>  	const struct of_device_id *match;
> -	of_init_fn_1 init_func;
>  	of_init_fn_1_ret init_func_ret;
>  	unsigned clocksources = 0;
>  	int ret;
> @@ -41,15 +36,6 @@ void __init clocksource_probe(void)
>  		if (!of_device_is_available(np))
>  			continue;
>  
> -		init_func = match->data;
> -		init_func(np);
> -		clocksources++;
> -	}
> -
> -	for_each_matching_node_and_match(np, __clksrc_ret_of_table, &match) {
> -		if (!of_device_is_available(np))
> -			continue;
> -
>  		init_func_ret = match->data;
>  
>  		ret = init_func_ret(np);
> diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c
> index c9022a9..03cc492 100644
> --- a/drivers/clocksource/clksrc_st_lpc.c
> +++ b/drivers/clocksource/clksrc_st_lpc.c
> @@ -132,4 +132,4 @@ static int __init st_clksrc_of_register(struct device_node *np)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(ddata, "st,stih407-lpc", st_clksrc_of_register);
> +CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
> diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
> index 3b66198..84aed78 100644
> --- a/drivers/clocksource/clps711x-timer.c
> +++ b/drivers/clocksource/clps711x-timer.c
> @@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np)
>  		return -EINVAL;
>  	}
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
> +CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
>  #endif
> diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
> index 4985a2c..aee6c0d 100644
> --- a/drivers/clocksource/dw_apb_timer_of.c
> +++ b/drivers/clocksource/dw_apb_timer_of.c
> @@ -167,7 +167,7 @@ static int __init dw_apb_timer_init(struct device_node *timer)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
> +CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
> +CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
> +CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
> +CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
> diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
> index f6caed0..0d18dd4b 100644
> --- a/drivers/clocksource/exynos_mct.c
> +++ b/drivers/clocksource/exynos_mct.c
> @@ -627,5 +627,5 @@ static int __init mct_init_ppi(struct device_node *np)
>  {
>  	return mct_init_dt(np, MCT_INT_PPI);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
> -CLOCKSOURCE_OF_DECLARE_RET(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
> +CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
> +CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
> diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
> index 9ad4ca3..738515b 100644
> --- a/drivers/clocksource/fsl_ftm_timer.c
> +++ b/drivers/clocksource/fsl_ftm_timer.c
> @@ -369,4 +369,4 @@ err:
>  	kfree(priv);
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(flextimer, "fsl,ftm-timer", ftm_timer_init);
> +CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
> diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
> index 9d99fc8..07d9d5b 100644
> --- a/drivers/clocksource/h8300_timer16.c
> +++ b/drivers/clocksource/h8300_timer16.c
> @@ -187,5 +187,5 @@ free_clk:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(h8300_16bit, "renesas,16bit-timer",
> +CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
>  			   h8300_16timer_init);
> diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
> index 0292a19..546bb18 100644
> --- a/drivers/clocksource/h8300_timer8.c
> +++ b/drivers/clocksource/h8300_timer8.c
> @@ -215,4 +215,4 @@ free_clk:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
> +CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
> diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
> index 4faf718..7bdf199 100644
> --- a/drivers/clocksource/h8300_tpu.c
> +++ b/drivers/clocksource/h8300_tpu.c
> @@ -154,4 +154,4 @@ free_clk:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(h8300_tpu, "renesas,tpu", h8300_tpu_init);
> +CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
> diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
> index 3a6e78f..52af591 100644
> --- a/drivers/clocksource/meson6_timer.c
> +++ b/drivers/clocksource/meson6_timer.c
> @@ -174,5 +174,5 @@ static int __init meson6_timer_init(struct device_node *node)
>  					1, 0xfffe);
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(meson6, "amlogic,meson6-timer",
> +CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
>  		       meson6_timer_init);
> diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
> index b164b87..1572c7a 100644
> --- a/drivers/clocksource/mips-gic-timer.c
> +++ b/drivers/clocksource/mips-gic-timer.c
> @@ -222,5 +222,5 @@ static void __init gic_clocksource_of_init(struct device_node *node)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(mips_gic_timer, "mti,gic-timer",
> +CLOCKSOURCE_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
>  		       gic_clocksource_of_init);
> diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
> index b9c30cd..8414544 100644
> --- a/drivers/clocksource/moxart_timer.c
> +++ b/drivers/clocksource/moxart_timer.c
> @@ -178,4 +178,4 @@ static int __init moxart_timer_init(struct device_node *node)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(moxart, "moxa,moxart-timer", moxart_timer_init);
> +CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
> diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
> index c303fa9..3e4431e 100644
> --- a/drivers/clocksource/mps2-timer.c
> +++ b/drivers/clocksource/mps2-timer.c
> @@ -274,4 +274,4 @@ static int __init mps2_timer_init(struct device_node *np)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(mps2_timer, "arm,mps2-timer", mps2_timer_init);
> +CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
> diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
> index 432a2c0..9065949 100644
> --- a/drivers/clocksource/mtk_timer.c
> +++ b/drivers/clocksource/mtk_timer.c
> @@ -265,4 +265,4 @@ err_kzalloc:
>  
>  	return -EINVAL;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
> +CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
> diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c
> index 17b9d19..630a8d3 100644
> --- a/drivers/clocksource/mxs_timer.c
> +++ b/drivers/clocksource/mxs_timer.c
> @@ -295,4 +295,4 @@ static int __init mxs_timer_init(struct device_node *np)
>  
>  	return setup_irq(irq, &mxs_timer_irq);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(mxs, "fsl,timrot", mxs_timer_init);
> +CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);
> diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
> index d2be5b3..3c124d1 100644
> --- a/drivers/clocksource/nomadik-mtu.c
> +++ b/drivers/clocksource/nomadik-mtu.c
> @@ -284,5 +284,5 @@ static int __init nmdk_timer_of_init(struct device_node *node)
>  
>  	return nmdk_timer_init(base, irq, pclk, clk);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(nomadik_mtu, "st,nomadik-mtu",
> +CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
>  		       nmdk_timer_of_init);
> diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c
> index 59af75c..937e10b 100644
> --- a/drivers/clocksource/pxa_timer.c
> +++ b/drivers/clocksource/pxa_timer.c
> @@ -213,7 +213,7 @@ static int __init pxa_timer_dt_init(struct device_node *np)
>  
>  	return pxa_timer_common_init(irq, clk_get_rate(clk));
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
> +CLOCKSOURCE_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
>  
>  /*
>   * Legacy timer init for non device-tree boards.
> diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
> index 79f73bd..6625763 100644
> --- a/drivers/clocksource/qcom-timer.c
> +++ b/drivers/clocksource/qcom-timer.c
> @@ -273,5 +273,5 @@ static int __init msm_dt_timer_init(struct device_node *np)
>  
>  	return msm_timer_init(freq, 32, irq, !!percpu_offset);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
> +CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
> +CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
> diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
> index d10bdee..d10e7a5 100644
> --- a/drivers/clocksource/rockchip_timer.c
> +++ b/drivers/clocksource/rockchip_timer.c
> @@ -200,7 +200,7 @@ static int __init rk3399_timer_init(struct device_node *np)
>  	return rk_timer_init(np, TIMER_CONTROL_REG3399);
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
> -			   rk3288_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
> -			   rk3399_timer_init);
> +CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
> +		       rk3288_timer_init);
> +CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
> +		       rk3399_timer_init);
> diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
> index 27a9797..54565bd 100644
> --- a/drivers/clocksource/samsung_pwm_timer.c
> +++ b/drivers/clocksource/samsung_pwm_timer.c
> @@ -466,7 +466,7 @@ static int __init s3c2410_pwm_clocksource_init(struct device_node *np)
>  {
>  	return samsung_pwm_alloc(np, &s3c24xx_variant);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
> +CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
>  
>  static const struct samsung_pwm_variant s3c64xx_variant = {
>  	.bits		= 32,
> @@ -479,7 +479,7 @@ static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)
>  {
>  	return samsung_pwm_alloc(np, &s3c64xx_variant);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
> +CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
>  
>  static const struct samsung_pwm_variant s5p64x0_variant = {
>  	.bits		= 32,
> @@ -492,7 +492,7 @@ static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)
>  {
>  	return samsung_pwm_alloc(np, &s5p64x0_variant);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
> +CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
>  
>  static const struct samsung_pwm_variant s5p_variant = {
>  	.bits		= 32,
> @@ -505,5 +505,5 @@ static int __init s5p_pwm_clocksource_init(struct device_node *np)
>  {
>  	return samsung_pwm_alloc(np, &s5p_variant);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
> +CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
>  #endif
> diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
> index 4453730..97669ee 100644
> --- a/drivers/clocksource/sun4i_timer.c
> +++ b/drivers/clocksource/sun4i_timer.c
> @@ -226,5 +226,5 @@ static int __init sun4i_timer_init(struct device_node *node)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(sun4i, "allwinner,sun4i-a10-timer",
> +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
>  		       sun4i_timer_init);
> diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
> index 7dc716c..12fcef8 100644
> --- a/drivers/clocksource/tango_xtal.c
> +++ b/drivers/clocksource/tango_xtal.c
> @@ -53,4 +53,4 @@ static int __init tango_clocksource_init(struct device_node *np)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(tango, "sigma,tick-counter", tango_clocksource_init);
> +CLOCKSOURCE_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
> diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
> index 543c37e..f960891 100644
> --- a/drivers/clocksource/tegra20_timer.c
> +++ b/drivers/clocksource/tegra20_timer.c
> @@ -237,7 +237,7 @@ static int __init tegra20_init_timer(struct device_node *np)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
> +CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
>  
>  static int __init tegra20_init_rtc(struct device_node *np)
>  {
> @@ -261,4 +261,4 @@ static int __init tegra20_init_rtc(struct device_node *np)
>  
>  	return register_persistent_clock(NULL, tegra_read_persistent_clock64);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
> +CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
> diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
> index bc4ab48..a4e5923 100644
> --- a/drivers/clocksource/time-armada-370-xp.c
> +++ b/drivers/clocksource/time-armada-370-xp.c
> @@ -371,7 +371,7 @@ static int __init armada_xp_timer_init(struct device_node *np)
>  
>  	return armada_370_xp_timer_common_init(np);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(armada_xp, "marvell,armada-xp-timer",
> +CLOCKSOURCE_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
>  		       armada_xp_timer_init);
>  
>  static int __init armada_375_timer_init(struct device_node *np)
> @@ -409,7 +409,7 @@ static int __init armada_375_timer_init(struct device_node *np)
>  
>  	return armada_370_xp_timer_common_init(np);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(armada_375, "marvell,armada-375-timer",
> +CLOCKSOURCE_OF_DECLARE(armada_375, "marvell,armada-375-timer",
>  		       armada_375_timer_init);
>  
>  static int __init armada_370_timer_init(struct device_node *np)
> @@ -432,5 +432,5 @@ static int __init armada_370_timer_init(struct device_node *np)
>  
>  	return armada_370_xp_timer_common_init(np);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(armada_370, "marvell,armada-370-timer",
> +CLOCKSOURCE_OF_DECLARE(armada_370, "marvell,armada-370-timer",
>  		       armada_370_timer_init);
> diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c
> index b71ffc6..10a0521 100644
> --- a/drivers/clocksource/time-efm32.c
> +++ b/drivers/clocksource/time-efm32.c
> @@ -282,5 +282,5 @@ static int __init efm32_timer_init(struct device_node *np)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(efm32compat, "efm32,timer", efm32_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(efm32, "energymicro,efm32-timer", efm32_timer_init);
> +CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
> +CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
> diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
> index cb5b866..9649cfd 100644
> --- a/drivers/clocksource/time-lpc32xx.c
> +++ b/drivers/clocksource/time-lpc32xx.c
> @@ -311,4 +311,4 @@ static int __init lpc32xx_timer_init(struct device_node *np)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
> +CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
> diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
> index 5fdeb5d..a28f496 100644
> --- a/drivers/clocksource/time-orion.c
> +++ b/drivers/clocksource/time-orion.c
> @@ -167,4 +167,4 @@ static int __init orion_timer_init(struct device_node *np)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(orion_timer, "marvell,orion-timer", orion_timer_init);
> +CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
> diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
> index adaaec5..a7d9a08 100644
> --- a/drivers/clocksource/time-pistachio.c
> +++ b/drivers/clocksource/time-pistachio.c
> @@ -214,5 +214,5 @@ static int __init pistachio_clksrc_of_init(struct device_node *node)
>  	sched_clock_register(pistachio_read_sched_clock, 32, rate);
>  	return clocksource_register_hz(&pcs_gpt.cs, rate);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(pistachio_gptimer, "img,pistachio-gptimer",
> +CLOCKSOURCE_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer",
>  		       pistachio_clksrc_of_init);
> diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c
> index 7b1a007..90f8fbc 100644
> --- a/drivers/clocksource/timer-atlas7.c
> +++ b/drivers/clocksource/timer-atlas7.c
> @@ -304,4 +304,4 @@ static int __init sirfsoc_of_timer_init(struct device_node *np)
>  
>  	return sirfsoc_atlas7_timer_init(np);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
> +CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
> diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
> index ffaca7c..1ffac0c 100644
> --- a/drivers/clocksource/timer-atmel-pit.c
> +++ b/drivers/clocksource/timer-atmel-pit.c
> @@ -270,5 +270,5 @@ static int __init at91sam926x_pit_dt_init(struct device_node *node)
>  
>  	return at91sam926x_pit_common_init(data);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(at91sam926x_pit, "atmel,at91sam9260-pit",
> +CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
>  		       at91sam926x_pit_dt_init);
> diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c
> index e9331d3..e90ab5b 100644
> --- a/drivers/clocksource/timer-atmel-st.c
> +++ b/drivers/clocksource/timer-atmel-st.c
> @@ -260,5 +260,5 @@ static int __init atmel_st_timer_init(struct device_node *node)
>  	/* register clocksource */
>  	return clocksource_register_hz(&clk32k, sclk_rate);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(atmel_st_timer, "atmel,at91rm9200-st",
> +CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
>  		       atmel_st_timer_init);
> diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c
> index b929061..10318cc 100644
> --- a/drivers/clocksource/timer-digicolor.c
> +++ b/drivers/clocksource/timer-digicolor.c
> @@ -202,5 +202,5 @@ static int __init digicolor_timer_init(struct device_node *node)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(conexant_digicolor, "cnxt,cx92755-timer",
> +CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
>  		       digicolor_timer_init);
> diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
> index d5640a74..f595460 100644
> --- a/drivers/clocksource/timer-imx-gpt.c
> +++ b/drivers/clocksource/timer-imx-gpt.c
> @@ -545,15 +545,15 @@ static int __init imx6dl_timer_init_dt(struct device_node *np)
>  	return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
> -CLOCKSOURCE_OF_DECLARE_RET(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
> +CLOCKSOURCE_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
> diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
> index 675face..df6e672 100644
> --- a/drivers/clocksource/timer-integrator-ap.c
> +++ b/drivers/clocksource/timer-integrator-ap.c
> @@ -232,5 +232,5 @@ static int __init integrator_ap_timer_init_of(struct device_node *node)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(integrator_ap_timer, "arm,integrator-timer",
> +CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
>  		       integrator_ap_timer_init_of);
> diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c
> index 4199823..ab68a47 100644
> --- a/drivers/clocksource/timer-keystone.c
> +++ b/drivers/clocksource/timer-keystone.c
> @@ -226,5 +226,5 @@ err:
>  	return error;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(keystone_timer, "ti,keystone-timer",
> +CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
>  			   keystone_timer_init);
> diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
> index b5c7b2b..70c149a 100644
> --- a/drivers/clocksource/timer-nps.c
> +++ b/drivers/clocksource/timer-nps.c
> @@ -96,5 +96,5 @@ static int __init nps_timer_init(struct device_node *node)
>  	return nps_setup_clocksource(node, clk);
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(ezchip_nps400_clksrc, "ezchip,nps400-timer",
> -			   nps_timer_init);
> +CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
> +		       nps_timer_init);
> diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
> index 0d99f40..bd887e2 100644
> --- a/drivers/clocksource/timer-oxnas-rps.c
> +++ b/drivers/clocksource/timer-oxnas-rps.c
> @@ -293,5 +293,5 @@ err_alloc:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(ox810se_rps,
> -			   "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
> +CLOCKSOURCE_OF_DECLARE(ox810se_rps,
> +		       "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
> diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
> index 7b1084d..dae8a66 100644
> --- a/drivers/clocksource/timer-prima2.c
> +++ b/drivers/clocksource/timer-prima2.c
> @@ -246,5 +246,5 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_prima2_timer,
> +CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
>  	"sirf,prima2-tick", sirfsoc_prima2_timer_init);
> diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
> index 7c2944ad..a2b28da 100644
> --- a/drivers/clocksource/timer-sp804.c
> +++ b/drivers/clocksource/timer-sp804.c
> @@ -287,7 +287,7 @@ err:
>  	iounmap(base);
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(sp804, "arm,sp804", sp804_of_init);
> +CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
>  
>  static int __init integrator_cp_of_init(struct device_node *np)
>  {
> @@ -336,4 +336,4 @@ err:
>  	iounmap(base);
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
> +CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
> diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
> index d5bf352..1b2574c 100644
> --- a/drivers/clocksource/timer-stm32.c
> +++ b/drivers/clocksource/timer-stm32.c
> @@ -187,4 +187,4 @@ err_clk_get:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(stm32, "st,stm32-timer", stm32_clockevent_init);
> +CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
> diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
> index f0a3ffb..c184eb8 100644
> --- a/drivers/clocksource/timer-sun5i.c
> +++ b/drivers/clocksource/timer-sun5i.c
> @@ -346,7 +346,7 @@ static int __init sun5i_timer_init(struct device_node *node)
>  
>  	return sun5i_setup_clockevent(node, timer_base, clk, irq);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(sun5i_a13, "allwinner,sun5i-a13-hstimer",
> +CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
>  			   sun5i_timer_init);
> -CLOCKSOURCE_OF_DECLARE_RET(sun7i_a20, "allwinner,sun7i-a20-hstimer",
> +CLOCKSOURCE_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
>  			   sun5i_timer_init);
> diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
> index e4ad3c6e..92b7e39 100644
> --- a/drivers/clocksource/timer-ti-32k.c
> +++ b/drivers/clocksource/timer-ti-32k.c
> @@ -124,5 +124,5 @@ static int __init ti_32k_timer_init(struct device_node *np)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(ti_32k_timer, "ti,omap-counter32k",
> +CLOCKSOURCE_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
>  		ti_32k_timer_init);
> diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
> index a6a0dec..704e40c 100644
> --- a/drivers/clocksource/timer-u300.c
> +++ b/drivers/clocksource/timer-u300.c
> @@ -458,5 +458,5 @@ static int __init u300_timer_init_of(struct device_node *np)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(u300_timer, "stericsson,u300-apptimer",
> +CLOCKSOURCE_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
>  		       u300_timer_init_of);
> diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
> index 8daeffa..220b490 100644
> --- a/drivers/clocksource/versatile.c
> +++ b/drivers/clocksource/versatile.c
> @@ -38,7 +38,7 @@ static int __init versatile_sched_clock_init(struct device_node *node)
>  
>  	return 0;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(vexpress, "arm,vexpress-sysreg",
> +CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg",
>  		       versatile_sched_clock_init);
> -CLOCKSOURCE_OF_DECLARE_RET(versatile, "arm,versatile-sysreg",
> +CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg",
>  		       versatile_sched_clock_init);
> diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
> index ca4dff4..55d8d84 100644
> --- a/drivers/clocksource/vf_pit_timer.c
> +++ b/drivers/clocksource/vf_pit_timer.c
> @@ -201,4 +201,4 @@ static int __init pit_timer_init(struct device_node *np)
>  
>  	return pit_clockevent_init(clk_rate, irq);
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(vf610, "fsl,vf610-pit", pit_timer_init);
> +CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
> diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
> index 1bc8707..b150694 100644
> --- a/drivers/clocksource/vt8500_timer.c
> +++ b/drivers/clocksource/vt8500_timer.c
> @@ -165,4 +165,4 @@ static int __init vt8500_timer_init(struct device_node *np)
>  	return 0;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(vt8500, "via,vt8500-timer", vt8500_timer_init);
> +CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
> diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
> index cb4cf056..9a53f5e 100644
> --- a/drivers/clocksource/zevio-timer.c
> +++ b/drivers/clocksource/zevio-timer.c
> @@ -215,4 +215,4 @@ static int __init zevio_timer_init(struct device_node *node)
>  	return zevio_timer_add(node);
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
> +CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index 8c6c626..6a67ab9 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -173,7 +173,6 @@
>  	*(__##name##_of_table_end)
>  
>  #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
> -#define CLKSRC_RET_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc_ret)
>  #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
>  #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
>  #define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
> @@ -532,7 +531,6 @@
>  	CLK_OF_TABLES()							\
>  	RESERVEDMEM_OF_TABLES()						\
>  	CLKSRC_OF_TABLES()						\
> -	CLKSRC_RET_OF_TABLES()						\
>  	IOMMU_OF_TABLES()						\
>  	CPU_METHOD_OF_TABLES()						\
>  	CPUIDLE_METHOD_OF_TABLES()					\
> diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
> index 15c3839..0839818 100644
> --- a/include/linux/clocksource.h
> +++ b/include/linux/clocksource.h
> @@ -244,10 +244,7 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
>  extern int clocksource_i8253_init(void);
>  
>  #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
> -	OF_DECLARE_1(clksrc, name, compat, fn)
> -
> -#define CLOCKSOURCE_OF_DECLARE_RET(name, compat, fn) \
> -	OF_DECLARE_1_RET(clksrc_ret, name, compat, fn)
> +	OF_DECLARE_1_RET(clksrc, name, compat, fn)
>  
>  #ifdef CONFIG_CLKSRC_PROBE
>  extern void clocksource_probe(void);
> -- 
> 1.9.1
> 

-- 
====================
| I would like to |
| fix the world,  |
| but they're not |
| giving me the   |
 \ source code!  /
  ---------------
    ?\_(?)_/?


From mars.cheng at mediatek.com  Mon Jun 20 02:19:10 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Mon, 20 Jun 2016 17:19:10 +0800
Subject: [PATCH 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <5767A9FB.3010007@gmail.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
 <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>
 <5767A9FB.3010007@gmail.com>
Message-ID: <1466414350.3378.9.camel@mtkswgap22>

On Mon, 2016-06-20 at 10:31 +0200, Matthias Brugger wrote:
> 
> On 14/06/16 04:20, Mars Cheng wrote:
> > This adds basic chip support for MT6755 SoC.
> >
> > Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
> > ---
> >   arch/arm64/boot/dts/mediatek/Makefile         |    1 +
> >   arch/arm64/boot/dts/mediatek/mt6755-phone.dts |   39 +++++++
> >   arch/arm64/boot/dts/mediatek/mt6755.dtsi      |  143 +++++++++++++++++++++++++
> >   3 files changed, 183 insertions(+)
> >   create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
> >   create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
> > index e0a4bff..9abdb01 100644
> > --- a/arch/arm64/boot/dts/mediatek/Makefile
> > +++ b/arch/arm64/boot/dts/mediatek/Makefile
> > @@ -1,3 +1,4 @@
> > +dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-phone.dtb
> 
> I suppose this is a evaluation platform? Is this different to the EVB 
> for this SoC? Or will the only phone build with this SoC be this one?
> 

Yes, this is an evaluation platform which is an internal demo phone.
No difference to EVB. Would you suggest I change the name? If so, I will
change it to mt6755-evb.dtb. So will the compatible string in it.
 
> Thanks,
> Matthias
> 
> >   dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
> >   dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
> >
> > diff --git a/arch/arm64/boot/dts/mediatek/mt6755-phone.dts b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
> > new file mode 100644
> > index 0000000..f551666
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
> > @@ -0,0 +1,39 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Mars.C <mars.cheng at mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +/dts-v1/;
> > +#include "mt6755.dtsi"
> > +
> > +/ {
> > +	model = "MediaTek MT6755 phone";
> > +	compatible = "mediatek,mt6755-phone", "mediatek,mt6755";
> > +
> > +	aliases {
> > +		serial0 = &uart0;
> > +		serial1 = &uart1;
> 
> we don't enable uart1, so we don't need the alias.

OK, will fix this.

> 
> > +	};
> > +
> > +	memory at 40000000 {
> > +		device_type = "memory";
> > +		reg = <0 0x40000000 0 0x1e800000>;
> > +	};
> > +
> > +	chosen {
> > +		stdout-path = "serial0:921600n8";
> > +	};
> > +};
> > +
> > +&uart0 {
> > +	status = "okay";
> > +};
> > diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
> > new file mode 100644
> > index 0000000..36c3384
> > --- /dev/null
> > +++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
> > @@ -0,0 +1,143 @@
> > +/*
> > + * Copyright (c) 2016 MediaTek Inc.
> > + * Author: Mars.C <mars.cheng at mediatek.com>
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> > + * GNU General Public License for more details.
> > + */
> > +
> > +#include <dt-bindings/interrupt-controller/irq.h>
> > +#include <dt-bindings/interrupt-controller/arm-gic.h>
> > +
> > +/ {
> > +	compatible = "mediatek,mt6755";
> > +	interrupt-parent = <&sysirq>;
> > +	#address-cells = <2>;
> > +	#size-cells = <2>;
> > +
> > +	psci {
> > +		compatible = "arm,psci-0.2";
> > +		method = "smc";
> > +	};
> > +
> > +	cpus {
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		cpu0: cpu at 0 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x000>;
> > +		};
> > +
> > +		cpu1: cpu at 1 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x001>;
> > +		};
> > +
> > +		cpu2: cpu at 2 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x002>;
> > +		};
> > +
> > +		cpu3: cpu at 3 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x003>;
> > +		};
> > +
> > +		cpu4: cpu at 100 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x100>;
> > +		};
> > +
> > +		cpu5: cpu at 101 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x101>;
> > +		};
> > +
> > +		cpu6: cpu at 102 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x102>;
> > +		};
> > +
> > +		cpu7: cpu at 103 {
> > +			device_type = "cpu";
> > +			compatible = "arm,cortex-a53";
> > +			enable-method = "psci";
> > +			reg = <0x103>;
> > +		};
> > +	};
> > +
> > +	uart_clk: dummy26m {
> > +		compatible = "fixed-clock";
> > +		clock-frequency = <26000000>;
> > +		#clock-cells = <0>;
> > +	};
> > +
> 
> We can do that, but I would prefer to see the clock driver early. So 
> that the DTS we carry around as complete as possible.
> 

OK, I will merge the clk later. However, the clk driver would be
submmited later, is that OK? Or would you prefer sunmmit clk driver
together?

> > +	timer {
> > +		compatible = "arm,armv8-timer";
> > +		interrupt-parent = <&gic>;
> > +		interrupts = <GIC_PPI 13
> > +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> > +			     <GIC_PPI 14
> > +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> > +			     <GIC_PPI 11
> > +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> > +			     <GIC_PPI 10
> > +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
> > +	};
> > +
> > +	sysirq: intpol-controller at 10200620 {
> > +		compatible = "mediatek,mt6755-sysirq",
> > +			     "mediatek,mt6577-sysirq";
> > +		interrupt-controller;
> > +		#interrupt-cells = <3>;
> > +		interrupt-parent = <&gic>;
> > +		reg = <0 0x10200620 0 0x20>;
> > +	};
> > +
> > +	gic: interrupt-controller at 10231000 {
> > +		compatible = "arm,gic-400";
> > +		#interrupt-cells = <3>;
> > +		interrupt-parent = <&gic>;
> > +		interrupt-controller;
> > +		reg = <0 0x10231000 0 0x1000>,
> > +		      <0 0x10232000 0 0x2000>,
> > +		      <0 0x10234000 0 0x2000>,
> > +		      <0 0x10236000 0 0x2000>;
> > +	};
> > +
> > +	uart0: serial at 11002000 {
> > +		compatible = "mediatek,mt6755-uart",
> > +			     "mediatek,mt6577-uart";
> > +		reg = <0 0x11002000 0 0x400>;
> > +		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
> > +		clocks = <&uart_clk>;
> 
> status = "disabled";

OK, will fix this.
> 
> > +	};
> > +
> > +	uart1: serial at 11003000 {
> > +		compatible = "mediatek,mt6755-uart",
> > +			     "mediatek,mt6577-uart";
> > +		reg = <0 0x11003000 0 0x400>;
> > +		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
> > +		clocks = <&uart_clk>;
> 
> same here.

OK, will fix this.

Thanks.
> 
> Regards,
> Matthias




From narmstrong at baylibre.com  Mon Jun 20 02:19:21 2016
From: narmstrong at baylibre.com (Neil Armstrong)
Date: Mon, 20 Jun 2016 11:19:21 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <5767B519.5010501@baylibre.com>

On 06/16/2016 11:27 PM, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  arch/arc/kernel/time.c                    |  6 +++---
>  arch/arm/kernel/smp_twd.c                 |  6 +++---
>  arch/microblaze/kernel/timer.c            |  2 +-
>  arch/mips/ralink/cevt-rt3352.c            |  2 +-
>  arch/nios2/kernel/time.c                  |  2 +-
>  drivers/clocksource/arm_arch_timer.c      |  6 +++---
>  drivers/clocksource/arm_global_timer.c    |  2 +-
>  drivers/clocksource/armv7m_systick.c      |  2 +-
>  drivers/clocksource/asm9260_timer.c       |  2 +-
>  drivers/clocksource/bcm2835_timer.c       |  2 +-
>  drivers/clocksource/bcm_kona_timer.c      |  4 ++--
>  drivers/clocksource/cadence_ttc_timer.c   |  2 +-
>  drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
>  drivers/clocksource/clksrc-probe.c        | 14 --------------
>  drivers/clocksource/clksrc_st_lpc.c       |  2 +-
>  drivers/clocksource/clps711x-timer.c      |  2 +-
>  drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
>  drivers/clocksource/exynos_mct.c          |  4 ++--
>  drivers/clocksource/fsl_ftm_timer.c       |  2 +-
>  drivers/clocksource/h8300_timer16.c       |  2 +-
>  drivers/clocksource/h8300_timer8.c        |  2 +-
>  drivers/clocksource/h8300_tpu.c           |  2 +-
>  drivers/clocksource/meson6_timer.c        |  2 +-
>  drivers/clocksource/mips-gic-timer.c      |  2 +-
>  drivers/clocksource/moxart_timer.c        |  2 +-
>  drivers/clocksource/mps2-timer.c          |  2 +-
>  drivers/clocksource/mtk_timer.c           |  2 +-
>  drivers/clocksource/mxs_timer.c           |  2 +-
>  drivers/clocksource/nomadik-mtu.c         |  2 +-
>  drivers/clocksource/pxa_timer.c           |  2 +-
>  drivers/clocksource/qcom-timer.c          |  4 ++--
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----
>  drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
>  drivers/clocksource/sun4i_timer.c         |  2 +-
>  drivers/clocksource/tango_xtal.c          |  2 +-
>  drivers/clocksource/tegra20_timer.c       |  4 ++--
>  drivers/clocksource/time-armada-370-xp.c  |  6 +++---
>  drivers/clocksource/time-efm32.c          |  4 ++--
>  drivers/clocksource/time-lpc32xx.c        |  2 +-
>  drivers/clocksource/time-orion.c          |  2 +-
>  drivers/clocksource/time-pistachio.c      |  2 +-
>  drivers/clocksource/timer-atlas7.c        |  2 +-
>  drivers/clocksource/timer-atmel-pit.c     |  2 +-
>  drivers/clocksource/timer-atmel-st.c      |  2 +-
>  drivers/clocksource/timer-digicolor.c     |  2 +-
>  drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
>  drivers/clocksource/timer-integrator-ap.c |  2 +-
>  drivers/clocksource/timer-keystone.c      |  2 +-
>  drivers/clocksource/timer-nps.c           |  4 ++--
>  drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
>  drivers/clocksource/timer-prima2.c        |  2 +-
>  drivers/clocksource/timer-sp804.c         |  4 ++--
>  drivers/clocksource/timer-stm32.c         |  2 +-
>  drivers/clocksource/timer-sun5i.c         |  4 ++--
>  drivers/clocksource/timer-ti-32k.c        |  2 +-
>  drivers/clocksource/timer-u300.c          |  2 +-
>  drivers/clocksource/versatile.c           |  4 ++--
>  drivers/clocksource/vf_pit_timer.c        |  2 +-
>  drivers/clocksource/vt8500_timer.c        |  2 +-
>  drivers/clocksource/zevio-timer.c         |  2 +-
>  include/asm-generic/vmlinux.lds.h         |  2 --
>  include/linux/clocksource.h               |  5 +----
>  62 files changed, 98 insertions(+), 117 deletions(-)
> 

[...]

> diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
> index 0d99f40..bd887e2 100644
> --- a/drivers/clocksource/timer-oxnas-rps.c
> +++ b/drivers/clocksource/timer-oxnas-rps.c
> @@ -293,5 +293,5 @@ err_alloc:
>  	return ret;
>  }
>  
> -CLOCKSOURCE_OF_DECLARE_RET(ox810se_rps,
> -			   "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
> +CLOCKSOURCE_OF_DECLARE(ox810se_rps,
> +		       "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);

For the OXNAS part :

Acked-by: Neil Armstrong <narmstrong at baylibre.com>

Thanks,
Neil



From yt.shen at mediatek.com  Mon Jun 20 01:53:48 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Mon, 20 Jun 2016 16:53:48 +0800
Subject: [RFC v3 4/5] drm/mediatek: add support for Mediatek SoC MT2701
In-Reply-To: <1465797366.27253.2.camel@mtksdaap41>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
 <1465401825-43642-5-git-send-email-yt.shen@mediatek.com>
 <1465797366.27253.2.camel@mtksdaap41>
Message-ID: <1466412828.14292.6.camel@mtksdaap41>

Hi CK,

Thanks for your comments.

On Mon, 2016-06-13 at 13:56 +0800, CK Hu wrote:
> Hi, YT:
> 
> Some comments inline.
> 
> On Thu, 2016-06-09 at 00:03 +0800, YT Shen wrote:
> > This patch add support for the Mediatek MT2701 DISP subsystem.
> > There is only one OVL engine in MT2701.
> > 
> > Signed-off-by: YT Shen <yt.shen at mediatek.com>
> > ---
> >  drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    6 ++++
> >  drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    6 ++++
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp.c      |   42 +++++++++++++++++++++++++++
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |    8 +++++
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |    2 ++
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c      |   31 ++++++++++++++++++++
> >  6 files changed, 95 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> > index eb5c05e..1da0a71 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
> > @@ -286,11 +286,17 @@ static int mtk_disp_ovl_remove(struct platform_device *pdev)
> >  	return 0;
> >  }
> >  
> > +static const struct mtk_ddp_comp_driver_data mt2701_ovl_driver_data = {
> > +	.ovl = {0x0040, 1 << 12, 0}
> > +};
> > +
> >  static const struct mtk_ddp_comp_driver_data mt8173_ovl_driver_data = {
> >  	.ovl = {0x0f40, 0, 1 << 12}
> >  };
> >  
> >  static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-ovl",
> > +	  .data = &mt2701_ovl_driver_data},
> >  	{ .compatible = "mediatek,mt8173-disp-ovl",
> >  	  .data = &mt8173_ovl_driver_data},
> >  	{},
> > diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> > index fb0db50..506a353 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
> > @@ -225,11 +225,17 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev)
> >  	return 0;
> >  }
> >  
> > +static const struct mtk_ddp_comp_driver_data mt2701_rdma_driver_data = {
> > +	.rdma_fifo_pseudo_size = SZ_4K,
> > +};
> > +
> >  static const struct mtk_ddp_comp_driver_data mt8173_rdma_driver_data = {
> >  	.rdma_fifo_pseudo_size = SZ_8K,
> >  };
> >  
> >  static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-rdma",
> > +	  .data = &mt2701_rdma_driver_data},
> >  	{ .compatible = "mediatek,mt8173-disp-rdma",
> >  	  .data = &mt8173_rdma_driver_data},
> >  	{},
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > index fa53806..7ab6986 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > @@ -32,6 +32,10 @@
> >  #define DISP_REG_CONFIG_DISP_RDMA1_MOUT_EN	0x0c8
> >  #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
> >  
> > +#define DISP_REG_CONFIG_DISP_OVL_MOUT_EN	0x030
> > +#define DISP_REG_CONFIG_OUT_SEL			0x04c
> > +#define DISP_REG_CONFIG_DSI_SEL			0x050
> 
> Align the digital value.
OK.
> 
> > +
> >  #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
> >  #define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
> >  #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
> > @@ -54,6 +58,13 @@
> >  #define MT8173_MUTEX_MOD_DISP_PWM1		BIT(24)
> >  #define MT8173_MUTEX_MOD_DISP_OD		BIT(25)
> >  
> > +#define MT2701_MUTEX_MOD_DISP_OVL		BIT(3)
> > +#define MT2701_MUTEX_MOD_DISP_WDMA		BIT(6)
> > +#define MT2701_MUTEX_MOD_DISP_COLOR		BIT(7)
> > +#define MT2701_MUTEX_MOD_DISP_BLS		BIT(9)
> > +#define MT2701_MUTEX_MOD_DISP_RDMA0		BIT(10)
> > +#define MT2701_MUTEX_MOD_DISP_RDMA1		BIT(12)
> > +
> >  #define MUTEX_SOF_SINGLE_MODE		0
> >  #define MUTEX_SOF_DSI0			1
> >  #define MUTEX_SOF_DSI1			2
> > @@ -69,6 +80,10 @@
> >  #define DPI0_SEL_IN_RDMA1		0x1
> >  #define COLOR1_SEL_IN_OVL1		0x1
> >  
> > +#define OVL_MOUT_EN_RDMA		0x1
> > +#define BLS_TO_DSI_RDMA1_TO_DPI1	0x8
> > +#define DSI_SEL_IN_BLS			0x0
> > +
> >  struct mtk_disp_mutex {
> >  	int id;
> >  	bool claimed;
> > @@ -82,6 +97,15 @@ struct mtk_ddp {
> >  	const unsigned int		*mutex_mod;
> >  };
> >  
> > +static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> > +	[DDP_COMPONENT_BLS] = MT2701_MUTEX_MOD_DISP_BLS,
> > +	[DDP_COMPONENT_COLOR0] = MT2701_MUTEX_MOD_DISP_COLOR,
> > +	[DDP_COMPONENT_OVL0] = MT2701_MUTEX_MOD_DISP_OVL,
> > +	[DDP_COMPONENT_RDMA0] = MT2701_MUTEX_MOD_DISP_RDMA0,
> > +	[DDP_COMPONENT_RDMA1] = MT2701_MUTEX_MOD_DISP_RDMA1,
> > +	[DDP_COMPONENT_WDMA0] = MT2701_MUTEX_MOD_DISP_WDMA,
> > +};
> > +
> >  static const unsigned int mt8173_mutex_mod[DDP_COMPONENT_ID_MAX] = {
> >  	[DDP_COMPONENT_AAL] = MT8173_MUTEX_MOD_DISP_AAL,
> >  	[DDP_COMPONENT_COLOR0] = MT8173_MUTEX_MOD_DISP_COLOR0,
> > @@ -109,6 +133,9 @@ static unsigned int mtk_ddp_mout_en(enum mtk_ddp_comp_id cur,
> >  	if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_COLOR0) {
> >  		*addr = DISP_REG_CONFIG_DISP_OVL0_MOUT_EN;
> >  		value = OVL0_MOUT_EN_COLOR0;
> > +	} else if (cur == DDP_COMPONENT_OVL0 && next == DDP_COMPONENT_RDMA0) {
> > +		*addr = DISP_REG_CONFIG_DISP_OVL_MOUT_EN;
> > +		value = OVL_MOUT_EN_RDMA;
> >  	} else if (cur == DDP_COMPONENT_OD && next == DDP_COMPONENT_RDMA0) {
> >  		*addr = DISP_REG_CONFIG_DISP_OD_MOUT_EN;
> >  		value = OD_MOUT_EN_RDMA0;
> > @@ -146,6 +173,9 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
> >  	} else if (cur == DDP_COMPONENT_OVL1 && next == DDP_COMPONENT_COLOR1) {
> >  		*addr = DISP_REG_CONFIG_DISP_COLOR1_SEL_IN;
> >  		value = COLOR1_SEL_IN_OVL1;
> > +	} else if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
> > +		*addr = DISP_REG_CONFIG_DSI_SEL;
> > +		value = DSI_SEL_IN_BLS;
> >  	} else {
> >  		value = 0;
> >  	}
> > @@ -153,6 +183,15 @@ static unsigned int mtk_ddp_sel_in(enum mtk_ddp_comp_id cur,
> >  	return value;
> >  }
> >  
> > +static void mtk_ddp_sout_sel(void __iomem *config_regs,
> > +			    enum mtk_ddp_comp_id cur, enum mtk_ddp_comp_id next)
> > +{
> > +	if (cur == DDP_COMPONENT_BLS && next == DDP_COMPONENT_DSI0) {
> > +		writel_relaxed(BLS_TO_DSI_RDMA1_TO_DPI1,
> > +			       config_regs + DISP_REG_CONFIG_OUT_SEL);
> > +	}
> 
> Redundant parentheses.
OK, I will remove the redundant.
> 
> > +}
> > +
> >  void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
> >  			      enum mtk_ddp_comp_id cur,
> >  			      enum mtk_ddp_comp_id next)
> > @@ -165,6 +204,8 @@ void mtk_ddp_add_comp_to_path(void __iomem *config_regs,
> >  		writel_relaxed(reg, config_regs + addr);
> >  	}
> >  
> > +	mtk_ddp_sout_sel(config_regs, cur, next);
> > +
> >  	value = mtk_ddp_sel_in(cur, next, &addr);
> >  	if (value) {
> >  		reg = readl_relaxed(config_regs + addr) | value;
> > @@ -362,6 +403,7 @@ static int mtk_ddp_remove(struct platform_device *pdev)
> >  }
> >  
> >  static const struct of_device_id ddp_driver_dt_match[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-mutex", .data = mt2701_mutex_mod},
> >  	{ .compatible = "mediatek,mt8173-disp-mutex", .data = mt8173_mutex_mod},
> >  	{},
> >  };
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > index 4b4e449..87e0436 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
> > @@ -102,6 +102,7 @@ static const char * const mtk_ddp_comp_stem[MTK_DDP_COMP_TYPE_MAX] = {
> >  	[MTK_DISP_PWM] = "pwm",
> >  	[MTK_DISP_MUTEX] = "mutex",
> >  	[MTK_DISP_OD] = "od",
> > +	[MTK_DISP_BLS] = "bls",
> >  };
> >  
> >  struct mtk_ddp_comp_match {
> > @@ -112,6 +113,7 @@ struct mtk_ddp_comp_match {
> >  
> >  static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> >  	[DDP_COMPONENT_AAL]	= { MTK_DISP_AAL,	0, NULL },
> > +	[DDP_COMPONENT_BLS]	= { MTK_DISP_BLS,	0, NULL },
> >  	[DDP_COMPONENT_COLOR0]	= { MTK_DISP_COLOR,	0, &ddp_color },
> >  	[DDP_COMPONENT_COLOR1]	= { MTK_DISP_COLOR,	1, &ddp_color },
> >  	[DDP_COMPONENT_DPI0]	= { MTK_DPI,		0, NULL },
> > @@ -130,11 +132,17 @@ static const struct mtk_ddp_comp_match mtk_ddp_matches[DDP_COMPONENT_ID_MAX] = {
> >  	[DDP_COMPONENT_WDMA1]	= { MTK_DISP_WDMA,	1, NULL },
> >  };
> >  
> > +static const struct mtk_ddp_comp_driver_data mt2701_color_driver_data = {
> > +	.color_offset = 0x0f00,
> > +};
> > +
> >  static const struct mtk_ddp_comp_driver_data mt8173_color_driver_data = {
> >  	.color_offset = 0x0c00,
> >  };
> >  
> >  static const struct of_device_id mtk_disp_color_driver_dt_match[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-color",
> > +	  .data = &mt2701_color_driver_data},
> >  	{ .compatible = "mediatek,mt8173-disp-color",
> >  	  .data = &mt8173_color_driver_data},
> >  	{},
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > index 53065c7..6e2d918 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
> > @@ -35,6 +35,7 @@ enum mtk_ddp_comp_type {
> >  	MTK_DISP_PWM,
> >  	MTK_DISP_MUTEX,
> >  	MTK_DISP_OD,
> > +	MTK_DISP_BLS,
> >  	MTK_DDP_COMP_TYPE_MAX,
> >  };
> >  
> > @@ -57,6 +58,7 @@ enum mtk_ddp_comp_id {
> >  	DDP_COMPONENT_UFOE,
> >  	DDP_COMPONENT_WDMA0,
> >  	DDP_COMPONENT_WDMA1,
> > +	DDP_COMPONENT_BLS,
> >  	DDP_COMPONENT_ID_MAX,
> >  };
> >  
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > index e9eb535..b4b66a7 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > @@ -109,6 +109,19 @@ static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
> >  	.atomic_commit = mtk_atomic_commit,
> >  };
> >  
> > +static const enum mtk_ddp_comp_id mt2701_mtk_ddp_main[] = {
> > +	DDP_COMPONENT_OVL0,
> > +	DDP_COMPONENT_RDMA0,
> > +	DDP_COMPONENT_COLOR0,
> > +	DDP_COMPONENT_BLS,
> > +	DDP_COMPONENT_DSI0,
> > +};
> > +
> > +static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
> > +	DDP_COMPONENT_RDMA1,
> > +	DDP_COMPONENT_DPI0,
> > +};
> > +
> >  static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
> >  	DDP_COMPONENT_OVL0,
> >  	DDP_COMPONENT_COLOR0,
> > @@ -128,6 +141,14 @@ static const enum mtk_ddp_comp_id mt8173_mtk_ddp_ext[] = {
> >  	DDP_COMPONENT_DPI0,
> >  };
> >  
> > +static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
> > +	.main_path = mt2701_mtk_ddp_main,
> > +	.main_len = ARRAY_SIZE(mt2701_mtk_ddp_main),
> > +	.ext_path = mt2701_mtk_ddp_ext,
> > +	.ext_len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
> > +	.shadow_register = true,
> > +};
> > +
> >  static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
> >  	.main_path = mt8173_mtk_ddp_main,
> >  	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
> > @@ -332,18 +353,26 @@ static const struct component_master_ops mtk_drm_ops = {
> >  };
> >  
> >  static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
> > +	{ .compatible = "mediatek,mt2701-disp-ovl",   .data = (void *)MTK_DISP_OVL },
> >  	{ .compatible = "mediatek,mt8173-disp-ovl",   .data = (void *)MTK_DISP_OVL },
> > +	{ .compatible = "mediatek,mt2701-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
> >  	{ .compatible = "mediatek,mt8173-disp-rdma",  .data = (void *)MTK_DISP_RDMA },
> > +	{ .compatible = "mediatek,mt2701-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
> >  	{ .compatible = "mediatek,mt8173-disp-wdma",  .data = (void *)MTK_DISP_WDMA },
> > +	{ .compatible = "mediatek,mt2701-disp-color", .data = (void *)MTK_DISP_COLOR },
> >  	{ .compatible = "mediatek,mt8173-disp-color", .data = (void *)MTK_DISP_COLOR },
> >  	{ .compatible = "mediatek,mt8173-disp-aal",   .data = (void *)MTK_DISP_AAL},
> >  	{ .compatible = "mediatek,mt8173-disp-gamma", .data = (void *)MTK_DISP_GAMMA, },
> >  	{ .compatible = "mediatek,mt8173-disp-ufoe",  .data = (void *)MTK_DISP_UFOE },
> > +	{ .compatible = "mediatek,mt2701-dsi",	      .data = (void *)MTK_DSI },
> >  	{ .compatible = "mediatek,mt8173-dsi",        .data = (void *)MTK_DSI },
> > +	{ .compatible = "mediatek,mt2701-dpi",	      .data = (void *)MTK_DPI },
> >  	{ .compatible = "mediatek,mt8173-dpi",        .data = (void *)MTK_DPI },
> > +	{ .compatible = "mediatek,mt2701-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> >  	{ .compatible = "mediatek,mt8173-disp-mutex", .data = (void *)MTK_DISP_MUTEX },
> >  	{ .compatible = "mediatek,mt8173-disp-pwm",   .data = (void *)MTK_DISP_PWM },
> >  	{ .compatible = "mediatek,mt8173-disp-od",    .data = (void *)MTK_DISP_OD },
> > +	{ .compatible = "mediatek,mt2701-disp-bls",   .data = (void *)MTK_DISP_BLS },
> >  	{ }
> >  };
> >  
> > @@ -515,6 +544,8 @@ static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
> >  			 mtk_drm_sys_resume);
> >  
> >  static const struct of_device_id mtk_drm_of_ids[] = {
> > +	{ .compatible = "mediatek,mt2701-mmsys",
> > +	  .data = &mt2701_mmsys_driver_data},
> >  	{ .compatible = "mediatek,mt8173-mmsys",
> >  	  .data = &mt8173_mmsys_driver_data},
> >  	{ }
> 
> Regards,
> CK
> 




From yt.shen at mediatek.com  Mon Jun 20 01:51:12 2016
From: yt.shen at mediatek.com (YT Shen)
Date: Mon, 20 Jun 2016 16:51:12 +0800
Subject: [RFC v3 3/5] drm/mediatek: add shadow register support
In-Reply-To: <1465800183.27253.4.camel@mtksdaap41>
References: <1465401825-43642-1-git-send-email-yt.shen@mediatek.com>
 <1465401825-43642-4-git-send-email-yt.shen@mediatek.com>
 <1465800183.27253.4.camel@mtksdaap41>
Message-ID: <1466412672.14292.4.camel@mtksdaap41>

Hi CK,

On Mon, 2016-06-13 at 14:43 +0800, CK Hu wrote:
> Hi, YT:
> 
> One comment inline.
> 
> On Thu, 2016-06-09 at 00:03 +0800, YT Shen wrote:
> > We need to acquire mutex before using the resources,
> > and need to release it after finished.
> > So we don't need to write registers in the blanking period.
> > 
> > Signed-off-by: YT Shen <yt.shen at mediatek.com>
> > ---
> >  drivers/gpu/drm/mediatek/mtk_drm_crtc.c |   75 +++++++++++++++++++------------
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp.c  |   22 +++++++++
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp.h  |    2 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.c  |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_drv.h  |    1 +
> >  5 files changed, 72 insertions(+), 29 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > index 24aa3ba..80d9641 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
> > @@ -315,6 +315,42 @@ static void mtk_crtc_ddp_hw_fini(struct mtk_drm_crtc *mtk_crtc)
> >  	pm_runtime_put(drm->dev);
> >  }
> >  
> > +static void mtk_crtc_ddp_config(struct drm_crtc *crtc)
> > +{
> > +	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> > +	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
> > +	struct mtk_ddp_comp *ovl = mtk_crtc->ddp_comp[0];
> > +	unsigned int i;
> > +
> > +	/*
> > +	 * TODO: instead of updating the registers here, we should prepare
> > +	 * working registers in atomic_commit and let the hardware command
> > +	 * queue update module registers on vblank.
> > +	 */
> > +	if (state->pending_config) {
> > +		mtk_ddp_comp_config(ovl, state->pending_width,
> > +				    state->pending_height,
> > +				    state->pending_vrefresh);
> > +
> > +		state->pending_config = false;
> > +	}
> > +
> > +	if (mtk_crtc->pending_planes) {
> > +		for (i = 0; i < OVL_LAYER_NR; i++) {
> > +			struct drm_plane *plane = &mtk_crtc->planes[i].base;
> > +			struct mtk_plane_state *plane_state;
> > +
> > +			plane_state = to_mtk_plane_state(plane->state);
> > +
> > +			if (plane_state->pending.config) {
> > +				mtk_ddp_comp_layer_config(ovl, i, plane_state);
> > +				plane_state->pending.config = false;
> > +			}
> > +		}
> > +		mtk_crtc->pending_planes = false;
> > +	}
> > +}
> > +
> >  static void mtk_drm_crtc_enable(struct drm_crtc *crtc)
> >  {
> >  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> > @@ -391,6 +427,7 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
> >  				      struct drm_crtc_state *old_crtc_state)
> >  {
> >  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> > +	struct mtk_drm_private *priv = crtc->dev->dev_private;
> >  	unsigned int pending_planes = 0;
> >  	int i;
> >  
> > @@ -409,6 +446,12 @@ static void mtk_drm_crtc_atomic_flush(struct drm_crtc *crtc,
> >  	}
> >  	if (pending_planes)
> >  		mtk_crtc->pending_planes = true;
> > +
> > +	if (priv->data->shadow_register) {
> > +		mtk_disp_mutex_acquire(mtk_crtc->mutex);
> > +		mtk_crtc_ddp_config(crtc);
> > +		mtk_disp_mutex_release(mtk_crtc->mutex);
> > +	}
> >  }
> >  
> >  static const struct drm_crtc_funcs mtk_crtc_funcs = {
> > @@ -453,36 +496,10 @@ err_cleanup_crtc:
> >  void mtk_crtc_ddp_irq(struct drm_crtc *crtc, struct mtk_ddp_comp *ovl)
> >  {
> >  	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
> > -	struct mtk_crtc_state *state = to_mtk_crtc_state(mtk_crtc->base.state);
> > -	unsigned int i;
> > +	struct mtk_drm_private *priv = crtc->dev->dev_private;
> >  
> > -	/*
> > -	 * TODO: instead of updating the registers here, we should prepare
> > -	 * working registers in atomic_commit and let the hardware command
> > -	 * queue update module registers on vblank.
> > -	 */
> > -	if (state->pending_config) {
> > -		mtk_ddp_comp_config(ovl, state->pending_width,
> > -				    state->pending_height,
> > -				    state->pending_vrefresh);
> > -
> > -		state->pending_config = false;
> > -	}
> > -
> > -	if (mtk_crtc->pending_planes) {
> > -		for (i = 0; i < OVL_LAYER_NR; i++) {
> > -			struct drm_plane *plane = &mtk_crtc->planes[i].base;
> > -			struct mtk_plane_state *plane_state;
> > -
> > -			plane_state = to_mtk_plane_state(plane->state);
> > -
> > -			if (plane_state->pending.config) {
> > -				mtk_ddp_comp_layer_config(ovl, i, plane_state);
> > -				plane_state->pending.config = false;
> > -			}
> > -		}
> > -		mtk_crtc->pending_planes = false;
> > -	}
> > +	if (!priv->data->shadow_register)
> > +		mtk_crtc_ddp_config(crtc);
> >  
> >  	mtk_drm_finish_page_flip(mtk_crtc);
> >  }
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > index 8030769..fa53806 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.c
> > @@ -12,6 +12,7 @@
> >   */
> >  
> >  #include <linux/clk.h>
> > +#include <linux/iopoll.h>
> >  #include <linux/module.h>
> >  #include <linux/of_device.h>
> >  #include <linux/platform_device.h>
> > @@ -32,6 +33,7 @@
> >  #define DISP_REG_CONFIG_MMSYS_CG_CON0		0x100
> >  
> >  #define DISP_REG_MUTEX_EN(n)	(0x20 + 0x20 * (n))
> > +#define DISP_REG_MUTEX(n)	(0x24 + 0x20 * (n))
> >  #define DISP_REG_MUTEX_RST(n)	(0x28 + 0x20 * (n))
> >  #define DISP_REG_MUTEX_MOD(n)	(0x2c + 0x20 * (n))
> >  #define DISP_REG_MUTEX_SOF(n)	(0x30 + 0x20 * (n))
> > @@ -300,6 +302,26 @@ void mtk_disp_mutex_disable(struct mtk_disp_mutex *mutex)
> >  	writel(0, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
> >  }
> >  
> > +void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex)
> > +{
> > +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> > +					   mutex[mutex->id]);
> > +	u32 tmp;
> > +
> > +	writel(1, ddp->regs + DISP_REG_MUTEX_EN(mutex->id));
> > +	writel(1, ddp->regs + DISP_REG_MUTEX(mutex->id));
> > +	readl_poll_timeout_atomic(ddp->regs + DISP_REG_MUTEX(mutex->id), tmp,
> > +				  tmp & 0x2, 1, 10000);
> > +}
> > +
> > +void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex)
> > +{
> > +	struct mtk_ddp *ddp = container_of(mutex, struct mtk_ddp,
> > +					   mutex[mutex->id]);
> > +
> > +	writel(0, ddp->regs + DISP_REG_MUTEX(mutex->id));
> > +}
> > +
> >  static int mtk_ddp_probe(struct platform_device *pdev)
> >  {
> >  	struct device *dev = &pdev->dev;
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> > index 92c1175..f9a7991 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp.h
> > @@ -37,5 +37,7 @@ void mtk_disp_mutex_remove_comp(struct mtk_disp_mutex *mutex,
> >  				enum mtk_ddp_comp_id id);
> >  void mtk_disp_mutex_unprepare(struct mtk_disp_mutex *mutex);
> >  void mtk_disp_mutex_put(struct mtk_disp_mutex *mutex);
> > +void mtk_disp_mutex_acquire(struct mtk_disp_mutex *mutex);
> > +void mtk_disp_mutex_release(struct mtk_disp_mutex *mutex);
> >  
> >  #endif /* MTK_DRM_DDP_H */
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > index 80b4f54..e9eb535 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> > @@ -133,6 +133,7 @@ static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
> >  	.main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
> >  	.ext_path = mt8173_mtk_ddp_ext,
> >  	.ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
> > +	.shadow_register = false,
> 
> global data is initialized as zero.
OK, I will update it in the next version.

> 
> >  };
> >  
> >  static int mtk_drm_kms_init(struct drm_device *drm)
> > diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.h b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> > index fa0b106..94f8b66 100644
> > --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> > +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.h
> > @@ -33,6 +33,7 @@ struct mtk_mmsys_driver_data {
> >  	unsigned int main_len;
> >  	const enum mtk_ddp_comp_id *ext_path;
> >  	unsigned int ext_len;
> > +	bool shadow_register;
> >  };
> >  
> >  struct mtk_drm_private {
> 
> 




From ck.hu at mediatek.com  Mon Jun 20 03:41:51 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Mon, 20 Jun 2016 18:41:51 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
Message-ID: <1466419311.27959.12.camel@mtksdaap41>

Hi, HS:

One comment inline.

On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> CMDQ is used to help read/write registers with critical time limitation,
> such as updating display configuration during the vblank. It controls
> Global Command Engine (GCE) hardware to achieve this requirement.
> Currently, CMDQ only supports display related hardwares, but we expect
> it can be extended to other hardwares for future requirements.
> 
> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> ---
>  drivers/soc/mediatek/Kconfig    |  10 +
>  drivers/soc/mediatek/Makefile   |   1 +
>  drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/mediatek/cmdq.h     | 197 +++++++++
>  4 files changed, 1151 insertions(+)
>  create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
>  create mode 100644 include/soc/mediatek/cmdq.h
> 
> diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> index 0a4ea80..c4ad75c 100644
> --- a/drivers/soc/mediatek/Kconfig
> +++ b/drivers/soc/mediatek/Kconfig
> @@ -1,6 +1,16 @@
>  #
>  # MediaTek SoC drivers
>  #
> +config MTK_CMDQ
> +	bool "MediaTek CMDQ Support"
> +	depends on ARCH_MEDIATEK || COMPILE_TEST
> +	select MTK_INFRACFG
> +	help
> +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> +	  driver. The CMDQ is used to help read/write registers with critical
> +	  time limitation, such as updating display configuration during the
> +	  vblank.
> +
>  config MTK_INFRACFG
>  	bool "MediaTek INFRACFG Support"
>  	depends on ARCH_MEDIATEK || COMPILE_TEST
> diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> index 12998b0..f7397ef 100644
> --- a/drivers/soc/mediatek/Makefile
> +++ b/drivers/soc/mediatek/Makefile

[Snip...]

> +
> +static int cmdq_eng_get_thread(u64 flag)
> +{
> +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> +		return CMDQ_THR_DISP_MAIN_IDX;
> +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> +		return CMDQ_THR_DISP_SUB_IDX;
> +	else
> +		return CMDQ_THR_DISP_MISC_IDX;
> +}

I think cmdq should not have knowledge of client. These statement show
that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
the 'session' to replace engine flag. For example, main display create
one cmdq_session and external display create another cmdq_session. For
client driver, every tasks created by main display is bound to main
display session. For cmdq driver, it should dynamically bind a session
to a HW thread, and then dispatch tasks of this session to this HW
thread. After HW thread run out of tasks of this session, detach this
session with this thread.
For the problem of remove wfe cmd, I think a session can have a property
of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
For other client, it's false.

Here is the sample code to create cmdq_rec with session.

merge_wfe_cmd = true;
cmdq_session_create(merge_wfe_cmd, &primary_display_session);
cmdq_rec_create(dev, primary_display_session, &rec);


Therefore, the below definition can be removed.

> +
> +enum cmdq_eng {
> +	CMDQ_ENG_DISP_AAL,
> +	CMDQ_ENG_DISP_COLOR0,
> +	CMDQ_ENG_DISP_COLOR1,
> +	CMDQ_ENG_DISP_DPI0,
> +	CMDQ_ENG_DISP_DSI0,
> +	CMDQ_ENG_DISP_DSI1,
> +	CMDQ_ENG_DISP_GAMMA,
> +	CMDQ_ENG_DISP_OD,
> +	CMDQ_ENG_DISP_OVL0,
> +	CMDQ_ENG_DISP_OVL1,
> +	CMDQ_ENG_DISP_PWM0,
> +	CMDQ_ENG_DISP_PWM1,
> +	CMDQ_ENG_DISP_RDMA0,
> +	CMDQ_ENG_DISP_RDMA1,
> +	CMDQ_ENG_DISP_RDMA2,
> +	CMDQ_ENG_DISP_UFOE,
> +	CMDQ_ENG_DISP_WDMA0,
> +	CMDQ_ENG_DISP_WDMA1,
> +	CMDQ_ENG_MAX,
> +};
> +


Regards,
CK



From hs.liao at mediatek.com  Mon Jun 20 04:22:00 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Mon, 20 Jun 2016 19:22:00 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466419311.27959.12.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466419311.27959.12.camel@mtksdaap41>
Message-ID: <1466421720.1918.11.camel@mtksdaap41>

On Mon, 2016-06-20 at 18:41 +0800, CK Hu wrote:
> Hi, HS:
> 
> One comment inline.
> 
> On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> > This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> > CMDQ is used to help read/write registers with critical time limitation,
> > such as updating display configuration during the vblank. It controls
> > Global Command Engine (GCE) hardware to achieve this requirement.
> > Currently, CMDQ only supports display related hardwares, but we expect
> > it can be extended to other hardwares for future requirements.
> > 
> > Signed-off-by: HS Liao <hs.liao at mediatek.com>
> > Signed-off-by: CK Hu <ck.hu at mediatek.com>
> > ---
> >  drivers/soc/mediatek/Kconfig    |  10 +
> >  drivers/soc/mediatek/Makefile   |   1 +
> >  drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> >  include/soc/mediatek/cmdq.h     | 197 +++++++++
> >  4 files changed, 1151 insertions(+)
> >  create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> >  create mode 100644 include/soc/mediatek/cmdq.h
> > 
> > diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> > index 0a4ea80..c4ad75c 100644
> > --- a/drivers/soc/mediatek/Kconfig
> > +++ b/drivers/soc/mediatek/Kconfig
> > @@ -1,6 +1,16 @@
> >  #
> >  # MediaTek SoC drivers
> >  #
> > +config MTK_CMDQ
> > +	bool "MediaTek CMDQ Support"
> > +	depends on ARCH_MEDIATEK || COMPILE_TEST
> > +	select MTK_INFRACFG
> > +	help
> > +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> > +	  driver. The CMDQ is used to help read/write registers with critical
> > +	  time limitation, such as updating display configuration during the
> > +	  vblank.
> > +
> >  config MTK_INFRACFG
> >  	bool "MediaTek INFRACFG Support"
> >  	depends on ARCH_MEDIATEK || COMPILE_TEST
> > diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> > index 12998b0..f7397ef 100644
> > --- a/drivers/soc/mediatek/Makefile
> > +++ b/drivers/soc/mediatek/Makefile
> 
> [Snip...]
> 
> > +
> > +static int cmdq_eng_get_thread(u64 flag)
> > +{
> > +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> > +		return CMDQ_THR_DISP_MAIN_IDX;
> > +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> > +		return CMDQ_THR_DISP_SUB_IDX;
> > +	else
> > +		return CMDQ_THR_DISP_MISC_IDX;
> > +}
> 
> I think cmdq should not have knowledge of client. These statement show
> that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
> the 'session' to replace engine flag. For example, main display create
> one cmdq_session and external display create another cmdq_session. For
> client driver, every tasks created by main display is bound to main
> display session. For cmdq driver, it should dynamically bind a session
> to a HW thread, and then dispatch tasks of this session to this HW
> thread. After HW thread run out of tasks of this session, detach this
> session with this thread.
> For the problem of remove wfe cmd, I think a session can have a property
> of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
> For other client, it's false.

Hi CK,

I think your suggestion is similar to CMDQ 'scenarios',
which was removed from CMDQ v2.

Daniel suggests to use engine flags instead of scenarios.
Quote from https://patchwork.kernel.org/patch/8068311/ .
'Instead of encoding policy (these arbitrary "scenarios"), perhaps the
 cmdq driver should just provide these flag bits, and the display
 subsystem can use them to build the "flag" parameter itself.

 After all, the exact configuration of mmsys components is somewhat
 flexible.'

Therefore, it would be better to discuss with Daniel before we change
it.


Hi Daniel,

Do you think we should use scenarios or sessions instead of flags?

Thanks,
HS

> Here is the sample code to create cmdq_rec with session.
> 
> merge_wfe_cmd = true;
> cmdq_session_create(merge_wfe_cmd, &primary_display_session);
> cmdq_rec_create(dev, primary_display_session, &rec);
> 
> 
> Therefore, the below definition can be removed.
> 
> > +
> > +enum cmdq_eng {
> > +	CMDQ_ENG_DISP_AAL,
> > +	CMDQ_ENG_DISP_COLOR0,
> > +	CMDQ_ENG_DISP_COLOR1,
> > +	CMDQ_ENG_DISP_DPI0,
> > +	CMDQ_ENG_DISP_DSI0,
> > +	CMDQ_ENG_DISP_DSI1,
> > +	CMDQ_ENG_DISP_GAMMA,
> > +	CMDQ_ENG_DISP_OD,
> > +	CMDQ_ENG_DISP_OVL0,
> > +	CMDQ_ENG_DISP_OVL1,
> > +	CMDQ_ENG_DISP_PWM0,
> > +	CMDQ_ENG_DISP_PWM1,
> > +	CMDQ_ENG_DISP_RDMA0,
> > +	CMDQ_ENG_DISP_RDMA1,
> > +	CMDQ_ENG_DISP_RDMA2,
> > +	CMDQ_ENG_DISP_UFOE,
> > +	CMDQ_ENG_DISP_WDMA0,
> > +	CMDQ_ENG_DISP_WDMA1,
> > +	CMDQ_ENG_MAX,
> > +};
> > +
> 
> 
> Regards,
> CK
> 




From boris.brezillon at free-electrons.com  Mon Jun 20 04:54:11 2016
From: boris.brezillon at free-electrons.com (Boris Brezillon)
Date: Mon, 20 Jun 2016 13:54:11 +0200
Subject: [PATCH v5 0/2] MTK Smart Device Gen1 NAND Driver
In-Reply-To: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
Message-ID: <20160620135411.2ef381e0@bbrezillon>

On Tue, 14 Jun 2016 11:50:49 -0400
Jorge Ramirez-Ortiz <jorge.ramirez-ortiz at linaro.org> wrote:

>   Fixes/Changes on v5 relative to: 
>   --------------------------------
> 
>   tree		: https://github.com/bbrezillon/linux-0day
>   branch	: nand/next
>   commit	:
>  		'commit "mtd: nand: sunxi: fix return value check..."'
> 		 Author: Wei Yongjun <yongjun_wei at trendmicro.com.cn>
> 		 Date: Mon Jun 13 14:27:18 2016 +0000
> 
>   Patch v5
>   --------
>   mtd: mediatek: device tree bindings for MTK Smart Device Gen1 NAND
>        - remove unused property
>   
>   mtd: mediatek: driver for MTK Smart Device Gen1 NAND
>        - clean up macros and clarify semantics.
>        - replace codec requests with operations.
>        - replace semaphore with mutex on ecc accesses.
>        - addtional comments to some functions.
>        - explicit error for 16bits buswidth (not supported).
>        - resolve endian issues.
>        - determine active ecc codec by register inspection. 
> 
>   Patch v4
>   --------
>   mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
>        - nand-on-flash-bbt, nand-ecc-mode, nand-ecc-step-size and
>          nand-ecc-strength are now optional parameters
>        - spare-per-sector removed from DT settings.
>        - extend documentation.
> 
>   mtd: mediatek: driver for MTK Smart Device Gen1 NAND
>        - rename functions (semantic issues).
>        - add lock to ECC engine.
>        - rework ECC interface (single enable/disable function).
>        - rework bad block mark (now stored in OOB).
>        - fix stat reporting.
>        - rework FDM calculation.
>        - handle optional DT settings (strength, mode, ecc step size).
>        - spare per sector clamped to valid register values (hw spec).
> 		
>   Patch v3
>   --------
>   mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
> 	- rename nandc (nand controller)
> 
>   mtd: mediatek: driver for MTK Smart Device Gen1 NAND
>  	- rename source and header files
> 	- register definitions move to source files
> 	- ecc interface dropped
> 	- blank lines between functions (checkpatch.pl doesn't catch them)
> 	- ecc_check function renamed ecc_get_stats
> 	- ecc->dev assigned during probe()
> 	- generic ecc control function replaced by individual functions
> 	- required ecc parameters passed in ecc specific structures
> 	- fix memory corruption (size 0 array)
> 	- clarify nand offset/ptr function helpers (naming corrected)
> 	- move ecc config call to select_chip()
> 	- comment on hw issue workaround during read_byte
> 	- implement write_buf
> 	- remove switch_oob
> 	- fix suspend/resume operation
> 	- make nand-ecc-mode mandatory in device tree 
> 
>   RFC v2
>   ------
>  
>   mtd: mediatek: device tree docs for MTK Smart Device Gen1 NAND
> 	- split nand flash interface and ecc engine
> 
>   mtd: mediatek: driver for MTK Smart Device Gen1 NAND
> 	- split host and ecc in two separate drivers.
> 	- expose ecc layout through mtd_ooblayout_ops.
> 	- fix wait loops.
> 	- use device tree to describe ecc strength and ecc size.
> 	- use device tree to describe CS.
> 	- replace cmdfunc with cmd_ctrl.
> 	- fix read byte interface.
> 
>   dont enable controller in mt2701.dtsi
> 	- requires additional device tree changes. This will be done in
> 	  the near future.
> 
> Overview:
> =========
> 
> The following patch-set adds support for Mediatek's Smart Device
> Gen1 NAND controller.
> 
> * Controller expected layout:
> -----------------------------
> 
> The controller expects the OOB information relevant to each sector
> within the page to follow immediately the data field.
> 
> For example for a 4KiB page with 1KiB sectors and 224B of OOB information,
> the controller would expect the following layout.
> 
> -----------------------------------------------------
> | 1KiB | 56B | 1KiB | 56B | 1KiB | 56B | 1KiB | 56B |
> -----------------------------------------------------
> 
> This implies that the following actions are required:
> 
> - raw writes:
>   to match the controller expected layout, the driver uses the CPU and
>   a private bounce buffer to reassemble the page before the actual
>   writes.
>   
> - non raw writes:
>   ecc is hardware generated and data DMA'd in a single transfer.
> 
> Bad Block mark
> ==============
> The bad block mark is stored in the OOB region (one byte)
> 
> Controller limitations:
> =======================
> 
>  * dma chaning not supported:
> 	To guarantee that the pages corresponding to the buffers are contiguous
> 	in memory the driver enables NAND_USE_BOUNCE_BUFFER.
>  * spare per sector: 16B to 128B
>  * sector sizes: 512B or 1024B
>  * programable page sizes: 512B, 2KB, 4KB, 8K, 16K 
> 
> Tests:
> ======
> 
> * UBIFS support has been validated on 512MiB device (iozone, dd)
> * All drivers/mtd/tests/* pass clean.
> * Speed tests:
>   eraseblock write speed:	7563 KiB/s
>   eraseblock read speed:	10904 KiB/s
>   page write speed:		7497 KiB/s
>   page read speed:		10864 KiB/s
>   2 page write speed:		7527 KiB/s
>   2 page read speed:		10881 KiB/s
>   erase speed:			103157 KiB/s
>   2x multi-block erase speed:	357445 KiB/s
>   4x multi-block erase speed:	362944 KiB/s
>   8x multi-block erase speed:	363805 KiB/s
>   16x multi-block erase speed:	364237 KiB/s
>   32x multi-block erase speed:	364670 KiB/s
>   64x multi-block erase speed:	365104 KiB/s

Applied after fixing a few coding style issues.

Thanks,

Boris

> 
> Jorge Ramirez-Ortiz (2):
>   mtd: mediatek: device tree bindings for MTK Smart Device Gen1 NAND
>   mtd: mediatek: driver for MTK Smart Device Gen1 NAND
> 
>  Documentation/devicetree/bindings/mtd/mtk-nand.txt |  160 +++
>  drivers/mtd/nand/Kconfig                           |    7 +
>  drivers/mtd/nand/Makefile                          |    1 +
>  drivers/mtd/nand/mtk_ecc.c                         |  528 +++++++
>  drivers/mtd/nand/mtk_ecc.h                         |   50 +
>  drivers/mtd/nand/mtk_nand.c                        | 1509 ++++++++++++++++++++
>  6 files changed, 2255 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mtd/mtk-nand.txt
>  create mode 100644 drivers/mtd/nand/mtk_ecc.c
>  create mode 100644 drivers/mtd/nand/mtk_ecc.h
>  create mode 100644 drivers/mtd/nand/mtk_nand.c
> 



From p.zabel at pengutronix.de  Mon Jun 20 05:00:03 2016
From: p.zabel at pengutronix.de (Philipp Zabel)
Date: Mon, 20 Jun 2016 14:00:03 +0200
Subject: [GIT PULL v2] drm/mediatek: MT8173 HDMI support
Message-ID: <1466424003.6070.33.camel@pengutronix.de>

Hi Dave,

please consider merging this tag, which contains the v16 MT8173 HDMI
patches I sent on 2016-05-26, rebased onto v4.7-rc2, with two warnings
fixed.

regards
Philipp

The following changes since commit af8c34ce6ae32addda3788d54a7e340cad22516b:

  Linux 4.7-rc2 (2016-06-05 14:31:26 -0700)

are available in the git repository at:

  git://git.pengutronix.de/git/pza/linux.git tags/mediatek-drm-2016-06-20

for you to fetch changes up to 9a449e0de0d708bae7af7f897c1a67848f91c455:

  dt-bindings: hdmi-connector: add DDC I2C bus phandle documentation (2016-06-16 16:30:39 +0200)

----------------------------------------------------------------
MT8173 HDMI support

- device tree binding documentation for MT8173 HDMI encoder, CEC, DDC,
  and PHY
- drivers for MT8173 HDMI encoder, CEC (HPD only for now), DDC, and PHY
- enable HDMI output via a custom SMCCC call
- add ddc-i2c-bus property to HDMI connector device tree binding

----------------------------------------------------------------
Jie Qiu (2):
      drm/mediatek: Add HDMI support
      drm/mediatek: enable hdmi output control bit

Philipp Zabel (2):
      dt-bindings: drm/mediatek: Add Mediatek HDMI dts binding
      dt-bindings: hdmi-connector: add DDC I2C bus phandle documentation

 .../bindings/display/connector/hdmi-connector.txt  |    1 +
 .../bindings/display/mediatek/mediatek,hdmi.txt    |  148 ++
 drivers/gpu/drm/mediatek/Kconfig                   |    8 +
 drivers/gpu/drm/mediatek/Makefile                  |    7 +
 drivers/gpu/drm/mediatek/mtk_cec.c                 |  265 +++
 drivers/gpu/drm/mediatek/mtk_cec.h                 |   26 +
 drivers/gpu/drm/mediatek/mtk_hdmi.c                | 1828 ++++++++++++++++++++
 drivers/gpu/drm/mediatek/mtk_hdmi.h                |   23 +
 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c            |  358 ++++
 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h           |  238 +++
 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c     |  515 ++++++
 11 files changed, 3417 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/display/mediatek/mediatek,hdmi.txt
 create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c
 create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
 create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c



From jorge.ramirez-ortiz at linaro.org  Mon Jun 20 07:17:55 2016
From: jorge.ramirez-ortiz at linaro.org (Jorge Ramirez)
Date: Mon, 20 Jun 2016 10:17:55 -0400
Subject: [PATCH v5 0/2] MTK Smart Device Gen1 NAND Driver
In-Reply-To: <20160620135411.2ef381e0@bbrezillon>
References: <1465919451-1891-1-git-send-email-jorge.ramirez-ortiz@linaro.org>
 <20160620135411.2ef381e0@bbrezillon>
Message-ID: <5767FB13.5070503@linaro.org>

On 06/20/2016 07:54 AM, Boris Brezillon wrote:
>> >Tests:
>> >======
>> >
>> >* UBIFS support has been validated on 512MiB device (iozone, dd)
>> >* All drivers/mtd/tests/* pass clean.
>> >* Speed tests:
>> >   eraseblock write speed:	7563 KiB/s
>> >   eraseblock read speed:	10904 KiB/s
>> >   page write speed:		7497 KiB/s
>> >   page read speed:		10864 KiB/s
>> >   2 page write speed:		7527 KiB/s
>> >   2 page read speed:		10881 KiB/s
>> >   erase speed:			103157 KiB/s
>> >   2x multi-block erase speed:	357445 KiB/s
>> >   4x multi-block erase speed:	362944 KiB/s
>> >   8x multi-block erase speed:	363805 KiB/s
>> >   16x multi-block erase speed:	364237 KiB/s
>> >   32x multi-block erase speed:	364670 KiB/s
>> >   64x multi-block erase speed:	365104 KiB/s
> Applied after fixing a few coding style issues.
>

Awesome.
Thanks for all the reviews and these additional fixes!

jorge





From slemieux.tyco at gmail.com  Mon Jun 20 10:03:27 2016
From: slemieux.tyco at gmail.com (Sylvain Lemieux)
Date: Mon, 20 Jun 2016 13:03:27 -0400
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1466442207.1887.3.camel@localhost>

On Thu, 2016-06-16 at 23:27 +0200, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  arch/arc/kernel/time.c                    |  6 +++---
>  arch/arm/kernel/smp_twd.c                 |  6 +++---
>  arch/microblaze/kernel/timer.c            |  2 +-
>  arch/mips/ralink/cevt-rt3352.c            |  2 +-
>  arch/nios2/kernel/time.c                  |  2 +-
>  drivers/clocksource/arm_arch_timer.c      |  6 +++---
>  drivers/clocksource/arm_global_timer.c    |  2 +-
>  drivers/clocksource/armv7m_systick.c      |  2 +-
>  drivers/clocksource/asm9260_timer.c       |  2 +-
>  drivers/clocksource/bcm2835_timer.c       |  2 +-
>  drivers/clocksource/bcm_kona_timer.c      |  4 ++--
>  drivers/clocksource/cadence_ttc_timer.c   |  2 +-
>  drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
>  drivers/clocksource/clksrc-probe.c        | 14 --------------
>  drivers/clocksource/clksrc_st_lpc.c       |  2 +-
>  drivers/clocksource/clps711x-timer.c      |  2 +-
>  drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
>  drivers/clocksource/exynos_mct.c          |  4 ++--
>  drivers/clocksource/fsl_ftm_timer.c       |  2 +-
>  drivers/clocksource/h8300_timer16.c       |  2 +-
>  drivers/clocksource/h8300_timer8.c        |  2 +-
>  drivers/clocksource/h8300_tpu.c           |  2 +-
>  drivers/clocksource/meson6_timer.c        |  2 +-
>  drivers/clocksource/mips-gic-timer.c      |  2 +-
>  drivers/clocksource/moxart_timer.c        |  2 +-
>  drivers/clocksource/mps2-timer.c          |  2 +-
>  drivers/clocksource/mtk_timer.c           |  2 +-
>  drivers/clocksource/mxs_timer.c           |  2 +-
>  drivers/clocksource/nomadik-mtu.c         |  2 +-
>  drivers/clocksource/pxa_timer.c           |  2 +-
>  drivers/clocksource/qcom-timer.c          |  4 ++--
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----
>  drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
>  drivers/clocksource/sun4i_timer.c         |  2 +-
>  drivers/clocksource/tango_xtal.c          |  2 +-
>  drivers/clocksource/tegra20_timer.c       |  4 ++--
>  drivers/clocksource/time-armada-370-xp.c  |  6 +++---
>  drivers/clocksource/time-efm32.c          |  4 ++--
>  drivers/clocksource/time-lpc32xx.c        |  2 +-
>  drivers/clocksource/time-orion.c          |  2 +-
>  drivers/clocksource/time-pistachio.c      |  2 +-
>  drivers/clocksource/timer-atlas7.c        |  2 +-
>  drivers/clocksource/timer-atmel-pit.c     |  2 +-
>  drivers/clocksource/timer-atmel-st.c      |  2 +-
>  drivers/clocksource/timer-digicolor.c     |  2 +-
>  drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
>  drivers/clocksource/timer-integrator-ap.c |  2 +-
>  drivers/clocksource/timer-keystone.c      |  2 +-
>  drivers/clocksource/timer-nps.c           |  4 ++--
>  drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
>  drivers/clocksource/timer-prima2.c        |  2 +-
>  drivers/clocksource/timer-sp804.c         |  4 ++--
>  drivers/clocksource/timer-stm32.c         |  2 +-
>  drivers/clocksource/timer-sun5i.c         |  4 ++--
>  drivers/clocksource/timer-ti-32k.c        |  2 +-
>  drivers/clocksource/timer-u300.c          |  2 +-
>  drivers/clocksource/versatile.c           |  4 ++--
>  drivers/clocksource/vf_pit_timer.c        |  2 +-
>  drivers/clocksource/vt8500_timer.c        |  2 +-
>  drivers/clocksource/zevio-timer.c         |  2 +-
>  include/asm-generic/vmlinux.lds.h         |  2 --
>  include/linux/clocksource.h               |  5 +----
>  62 files changed, 98 insertions(+), 117 deletions(-)
> 
[..]

> diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
> index cb5b866..9649cfd 100644
> --- a/drivers/clocksource/time-lpc32xx.c
> +++ b/drivers/clocksource/time-lpc32xx.c
> @@ -311,4 +311,4 @@ static int __init lpc32xx_timer_init(struct device_node *np)
>  
>  	return ret;
>  }
> -CLOCKSOURCE_OF_DECLARE_RET(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
> +CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);

For LPC32xx driver:
Acked-by: Sylvain Lemieux <slemieux.tyco at gmail.com>




From matthias.bgg at gmail.com  Mon Jun 20 10:09:15 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 20 Jun 2016 19:09:15 +0200
Subject: [PATCH 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <1466414350.3378.9.camel@mtkswgap22>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
 <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>
 <5767A9FB.3010007@gmail.com> <1466414350.3378.9.camel@mtkswgap22>
Message-ID: <5768233B.8080109@gmail.com>



On 20/06/16 11:19, Mars Cheng wrote:
> On Mon, 2016-06-20 at 10:31 +0200, Matthias Brugger wrote:
>>
>> On 14/06/16 04:20, Mars Cheng wrote:
>>> This adds basic chip support for MT6755 SoC.
>>>
>>> Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
>>> ---
>>>    arch/arm64/boot/dts/mediatek/Makefile         |    1 +
>>>    arch/arm64/boot/dts/mediatek/mt6755-phone.dts |   39 +++++++
>>>    arch/arm64/boot/dts/mediatek/mt6755.dtsi      |  143 +++++++++++++++++++++++++
>>>    3 files changed, 183 insertions(+)
>>>    create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-phone.dts
>>>    create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi
>>>
>>> diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
>>> index e0a4bff..9abdb01 100644
>>> --- a/arch/arm64/boot/dts/mediatek/Makefile
>>> +++ b/arch/arm64/boot/dts/mediatek/Makefile
>>> @@ -1,3 +1,4 @@
>>> +dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-phone.dtb
>>
>> I suppose this is a evaluation platform? Is this different to the EVB
>> for this SoC? Or will the only phone build with this SoC be this one?
>>
>
> Yes, this is an evaluation platform which is an internal demo phone.
> No difference to EVB. Would you suggest I change the name? If so, I will
> change it to mt6755-evb.dtb. So will the compatible string in it.
>

Sounds good.

>> Thanks,
>> Matthias
>>
>>>    dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
>>>    dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
>>>
>>> diff --git a/arch/arm64/boot/dts/mediatek/mt6755-phone.dts b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
>>> new file mode 100644
>>> index 0000000..f551666
>>> --- /dev/null
>>> +++ b/arch/arm64/boot/dts/mediatek/mt6755-phone.dts
>>> @@ -0,0 +1,39 @@
>>> +/*
>>> + * Copyright (c) 2016 MediaTek Inc.
>>> + * Author: Mars.C <mars.cheng at mediatek.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +/dts-v1/;
>>> +#include "mt6755.dtsi"
>>> +
>>> +/ {
>>> +	model = "MediaTek MT6755 phone";
>>> +	compatible = "mediatek,mt6755-phone", "mediatek,mt6755";
>>> +
>>> +	aliases {
>>> +		serial0 = &uart0;
>>> +		serial1 = &uart1;
>>
>> we don't enable uart1, so we don't need the alias.
>
> OK, will fix this.
>
>>
>>> +	};
>>> +
>>> +	memory at 40000000 {
>>> +		device_type = "memory";
>>> +		reg = <0 0x40000000 0 0x1e800000>;
>>> +	};
>>> +
>>> +	chosen {
>>> +		stdout-path = "serial0:921600n8";
>>> +	};
>>> +};
>>> +
>>> +&uart0 {
>>> +	status = "okay";
>>> +};
>>> diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
>>> new file mode 100644
>>> index 0000000..36c3384
>>> --- /dev/null
>>> +++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
>>> @@ -0,0 +1,143 @@
>>> +/*
>>> + * Copyright (c) 2016 MediaTek Inc.
>>> + * Author: Mars.C <mars.cheng at mediatek.com>
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful,
>>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>>> + * GNU General Public License for more details.
>>> + */
>>> +
>>> +#include <dt-bindings/interrupt-controller/irq.h>
>>> +#include <dt-bindings/interrupt-controller/arm-gic.h>
>>> +
>>> +/ {
>>> +	compatible = "mediatek,mt6755";
>>> +	interrupt-parent = <&sysirq>;
>>> +	#address-cells = <2>;
>>> +	#size-cells = <2>;
>>> +
>>> +	psci {
>>> +		compatible = "arm,psci-0.2";
>>> +		method = "smc";
>>> +	};
>>> +
>>> +	cpus {
>>> +		#address-cells = <1>;
>>> +		#size-cells = <0>;
>>> +
>>> +		cpu0: cpu at 0 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x000>;
>>> +		};
>>> +
>>> +		cpu1: cpu at 1 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x001>;
>>> +		};
>>> +
>>> +		cpu2: cpu at 2 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x002>;
>>> +		};
>>> +
>>> +		cpu3: cpu at 3 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x003>;
>>> +		};
>>> +
>>> +		cpu4: cpu at 100 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x100>;
>>> +		};
>>> +
>>> +		cpu5: cpu at 101 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x101>;
>>> +		};
>>> +
>>> +		cpu6: cpu at 102 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x102>;
>>> +		};
>>> +
>>> +		cpu7: cpu at 103 {
>>> +			device_type = "cpu";
>>> +			compatible = "arm,cortex-a53";
>>> +			enable-method = "psci";
>>> +			reg = <0x103>;
>>> +		};
>>> +	};
>>> +
>>> +	uart_clk: dummy26m {
>>> +		compatible = "fixed-clock";
>>> +		clock-frequency = <26000000>;
>>> +		#clock-cells = <0>;
>>> +	};
>>> +
>>
>> We can do that, but I would prefer to see the clock driver early. So
>> that the DTS we carry around as complete as possible.
>>
>
> OK, I will merge the clk later. However, the clk driver would be
> submmited later, is that OK? Or would you prefer sunmmit clk driver
> together?
>

The uart won't work without any clock node. It is OK like this. I just 
wanted to emphasis that we should try to get the clock driver accpeted 
early in the effort.

Thanks,
Matthias

>>> +	timer {
>>> +		compatible = "arm,armv8-timer";
>>> +		interrupt-parent = <&gic>;
>>> +		interrupts = <GIC_PPI 13
>>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
>>> +			     <GIC_PPI 14
>>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
>>> +			     <GIC_PPI 11
>>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
>>> +			     <GIC_PPI 10
>>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
>>> +	};
>>> +
>>> +	sysirq: intpol-controller at 10200620 {
>>> +		compatible = "mediatek,mt6755-sysirq",
>>> +			     "mediatek,mt6577-sysirq";
>>> +		interrupt-controller;
>>> +		#interrupt-cells = <3>;
>>> +		interrupt-parent = <&gic>;
>>> +		reg = <0 0x10200620 0 0x20>;
>>> +	};
>>> +
>>> +	gic: interrupt-controller at 10231000 {
>>> +		compatible = "arm,gic-400";
>>> +		#interrupt-cells = <3>;
>>> +		interrupt-parent = <&gic>;
>>> +		interrupt-controller;
>>> +		reg = <0 0x10231000 0 0x1000>,
>>> +		      <0 0x10232000 0 0x2000>,
>>> +		      <0 0x10234000 0 0x2000>,
>>> +		      <0 0x10236000 0 0x2000>;
>>> +	};
>>> +
>>> +	uart0: serial at 11002000 {
>>> +		compatible = "mediatek,mt6755-uart",
>>> +			     "mediatek,mt6577-uart";
>>> +		reg = <0 0x11002000 0 0x400>;
>>> +		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
>>> +		clocks = <&uart_clk>;
>>
>> status = "disabled";
>
> OK, will fix this.
>>
>>> +	};
>>> +
>>> +	uart1: serial at 11003000 {
>>> +		compatible = "mediatek,mt6755-uart",
>>> +			     "mediatek,mt6577-uart";
>>> +		reg = <0 0x11003000 0 0x400>;
>>> +		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
>>> +		clocks = <&uart_clk>;
>>
>> same here.
>
> OK, will fix this.
>
> Thanks.
>>
>> Regards,
>> Matthias
>
>


From mars.cheng at mediatek.com  Mon Jun 20 17:50:18 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 21 Jun 2016 08:50:18 +0800
Subject: [PATCH 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <5768233B.8080109@gmail.com>
References: <1465870805-10451-1-git-send-email-mars.cheng@mediatek.com>
 <1465870805-10451-3-git-send-email-mars.cheng@mediatek.com>
 <5767A9FB.3010007@gmail.com> <1466414350.3378.9.camel@mtkswgap22>
 <5768233B.8080109@gmail.com>
Message-ID: <1466470218.863.4.camel@mtkswgap22>

On Mon, 2016-06-20 at 19:09 +0200, Matthias Brugger wrote:

> >>> +	uart_clk: dummy26m {
> >>> +		compatible = "fixed-clock";
> >>> +		clock-frequency = <26000000>;
> >>> +		#clock-cells = <0>;
> >>> +	};
> >>> +
> >>
> >> We can do that, but I would prefer to see the clock driver early. So
> >> that the DTS we carry around as complete as possible.
> >>
> >
> > OK, I will merge the clk later. However, the clk driver would be
> > submmited later, is that OK? Or would you prefer sunmmit clk driver
> > together?
> >
> 
> The uart won't work without any clock node. It is OK like this. I just 
> wanted to emphasis that we should try to get the clock driver accpeted 
> early in the effort.
> 
> Thanks,
> Matthias

Got it. I will not include the clk dts & driver in basic chip patch
v2.We will submmit clk driver soon after this basic chip support patch.

Thanks.

> 
> >>> +	timer {
> >>> +		compatible = "arm,armv8-timer";
> >>> +		interrupt-parent = <&gic>;
> >>> +		interrupts = <GIC_PPI 13
> >>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> >>> +			     <GIC_PPI 14
> >>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> >>> +			     <GIC_PPI 11
> >>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
> >>> +			     <GIC_PPI 10
> >>> +			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
> >>> +	};
> >>> +
> >>> +	sysirq: intpol-controller at 10200620 {
> >>> +		compatible = "mediatek,mt6755-sysirq",
> >>> +			     "mediatek,mt6577-sysirq";
> >>> +		interrupt-controller;
> >>> +		#interrupt-cells = <3>;
> >>> +		interrupt-parent = <&gic>;
> >>> +		reg = <0 0x10200620 0 0x20>;
> >>> +	};
> >>> +
> >>> +	gic: interrupt-controller at 10231000 {
> >>> +		compatible = "arm,gic-400";
> >>> +		#interrupt-cells = <3>;
> >>> +		interrupt-parent = <&gic>;
> >>> +		interrupt-controller;
> >>> +		reg = <0 0x10231000 0 0x1000>,
> >>> +		      <0 0x10232000 0 0x2000>,
> >>> +		      <0 0x10234000 0 0x2000>,
> >>> +		      <0 0x10236000 0 0x2000>;
> >>> +	};
> >>> +
> >>> +	uart0: serial at 11002000 {
> >>> +		compatible = "mediatek,mt6755-uart",
> >>> +			     "mediatek,mt6577-uart";
> >>> +		reg = <0 0x11002000 0 0x400>;
> >>> +		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
> >>> +		clocks = <&uart_clk>;
> >>
> >> status = "disabled";
> >
> > OK, will fix this.
> >>
> >>> +	};
> >>> +
> >>> +	uart1: serial at 11003000 {
> >>> +		compatible = "mediatek,mt6755-uart",
> >>> +			     "mediatek,mt6577-uart";
> >>> +		reg = <0 0x11003000 0 0x400>;
> >>> +		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
> >>> +		clocks = <&uart_clk>;
> >>
> >> same here.
> >
> > OK, will fix this.
> >
> > Thanks.
> >>
> >> Regards,
> >> Matthias
> >
> >




From ck.hu at mediatek.com  Mon Jun 20 19:03:00 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Tue, 21 Jun 2016 10:03:00 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466421720.1918.11.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466419311.27959.12.camel@mtksdaap41>
 <1466421720.1918.11.camel@mtksdaap41>
Message-ID: <1466474580.6214.29.camel@mtksdaap41>

On Mon, 2016-06-20 at 19:22 +0800, Horng-Shyang Liao wrote:
> On Mon, 2016-06-20 at 18:41 +0800, CK Hu wrote:
> > Hi, HS:
> > 
> > One comment inline.
> > 
> > On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> > > This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> > > CMDQ is used to help read/write registers with critical time limitation,
> > > such as updating display configuration during the vblank. It controls
> > > Global Command Engine (GCE) hardware to achieve this requirement.
> > > Currently, CMDQ only supports display related hardwares, but we expect
> > > it can be extended to other hardwares for future requirements.
> > > 
> > > Signed-off-by: HS Liao <hs.liao at mediatek.com>
> > > Signed-off-by: CK Hu <ck.hu at mediatek.com>
> > > ---
> > >  drivers/soc/mediatek/Kconfig    |  10 +
> > >  drivers/soc/mediatek/Makefile   |   1 +
> > >  drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> > >  include/soc/mediatek/cmdq.h     | 197 +++++++++
> > >  4 files changed, 1151 insertions(+)
> > >  create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> > >  create mode 100644 include/soc/mediatek/cmdq.h
> > > 
> > > diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> > > index 0a4ea80..c4ad75c 100644
> > > --- a/drivers/soc/mediatek/Kconfig
> > > +++ b/drivers/soc/mediatek/Kconfig
> > > @@ -1,6 +1,16 @@
> > >  #
> > >  # MediaTek SoC drivers
> > >  #
> > > +config MTK_CMDQ
> > > +	bool "MediaTek CMDQ Support"
> > > +	depends on ARCH_MEDIATEK || COMPILE_TEST
> > > +	select MTK_INFRACFG
> > > +	help
> > > +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> > > +	  driver. The CMDQ is used to help read/write registers with critical
> > > +	  time limitation, such as updating display configuration during the
> > > +	  vblank.
> > > +
> > >  config MTK_INFRACFG
> > >  	bool "MediaTek INFRACFG Support"
> > >  	depends on ARCH_MEDIATEK || COMPILE_TEST
> > > diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> > > index 12998b0..f7397ef 100644
> > > --- a/drivers/soc/mediatek/Makefile
> > > +++ b/drivers/soc/mediatek/Makefile
> > 
> > [Snip...]
> > 
> > > +
> > > +static int cmdq_eng_get_thread(u64 flag)
> > > +{
> > > +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> > > +		return CMDQ_THR_DISP_MAIN_IDX;
> > > +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> > > +		return CMDQ_THR_DISP_SUB_IDX;
> > > +	else
> > > +		return CMDQ_THR_DISP_MISC_IDX;
> > > +}
> > 
> > I think cmdq should not have knowledge of client. These statement show
> > that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
> > the 'session' to replace engine flag. For example, main display create
> > one cmdq_session and external display create another cmdq_session. For
> > client driver, every tasks created by main display is bound to main
> > display session. For cmdq driver, it should dynamically bind a session
> > to a HW thread, and then dispatch tasks of this session to this HW
> > thread. After HW thread run out of tasks of this session, detach this
> > session with this thread.
> > For the problem of remove wfe cmd, I think a session can have a property
> > of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
> > For other client, it's false.
> 
> Hi CK,
> 
> I think your suggestion is similar to CMDQ 'scenarios',
> which was removed from CMDQ v2.
> 
> Daniel suggests to use engine flags instead of scenarios.
> Quote from https://patchwork.kernel.org/patch/8068311/ .
> 'Instead of encoding policy (these arbitrary "scenarios"), perhaps the
>  cmdq driver should just provide these flag bits, and the display
>  subsystem can use them to build the "flag" parameter itself.
> 
>  After all, the exact configuration of mmsys components is somewhat
>  flexible.'
> 
> Therefore, it would be better to discuss with Daniel before we change
> it.
> 
> 
> Hi Daniel,
> 
> Do you think we should use scenarios or sessions instead of flags?
> 
> Thanks,
> HS
> 

Hi, HS:

'session' is not similar to 'scenarios'.

In 'scenarios' mechanism, client bind a task with scenarios and send to
cmdq. Cmdq transfer scenarios to engine flag, and use engine flag to
dispatch task to HW thread.


In 'engine flag' mechanism, proposed by Daniel, client bind a task
directly with engine flag. Cmdq directly use engine flag to dispatch
task to HW thread without any translation.

But neither 'scenarios' mechanism nor 'engine flag' mechanism get rid of
engine flag, which make cmdq have knowledge of client.

In 'session' mechanism, there is no engine flag any more. Client bind
time-sequential tasks to the same session and tasks in different session
can execute parallelly. One thing cmdq need to know is to dispatch tasks
with the same session to the same HW thread, so cmdq does not have any
knowledge of client.

Daniel focus on reduce translating scenarios to engine flag. I think
'session' mechanism does not conflict with his opinion because we does
not translate 'session' to engine flag. Therefore, I think 'session' is
the best of these three mechanism.

Regards,
CK

> > Here is the sample code to create cmdq_rec with session.
> > 
> > merge_wfe_cmd = true;
> > cmdq_session_create(merge_wfe_cmd, &primary_display_session);
> > cmdq_rec_create(dev, primary_display_session, &rec);
> > 
> > 
> > Therefore, the below definition can be removed.
> > 
> > > +
> > > +enum cmdq_eng {
> > > +	CMDQ_ENG_DISP_AAL,
> > > +	CMDQ_ENG_DISP_COLOR0,
> > > +	CMDQ_ENG_DISP_COLOR1,
> > > +	CMDQ_ENG_DISP_DPI0,
> > > +	CMDQ_ENG_DISP_DSI0,
> > > +	CMDQ_ENG_DISP_DSI1,
> > > +	CMDQ_ENG_DISP_GAMMA,
> > > +	CMDQ_ENG_DISP_OD,
> > > +	CMDQ_ENG_DISP_OVL0,
> > > +	CMDQ_ENG_DISP_OVL1,
> > > +	CMDQ_ENG_DISP_PWM0,
> > > +	CMDQ_ENG_DISP_PWM1,
> > > +	CMDQ_ENG_DISP_RDMA0,
> > > +	CMDQ_ENG_DISP_RDMA1,
> > > +	CMDQ_ENG_DISP_RDMA2,
> > > +	CMDQ_ENG_DISP_UFOE,
> > > +	CMDQ_ENG_DISP_WDMA0,
> > > +	CMDQ_ENG_DISP_WDMA1,
> > > +	CMDQ_ENG_MAX,
> > > +};
> > > +
> > 
> > 
> > Regards,
> > CK
> > 
> 
> 




From hs.liao at mediatek.com  Mon Jun 20 22:52:38 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Tue, 21 Jun 2016 13:52:38 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <57641E01.3070205@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41>
 <1466152107.11184.14.camel@mtksdaap41> <57641E01.3070205@gmail.com>
Message-ID: <1466488358.8045.19.camel@mtksdaap41>

On Fri, 2016-06-17 at 17:57 +0200, Matthias Brugger wrote:
> 
> On 17/06/16 10:28, Horng-Shyang Liao wrote:
> > Hi Matthias,
> >
> > On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
> >> Hi Matthias,
> >>
> >> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
> >>>
> >>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
> >>>> Hi Matthias,
> >>>>
> >>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
> >>>>>
> >>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
> >>>>>> Hi Matthias,
> >>>>>>
> >>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> >>>>>>>
> >>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> >>>>>>>> Hi Matthias,
> >>>>>>>>
> >>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> >>>>>>>>>
> >>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
> >>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>> [...]
> >>>>>>>>>
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>>>>>>>>>>> task->command_size,
> >>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>>>>>>>>>>> +}
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>>>>>>>>>>> +{
> >>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>>>>>>>>>>> +    bool err;
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>>>>>>>>>>> +        err = true;
> >>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>>>>>>>>>>> +        err = false;
> >>>>>>>>>>>>>>>>>>> +    else
> >>>>>>>>>>>>>>>>>>> +        return;
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>>>>>>>>>>> +                 list_entry) {
> >>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>>>>>>>>>>> commands in the
> >>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>>>>>>>>>>> guarantees
> >>>>>>>>>>>>>>>>>> about dma_handle?
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>>>>>>>>>>> 2. Yes.
> >>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>>>>>>>>>>> calling
> >>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>>>>>>>>>>> use this
> >>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>>>>>>>>>>> currently
> >>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>>>>>>>>>>        From CPU's point of view, tasks are linked by the busy list.
> >>>>>>>>>>>>>>>        From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>>>>>>>>>>> when it is running ISR.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>>>>>>>>>>> we need to do it in a thread context.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
> >>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
> >>>>>>>>>>>>> else display will drop frame.
> >>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
> >>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
> >>>>>>>>>>>>> 32 ms.sometimes.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
> >>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
> >>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
> >>>>>>>>>>>
> >>>>>>>>>>> The order should be kept.
> >>>>>>>>>>> Let me use mouse cursor as an example.
> >>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> >>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
> >>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
> >>>>>>>>>>>
> >>>>>>>>>>
> >>>>>>>>>> Got it, thanks for the clarification.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
> >>>>>>>>> programmed to the time a timeout in the first task in the busy list
> >>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
> >>>>>>>>> got finished by the thread), we update the timer. When the timer
> >>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
> >>>>>>>>> the busy list elements, until the time is lower then the actual time.
> >>>>>>>>>
> >>>>>>>>> At least with this we can reduce the data structures in this driver and
> >>>>>>>>> make it more lightweight.
> >>>>>>>>
> >>>>>>>>     From my understanding, your proposed method can handle timeout case.
> >>>>>>>>
> >>>>>>>> However, the workqueue is also in charge of releasing tasks.
> >>>>>>>> Do you take releasing tasks into consideration by using the proposed
> >>>>>>>> timer method?
> >>>>>>>> Furthermore, I think the code will become more complex if we also use
> >>>>>>>> timer to implement releasing tasks.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Can't we call
> >>>>>>>             clk_disable_unprepare(cmdq->clock);
> >>>>>>>             cmdq_task_release(task);
> >>>>>>> after invoking the callback?
> >
> > After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
> > another BUG.
> >
> > (Quote some Linux 4.7 source code.)
> >
> >   605 void clk_unprepare(struct clk *clk)
> >   606 {
> >   607         if (IS_ERR_OR_NULL(clk))
> >   608                 return;
> >   609
> >   610         clk_prepare_lock();                      // <-- Here
> >   611         clk_core_unprepare(clk->core);
> >   612         clk_prepare_unlock();
> >   613 }
> >   614 EXPORT_SYMBOL_GPL(clk_unprepare);
> >
> >    91 static void clk_prepare_lock(void)
> >    92 {
> >    93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
> >    94                 if (prepare_owner == current) {
> >    95                         prepare_refcnt++;
> >    96                         return;
> >    97                 }
> >    98                 mutex_lock(&prepare_lock);
> >    99         }
> >   100         WARN_ON_ONCE(prepare_owner != NULL);
> >   101         WARN_ON_ONCE(prepare_refcnt != 0);
> >   102         prepare_owner = current;
> >   103         prepare_refcnt = 1;
> >   104 }
> >
> > So, 'unprepare' can sleep and cannot be put into ISR.
> > I also try to put it into a timer, but the error is the same
> > since timer callback is executed by softirq.
> >
> > We need clk_disable_unprepare() since it can save power consumption
> > in idle.
> 
> We can call clk_prepare in probe and then use clk_enable/clk_disable, 
> which don't sleep.
> 
> Regards,
> Matthias

Hi Matthias,

Because clock gate and MUX are controlled by clk_enable/clk_disable,
and PLL is controlled by clk_prepare/clk_unprepare,
I still need to call clk_unprepare.

After I remove releasing buffer, releasing task, and timeout task from
work, the work can be detached from task.

Therefore, I can use the following flow to reduce the number of works.

if task_busy_list from empty to non-empty
	clk_prepare_enable
if task_busy_list from non-empty to empty
	in ISR, add work for clk_disable_unprepare

What do you think of this solution?

Thanks,
HS

> > Therefore, I plan to
> > (1) move releasing buffer and task into ISR,
> > (2) move timeout into timer, and
> > (3) keep workqueue for clk_disable_unprepare().
> >
> > What do you think?
> >
> > Thanks,
> > HS
> >
> >>>>>>
> >>>>>> Do you mean just call these two functions in ISR?
> >>>>>> My major concern is dma_free_coherent() and kfree() in
> >>>>>> cmdq_task_release(task).
> >>>>>
> >>>>> Why do we need the dma calls at all? Can't we just calculate the
> >>>>> physical address using __pa(x)?
> >>>>
> >>>> I prefer to use dma_map_single/dma_unmap_single.
> >>>>
> >>>
> >>> Can you please elaborate why you need this. We don't do dma, so we
> >>> should not use dma memory for this.
> >>
> >> We need a buffer to share between CPU and GCE, so we do need DMA.
> >> CPU is in charge of writing GCE commands into this buffer.
> >> GCE is in charge of reading and running GCE commands from this buffer.
> >> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
> >> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
> >>
> >> However, if we want to use timer to handle timeout, we need to release
> >> memory in ISR.
> >> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
> >> instead of dma_alloc_coherent/dma_free_coherent is an alternative
> >> solution, but taking care the synchronization between cache and memory
> >> is the expected overhead.
> >>
> >>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
> >>>>>> dma_alloc_coherent() and kzalloc(). Right?
> >>>>>
> >>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
> >>>>> memory.
> >>>>
> >>>> I tested these two functions, and kfree was safe.
> >>>> However, dma_free_coherent raised BUG.
> >>>> BUG: failure at
> >>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
> >>>
> >>> Just a general hint. Please try to evaluate on a recent kernel. It looks
> >>> like as if you tried this on a v3.18 based one.
> >>
> >> This driver should be backward compatible to v3.18 for a MTK project.
> >>
> >>> Best regards,
> >>> Matthias
> >>
> >> Thanks,
> >> HS
> >>
> >>>> 1512 void vunmap(const void *addr)
> >>>> 1513 {
> >>>> 1514         BUG_ON(in_interrupt());		// <-- here
> >>>> 1515         might_sleep();
> >>>> 1516         if (addr)
> >>>> 1517                 __vunmap(addr, 0);
> >>>> 1518 }
> >>>> 1519 EXPORT_SYMBOL(vunmap);
> >>>>
> >>>> Therefore, I plan to use kmalloc + dma_map_single instead of
> >>>> dma_alloc_coherent, and dma_unmap_single + kfree instead of
> >>>> dma_free_coherent.
> >>>>
> >>>> What do you think about the function replacement?
> >>>>
> >>>>>> If so, I can try to implement timeout by timer, and discuss with you
> >>>>>> if I have further questions.
> >>>>>>
> >>>>>
> >>>>> Sounds good :)
> >>>>>
> >>>>> Thanks,
> >>>>> Matthias
> >>>>
> >>>> Thanks,
> >>>> HS
> >>>>
> >>>>>>> Regrading the clock, wouldn't it be easier to handle the clock
> >>>>>>> enable/disable depending on the state of task_busy_list? I suppose we
> >>>>>>> can't as we would need to check the task_busy_list of all threads, right?
> >>>>>>>
> >>>>>>> Regards,
> >>>>>>> Matthias
> >>>>>>
> >>>>>> Thanks,
> >>>>>> HS




From ray.jui at broadcom.com  Mon Jun 20 23:00:22 2016
From: ray.jui at broadcom.com (Ray Jui)
Date: Mon, 20 Jun 2016 23:00:22 -0700
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <35869d12-1686-98d3-185c-cedacbc7eed1@broadcom.com>

Hi Daniel,

On 6/16/2016 2:27 PM, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
>
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  arch/arc/kernel/time.c                    |  6 +++---
>  arch/arm/kernel/smp_twd.c                 |  6 +++---
>  arch/microblaze/kernel/timer.c            |  2 +-
>  arch/mips/ralink/cevt-rt3352.c            |  2 +-
>  arch/nios2/kernel/time.c                  |  2 +-
>  drivers/clocksource/arm_arch_timer.c      |  6 +++---
>  drivers/clocksource/arm_global_timer.c    |  2 +-
>  drivers/clocksource/armv7m_systick.c      |  2 +-
>  drivers/clocksource/asm9260_timer.c       |  2 +-
>  drivers/clocksource/bcm2835_timer.c       |  2 +-
>  drivers/clocksource/bcm_kona_timer.c      |  4 ++--
>  drivers/clocksource/cadence_ttc_timer.c   |  2 +-
>  drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
>  drivers/clocksource/clksrc-probe.c        | 14 --------------
>  drivers/clocksource/clksrc_st_lpc.c       |  2 +-
>  drivers/clocksource/clps711x-timer.c      |  2 +-
>  drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
>  drivers/clocksource/exynos_mct.c          |  4 ++--
>  drivers/clocksource/fsl_ftm_timer.c       |  2 +-
>  drivers/clocksource/h8300_timer16.c       |  2 +-
>  drivers/clocksource/h8300_timer8.c        |  2 +-
>  drivers/clocksource/h8300_tpu.c           |  2 +-
>  drivers/clocksource/meson6_timer.c        |  2 +-
>  drivers/clocksource/mips-gic-timer.c      |  2 +-
>  drivers/clocksource/moxart_timer.c        |  2 +-
>  drivers/clocksource/mps2-timer.c          |  2 +-
>  drivers/clocksource/mtk_timer.c           |  2 +-
>  drivers/clocksource/mxs_timer.c           |  2 +-
>  drivers/clocksource/nomadik-mtu.c         |  2 +-
>  drivers/clocksource/pxa_timer.c           |  2 +-
>  drivers/clocksource/qcom-timer.c          |  4 ++--
>  drivers/clocksource/rockchip_timer.c      |  8 ++++----
>  drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
>  drivers/clocksource/sun4i_timer.c         |  2 +-
>  drivers/clocksource/tango_xtal.c          |  2 +-
>  drivers/clocksource/tegra20_timer.c       |  4 ++--
>  drivers/clocksource/time-armada-370-xp.c  |  6 +++---
>  drivers/clocksource/time-efm32.c          |  4 ++--
>  drivers/clocksource/time-lpc32xx.c        |  2 +-
>  drivers/clocksource/time-orion.c          |  2 +-
>  drivers/clocksource/time-pistachio.c      |  2 +-
>  drivers/clocksource/timer-atlas7.c        |  2 +-
>  drivers/clocksource/timer-atmel-pit.c     |  2 +-
>  drivers/clocksource/timer-atmel-st.c      |  2 +-
>  drivers/clocksource/timer-digicolor.c     |  2 +-
>  drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
>  drivers/clocksource/timer-integrator-ap.c |  2 +-
>  drivers/clocksource/timer-keystone.c      |  2 +-
>  drivers/clocksource/timer-nps.c           |  4 ++--
>  drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
>  drivers/clocksource/timer-prima2.c        |  2 +-
>  drivers/clocksource/timer-sp804.c         |  4 ++--
>  drivers/clocksource/timer-stm32.c         |  2 +-
>  drivers/clocksource/timer-sun5i.c         |  4 ++--
>  drivers/clocksource/timer-ti-32k.c        |  2 +-
>  drivers/clocksource/timer-u300.c          |  2 +-
>  drivers/clocksource/versatile.c           |  4 ++--
>  drivers/clocksource/vf_pit_timer.c        |  2 +-
>  drivers/clocksource/vt8500_timer.c        |  2 +-
>  drivers/clocksource/zevio-timer.c         |  2 +-
>  include/asm-generic/vmlinux.lds.h         |  2 --
>  include/linux/clocksource.h               |  5 +----
>  62 files changed, 98 insertions(+), 117 deletions(-)

[..]

> diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
> index 98bc2a2..ee07e7e3 100644
> --- a/drivers/clocksource/bcm_kona_timer.c
> +++ b/drivers/clocksource/bcm_kona_timer.c
> @@ -200,9 +200,9 @@ static int __init kona_timer_init(struct device_node *node)
>  	return 0;
>  }
>
> -CLOCKSOURCE_OF_DECLARE_RET(brcm_kona, "brcm,kona-timer", kona_timer_init);
> +CLOCKSOURCE_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
>  /*
>   * bcm,kona-timer is deprecated by brcm,kona-timer
>   * being kept here for driver compatibility
>   */
> -CLOCKSOURCE_OF_DECLARE_RET(bcm_kona, "bcm,kona-timer", kona_timer_init);
> +CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);

For Broadcom Kona timer change:

Acked-by: Ray Jui <ray.jui at broadcom.com>

Thanks!

Ray


From noamca at mellanox.com  Mon Jun 20 23:45:58 2016
From: noamca at mellanox.com (Noam Camus)
Date: Tue, 21 Jun 2016 06:45:58 +0000
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <HE1PR05MB162581196C0157F45749344FAA2B0@HE1PR05MB1625.eurprd05.prod.outlook.com>

>From: Daniel Lezcano [mailto:daniel.lezcano at linaro.org] 
>Sent: Friday, June 17, 2016 12:27 AM

>All the clocksource drivers's init function are now converted to return an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the clksrc-of table.

>Let's convert back the names:
> - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
> - clksrc-of-ret              => clksrc-of

> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
> arch/arc/kernel/time.c                    |  6 +++---
> arch/arm/kernel/smp_twd.c                 |  6 +++---
> arch/microblaze/kernel/timer.c            |  2 +-
> arch/mips/ralink/cevt-rt3352.c            |  2 +-
> arch/nios2/kernel/time.c                  |  2 +-
> drivers/clocksource/arm_arch_timer.c      |  6 +++---
> drivers/clocksource/arm_global_timer.c    |  2 +-
> drivers/clocksource/armv7m_systick.c      |  2 +-
> drivers/clocksource/asm9260_timer.c       |  2 +-
> drivers/clocksource/bcm2835_timer.c       |  2 +-
> drivers/clocksource/bcm_kona_timer.c      |  4 ++--
> drivers/clocksource/cadence_ttc_timer.c   |  2 +-
> drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
> drivers/clocksource/clksrc-probe.c        | 14 --------------
> drivers/clocksource/clksrc_st_lpc.c       |  2 +-
> drivers/clocksource/clps711x-timer.c      |  2 +-
> drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
> drivers/clocksource/exynos_mct.c          |  4 ++--
> drivers/clocksource/fsl_ftm_timer.c       |  2 +-
> drivers/clocksource/h8300_timer16.c       |  2 +-
> drivers/clocksource/h8300_timer8.c        |  2 +-
> drivers/clocksource/h8300_tpu.c           |  2 +-
> drivers/clocksource/meson6_timer.c        |  2 +-
> drivers/clocksource/mips-gic-timer.c      |  2 +-
> drivers/clocksource/moxart_timer.c        |  2 +-
> drivers/clocksource/mps2-timer.c          |  2 +-
> drivers/clocksource/mtk_timer.c           |  2 +-
> drivers/clocksource/mxs_timer.c           |  2 +-
> drivers/clocksource/nomadik-mtu.c         |  2 +-
> drivers/clocksource/pxa_timer.c           |  2 +-
> drivers/clocksource/qcom-timer.c          |  4 ++--
> drivers/clocksource/rockchip_timer.c      |  8 ++++----
> drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
> drivers/clocksource/sun4i_timer.c         |  2 +-
> drivers/clocksource/tango_xtal.c          |  2 +-
> drivers/clocksource/tegra20_timer.c       |  4 ++--
> drivers/clocksource/time-armada-370-xp.c  |  6 +++---
> drivers/clocksource/time-efm32.c          |  4 ++--
> drivers/clocksource/time-lpc32xx.c        |  2 +-
> drivers/clocksource/time-orion.c          |  2 +-
> drivers/clocksource/time-pistachio.c      |  2 +-
> drivers/clocksource/timer-atlas7.c        |  2 +-
> drivers/clocksource/timer-atmel-pit.c     |  2 +-
> drivers/clocksource/timer-atmel-st.c      |  2 +-
> drivers/clocksource/timer-digicolor.c     |  2 +-
> drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
> drivers/clocksource/timer-integrator-ap.c |  2 +-
> drivers/clocksource/timer-keystone.c      |  2 +-
> drivers/clocksource/timer-nps.c           |  4 ++--
> drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
> drivers/clocksource/timer-prima2.c        |  2 +-
> drivers/clocksource/timer-sp804.c         |  4 ++--
> drivers/clocksource/timer-stm32.c         |  2 +-
> drivers/clocksource/timer-sun5i.c         |  4 ++--
> drivers/clocksource/timer-ti-32k.c        |  2 +-
> drivers/clocksource/timer-u300.c          |  2 +-
> drivers/clocksource/versatile.c           |  4 ++--
> drivers/clocksource/vf_pit_timer.c        |  2 +-
> drivers/clocksource/vt8500_timer.c        |  2 +-
> drivers/clocksource/zevio-timer.c         |  2 +-
> include/asm-generic/vmlinux.lds.h         |  2 --
> include/linux/clocksource.h               |  5 +----
> 62 files changed, 98 insertions(+), 117 deletions(-)

[..]

> diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c index b5c7b2b..70c149a 100644
> --- a/drivers/clocksource/timer-nps.c
> +++ b/drivers/clocksource/timer-nps.c
> @@ -96,5 +96,5 @@ static int __init nps_timer_init(struct device_node *node)
> 	return nps_setup_clocksource(node, clk);  }
 
> -CLOCKSOURCE_OF_DECLARE_RET(ezchip_nps400_clksrc, "ezchip,nps400-timer",
> -			   nps_timer_init);
> +CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
> +		       nps_timer_init);
> diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
> index 0d99f40..bd887e2 100644
> --- a/drivers/clocksource/timer-oxnas-rps.c
> +++ b/drivers/clocksource/timer-oxnas-rps.c
> @@ -293,5 +293,5 @@ err_alloc:
>  	return ret;
> }
 

Acked-by: Noam Camus <noamca at mellanox.com>


From hs.liao at mediatek.com  Tue Jun 21 00:46:11 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Tue, 21 Jun 2016 15:46:11 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466474580.6214.29.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466419311.27959.12.camel@mtksdaap41>
 <1466421720.1918.11.camel@mtksdaap41> <1466474580.6214.29.camel@mtksdaap41>
Message-ID: <1466495171.8045.27.camel@mtksdaap41>

On Tue, 2016-06-21 at 10:03 +0800, CK Hu wrote:
> On Mon, 2016-06-20 at 19:22 +0800, Horng-Shyang Liao wrote:
> > On Mon, 2016-06-20 at 18:41 +0800, CK Hu wrote:
> > > Hi, HS:
> > > 
> > > One comment inline.
> > > 
> > > On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> > > > This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> > > > CMDQ is used to help read/write registers with critical time limitation,
> > > > such as updating display configuration during the vblank. It controls
> > > > Global Command Engine (GCE) hardware to achieve this requirement.
> > > > Currently, CMDQ only supports display related hardwares, but we expect
> > > > it can be extended to other hardwares for future requirements.
> > > > 
> > > > Signed-off-by: HS Liao <hs.liao at mediatek.com>
> > > > Signed-off-by: CK Hu <ck.hu at mediatek.com>
> > > > ---
> > > >  drivers/soc/mediatek/Kconfig    |  10 +
> > > >  drivers/soc/mediatek/Makefile   |   1 +
> > > >  drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
> > > >  include/soc/mediatek/cmdq.h     | 197 +++++++++
> > > >  4 files changed, 1151 insertions(+)
> > > >  create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
> > > >  create mode 100644 include/soc/mediatek/cmdq.h
> > > > 
> > > > diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
> > > > index 0a4ea80..c4ad75c 100644
> > > > --- a/drivers/soc/mediatek/Kconfig
> > > > +++ b/drivers/soc/mediatek/Kconfig
> > > > @@ -1,6 +1,16 @@
> > > >  #
> > > >  # MediaTek SoC drivers
> > > >  #
> > > > +config MTK_CMDQ
> > > > +	bool "MediaTek CMDQ Support"
> > > > +	depends on ARCH_MEDIATEK || COMPILE_TEST
> > > > +	select MTK_INFRACFG
> > > > +	help
> > > > +	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
> > > > +	  driver. The CMDQ is used to help read/write registers with critical
> > > > +	  time limitation, such as updating display configuration during the
> > > > +	  vblank.
> > > > +
> > > >  config MTK_INFRACFG
> > > >  	bool "MediaTek INFRACFG Support"
> > > >  	depends on ARCH_MEDIATEK || COMPILE_TEST
> > > > diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
> > > > index 12998b0..f7397ef 100644
> > > > --- a/drivers/soc/mediatek/Makefile
> > > > +++ b/drivers/soc/mediatek/Makefile
> > > 
> > > [Snip...]
> > > 
> > > > +
> > > > +static int cmdq_eng_get_thread(u64 flag)
> > > > +{
> > > > +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> > > > +		return CMDQ_THR_DISP_MAIN_IDX;
> > > > +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> > > > +		return CMDQ_THR_DISP_SUB_IDX;
> > > > +	else
> > > > +		return CMDQ_THR_DISP_MISC_IDX;
> > > > +}
> > > 
> > > I think cmdq should not have knowledge of client. These statement show
> > > that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
> > > the 'session' to replace engine flag. For example, main display create
> > > one cmdq_session and external display create another cmdq_session. For
> > > client driver, every tasks created by main display is bound to main
> > > display session. For cmdq driver, it should dynamically bind a session
> > > to a HW thread, and then dispatch tasks of this session to this HW
> > > thread. After HW thread run out of tasks of this session, detach this
> > > session with this thread.
> > > For the problem of remove wfe cmd, I think a session can have a property
> > > of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
> > > For other client, it's false.
> > 
> > Hi CK,
> > 
> > I think your suggestion is similar to CMDQ 'scenarios',
> > which was removed from CMDQ v2.
> > 
> > Daniel suggests to use engine flags instead of scenarios.
> > Quote from https://patchwork.kernel.org/patch/8068311/ .
> > 'Instead of encoding policy (these arbitrary "scenarios"), perhaps the
> >  cmdq driver should just provide these flag bits, and the display
> >  subsystem can use them to build the "flag" parameter itself.
> > 
> >  After all, the exact configuration of mmsys components is somewhat
> >  flexible.'
> > 
> > Therefore, it would be better to discuss with Daniel before we change
> > it.
> > 
> > 
> > Hi Daniel,
> > 
> > Do you think we should use scenarios or sessions instead of flags?
> > 
> > Thanks,
> > HS
> > 
> 
> Hi, HS:
> 
> 'session' is not similar to 'scenarios'.
> 
> In 'scenarios' mechanism, client bind a task with scenarios and send to
> cmdq. Cmdq transfer scenarios to engine flag, and use engine flag to
> dispatch task to HW thread.
> 
> 
> In 'engine flag' mechanism, proposed by Daniel, client bind a task
> directly with engine flag. Cmdq directly use engine flag to dispatch
> task to HW thread without any translation.
> 
> But neither 'scenarios' mechanism nor 'engine flag' mechanism get rid of
> engine flag, which make cmdq have knowledge of client.
> 
> In 'session' mechanism, there is no engine flag any more. Client bind
> time-sequential tasks to the same session and tasks in different session
> can execute parallelly. One thing cmdq need to know is to dispatch tasks
> with the same session to the same HW thread, so cmdq does not have any
> knowledge of client.
> 
> Daniel focus on reduce translating scenarios to engine flag. I think
> 'session' mechanism does not conflict with his opinion because we does
> not translate 'session' to engine flag. Therefore, I think 'session' is
> the best of these three mechanism.
> 
> Regards,
> CK

Hi CK,

'Session' looks like a group of options for CMDQ.
CMDQ driver can just follow the options to run its flow,
and doesn't need to know its client(s).

We don't have many options now, but it has good flexibility to extend
for future requirements.

I will add it in next version.

Thanks,
HS

> > > Here is the sample code to create cmdq_rec with session.
> > > 
> > > merge_wfe_cmd = true;
> > > cmdq_session_create(merge_wfe_cmd, &primary_display_session);
> > > cmdq_rec_create(dev, primary_display_session, &rec);
> > > 
> > > 
> > > Therefore, the below definition can be removed.
> > > 
> > > > +
> > > > +enum cmdq_eng {
> > > > +	CMDQ_ENG_DISP_AAL,
> > > > +	CMDQ_ENG_DISP_COLOR0,
> > > > +	CMDQ_ENG_DISP_COLOR1,
> > > > +	CMDQ_ENG_DISP_DPI0,
> > > > +	CMDQ_ENG_DISP_DSI0,
> > > > +	CMDQ_ENG_DISP_DSI1,
> > > > +	CMDQ_ENG_DISP_GAMMA,
> > > > +	CMDQ_ENG_DISP_OD,
> > > > +	CMDQ_ENG_DISP_OVL0,
> > > > +	CMDQ_ENG_DISP_OVL1,
> > > > +	CMDQ_ENG_DISP_PWM0,
> > > > +	CMDQ_ENG_DISP_PWM1,
> > > > +	CMDQ_ENG_DISP_RDMA0,
> > > > +	CMDQ_ENG_DISP_RDMA1,
> > > > +	CMDQ_ENG_DISP_RDMA2,
> > > > +	CMDQ_ENG_DISP_UFOE,
> > > > +	CMDQ_ENG_DISP_WDMA0,
> > > > +	CMDQ_ENG_DISP_WDMA1,
> > > > +	CMDQ_ENG_MAX,
> > > > +};
> > > > +
> > > 
> > > 
> > > Regards,
> > > CK
> > > 
> > 
> > 
> 
> 




From joro at 8bytes.org  Tue Jun 21 02:39:51 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Tue, 21 Jun 2016 11:39:51 +0200
Subject: [PATCH] iommu/mediatek: Do not call of_node_put in
 mtk_iommu_of_xlate
In-Reply-To: <1465379444-14719-1-git-send-email-honghui.zhang@mediatek.com>
References: <1465379444-14719-1-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <20160621093951.GC28248@8bytes.org>

On Wed, Jun 08, 2016 at 05:50:44PM +0800, honghui.zhang at mediatek.com wrote:
> From: Honghui Zhang <honghui.zhang at mediatek.com>
> 
> The device_node will be released in of_iommu_configure, it may be double
> released if call of_node_put in mtk_iommu_of_xlate.
> 
> Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> ---
>  drivers/iommu/mtk_iommu.c | 1 -
>  1 file changed, 1 deletion(-)

Applied, thanks.


From joro at 8bytes.org  Tue Jun 21 02:41:16 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Tue, 21 Jun 2016 11:41:16 +0200
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <20160621094116.GD28248@8bytes.org>

On Wed, Jun 08, 2016 at 05:51:01PM +0800, honghui.zhang at mediatek.com wrote:
> From: Honghui Zhang <honghui.zhang at mediatek.com>
> 
> Add the dtsi node of iommu and smi for mt2701.
> 
> Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> ---
>  arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)

Applied the series, except this last patch. It didn't apply cleanly and
I was not sure how to correctly fix that. Can you please resubmit the
patch based on my arm/mediatek branch once I pushed it?

Thanks,

	Joerg



From joro at 8bytes.org  Tue Jun 21 02:57:48 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Tue, 21 Jun 2016 11:57:48 +0200
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
Message-ID: <20160621095748.GE28248@8bytes.org>

On Wed, Jun 08, 2016 at 05:51:01PM +0800, honghui.zhang at mediatek.com wrote:
> From: Honghui Zhang <honghui.zhang at mediatek.com>
> 
> Add the dtsi node of iommu and smi for mt2701.
> 
> Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> ---
>  arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)

Okay, I pushed my arm/mediatek branch to my tree at

	git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git

Please base the patch on that branch and re-send.

Thanks,

	Joerg



From honghui.zhang at mediatek.com  Tue Jun 21 02:55:00 2016
From: honghui.zhang at mediatek.com (Honghui Zhang)
Date: Tue, 21 Jun 2016 17:55:00 +0800
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <20160621094116.GD28248@8bytes.org>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621094116.GD28248@8bytes.org>
Message-ID: <1466502900.8787.2.camel@mtksdaap41>

On Tue, 2016-06-21 at 11:41 +0200, Joerg Roedel wrote:
> On Wed, Jun 08, 2016 at 05:51:01PM +0800, honghui.zhang at mediatek.com wrote:
> > From: Honghui Zhang <honghui.zhang at mediatek.com>
> > 
> > Add the dtsi node of iommu and smi for mt2701.
> > 
> > Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> > ---
> >  arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 51 insertions(+)
> 
> Applied the series, except this last patch. It didn't apply cleanly and
> I was not sure how to correctly fix that. Can you please resubmit the
> patch based on my arm/mediatek branch once I pushed it?
> 

I will rebase this one later after your push.
Thanks.

> Thanks,
> 
> 	Joerg
> 




From ulf.hansson at linaro.org  Tue Jun 21 06:15:51 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Tue, 21 Jun 2016 15:15:51 +0200
Subject: [PATCH 1/3] mmc: mmc: use ops->card_busy() to check card status
 in __mmc_switch()
In-Reply-To: <1463647662-27426-2-git-send-email-chaotian.jing@mediatek.com>
References: <1463647662-27426-1-git-send-email-chaotian.jing@mediatek.com>
 <1463647662-27426-2-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <CAPDyKFrAKuv2x-mnt-r=RWKZVci=mBTcJeG0=CfLtdG-voWyxw@mail.gmail.com>

On 19 May 2016 at 10:47, Chaotian Jing <chaotian.jing at mediatek.com> wrote:
> some MMC host do not support MMC_CAP_WAIT_WHILE_BUSY but provides
> ops->card_busy(), So, add this method to check card status after
> switch command.
>
> Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>

Thanks, applied for next! (With some minor updates to the change log)

Kind regards
Uffe

> ---
>  drivers/mmc/core/mmc_ops.c | 25 ++++++++++++++++---------
>  1 file changed, 16 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index 62355bd..32de144 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -480,6 +480,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>         u32 status = 0;
>         bool use_r1b_resp = use_busy_signal;
>         bool expired = false;
> +       bool busy = false;
>
>         mmc_retune_hold(host);
>
> @@ -535,19 +536,24 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>         /* Must check status to be sure of no errors. */
>         timeout = jiffies + msecs_to_jiffies(timeout_ms);
>         do {
> +               /*
> +                * Due to the possibility of being preempted after
> +                * sending the status command, check the expiration
> +                * time first.
> +                */
> +               expired = time_after(jiffies, timeout);
>                 if (send_status) {
> -                       /*
> -                        * Due to the possibility of being preempted after
> -                        * sending the status command, check the expiration
> -                        * time first.
> -                        */
> -                       expired = time_after(jiffies, timeout);
>                         err = __mmc_send_status(card, &status, ignore_crc);
>                         if (err)
>                                 goto out;
>                 }
>                 if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
>                         break;
> +               if (host->ops->card_busy) {
> +                       if (!host->ops->card_busy(host))
> +                               break;
> +                       busy = true;
> +               }
>                 if (mmc_host_is_spi(host))
>                         break;
>
> @@ -556,19 +562,20 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>                  * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
>                  * rely on waiting for the stated timeout to be sufficient.
>                  */
> -               if (!send_status) {
> +               if (!send_status && !host->ops->card_busy) {
>                         mmc_delay(timeout_ms);
>                         goto out;
>                 }
>
>                 /* Timeout if the device never leaves the program state. */
> -               if (expired && R1_CURRENT_STATE(status) == R1_STATE_PRG) {
> +               if (expired &&
> +                   (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) {
>                         pr_err("%s: Card stuck in programming state! %s\n",
>                                 mmc_hostname(host), __func__);
>                         err = -ETIMEDOUT;
>                         goto out;
>                 }
> -       } while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
> +       } while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy);
>
>         err = mmc_switch_status_error(host, status);
>  out:
> --
> 1.8.1.1.dirty
>


From ulf.hansson at linaro.org  Tue Jun 21 06:15:58 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Tue, 21 Jun 2016 15:15:58 +0200
Subject: [PATCH 3/3] mmc: mmc: fix switch timeout issue caused by jiffies
 precision
In-Reply-To: <1463647662-27426-4-git-send-email-chaotian.jing@mediatek.com>
References: <1463647662-27426-1-git-send-email-chaotian.jing@mediatek.com>
 <1463647662-27426-4-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <CAPDyKFqVW0sdwwvYoJcwZh=WKq1QzJREvt2y+rNWyA324J7p4A@mail.gmail.com>

On 19 May 2016 at 10:47, Chaotian Jing <chaotian.jing at mediatek.com> wrote:
> with CONFIG_HZ=100, the precision of jiffies is 10ms, and the
> generic_cmd6_time of some card is also 10ms. then, may be current
> time is only 5ms, but already timed out caused by jiffies precision.
>
> Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>

Thanks, applied for next!

Kind regards
Uffe

> ---
>  drivers/mmc/core/mmc_ops.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index 32de144..ad6e979 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -534,7 +534,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
>                 timeout_ms = MMC_OPS_TIMEOUT_MS;
>
>         /* Must check status to be sure of no errors. */
> -       timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +       timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
>         do {
>                 /*
>                  * Due to the possibility of being preempted after
> --
> 1.8.1.1.dirty
>


From ulf.hansson at linaro.org  Tue Jun 21 06:16:03 2016
From: ulf.hansson at linaro.org (Ulf Hansson)
Date: Tue, 21 Jun 2016 15:16:03 +0200
Subject: [PATCH 2/3] mmc: mmc: do not use CMD13 to get status after speed
 mode switch
In-Reply-To: <1463647662-27426-3-git-send-email-chaotian.jing@mediatek.com>
References: <1463647662-27426-1-git-send-email-chaotian.jing@mediatek.com>
 <1463647662-27426-3-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <CAPDyKFqaba9D4XtuDnPLcq2D4dpO6Cq9-RfhwoEu_vO=Pt9wEQ@mail.gmail.com>

On 19 May 2016 at 10:47, Chaotian Jing <chaotian.jing at mediatek.com> wrote:
> Per JEDEC spec, it is not recommended to use CMD13 to get card status
> after speed mode switch. below are two reason about this:
> 1. CMD13 cannot be guaranteed due to the asynchronous operation.
> Therefore it is not recommended to use CMD13 to check busy completion
> of the timing change indication.
> 2. After switch to HS200, CMD13 will get response of 0x800, and even the
> busy signal gets de-asserted, the response of CMD13 is aslo 0x800.
>
> Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>

Thanks, applied for next! (With a minor change, see below)

[...]

>
>         /* Switch HS to HS200 */
>         val = EXT_CSD_TIMING_HS200 |
>               card->drive_strength << EXT_CSD_DRV_STR_SHIFT;
>         err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
>                            val, card->ext_csd.generic_cmd6_time, true,
> -                          send_status, true);
> +                          false, true);

To keep consistency with other calls to __mmc_switch(), I change these lines to:

err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING,
                       val, card->ext_csd.generic_cmd6_time,
                       true, false, true);

>         if (err)
>                 goto out_err;
>
>         mmc_set_timing(host, MMC_TIMING_MMC_HS200);
>
> -       if (!send_status) {
> -               err = mmc_switch_status(card);
> -               if (err)
> -                       goto out_err;
> -       }
> +       err = mmc_switch_status(card);
> +       if (err)
> +               goto out_err;
>
>         mmc_set_bus_speed(card);
>
> @@ -1243,7 +1227,6 @@ static void mmc_select_driver_type(struct mmc_card *card)
>  static int mmc_select_hs200(struct mmc_card *card)
>  {
>         struct mmc_host *host = card->host;
> -       bool send_status = true;
>         unsigned int old_timing;
>         int err = -EINVAL;
>         u8 val;
> @@ -1260,9 +1243,6 @@ static int mmc_select_hs200(struct mmc_card *card)
>
>         mmc_select_driver_type(card);
>
> -       if (host->caps & MMC_CAP_WAIT_WHILE_BUSY)
> -               send_status = false;
> -
>         /*
>          * Set the bus width(4 or 8) with host's support and
>          * switch to HS200 mode if bus width is set successfully.
> @@ -1274,20 +1254,18 @@ static int mmc_select_hs200(struct mmc_card *card)
>                 err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
>                                    EXT_CSD_HS_TIMING, val,
>                                    card->ext_csd.generic_cmd6_time,
> -                                  true, send_status, true);
> +                                  true, false, true);

[...]

Kind regards
Uffe


From thierry.reding at gmail.com  Tue Jun 21 06:19:12 2016
From: thierry.reding at gmail.com (Thierry Reding)
Date: Tue, 21 Jun 2016 15:19:12 +0200
Subject: [PATCH] drm: mediatek: remove IOMMU_DMA select
In-Reply-To: <3436915.Zo8cThvXaA@wuerfel>
References: <1462982509-342936-1-git-send-email-arnd@arndb.de>
 <1462997501-982363-1-git-send-email-arnd@arndb.de>
 <3436915.Zo8cThvXaA@wuerfel>
Message-ID: <20160621131912.GA6060@ulmo.ba.sec>

On Wed, May 11, 2016 at 10:15:20PM +0200, Arnd Bergmann wrote:
> On Wednesday 11 May 2016 22:11:07 Arnd Bergmann wrote:
> > We get a harmless build warning when trying to use the mediatek
> > DRM driver with IOMMU support disabled:
> > 
> > warning: (DRM_MEDIATEK) selects IOMMU_DMA which has unmet direct dependencies (IOMMU_SUPPORT)
> > 
> > However, the IOMMU_DMA symbol is not meant to be used by drivers
> > at all, and this driver doesn't seem to have a strict dependency
> > on it other than using the mediatek IOMMU driver that does.
> > 
> > Since we also want to be able to do compile tests with the
> > driver on other platforms, the IOMMU_DMA symbol should not
> > be selected here.
> > 
> > Signed-off-by: Arnd Bergmann <arnd at arndb.de>
> > ---
> > If someone has a better explanation about why the 'select' is here,
> > let me know, it certainly seems out of place.
> 
> Sorry, I didn't mean to send this as a reply to "More build fixes for
> omapdrm in current -next", I copied the wrong command line.
> 
> I'll resend it if necessary.

That's probably the reason why patchwork didn't include the commit
message in the downloaded mbox. I've pieced it together manually and
applied this to topic/drm-misc.

Thanks,
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-mediatek/attachments/20160621/563249f9/attachment.sig>

From matthias.bgg at gmail.com  Tue Jun 21 06:41:33 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Tue, 21 Jun 2016 15:41:33 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466488358.8045.19.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41> <1466152107.11184.14.camel@mtksdaap41>
 <57641E01.3070205@gmail.com> <1466488358.8045.19.camel@mtksdaap41>
Message-ID: <5769440D.5030505@gmail.com>



On 21/06/16 07:52, Horng-Shyang Liao wrote:
> On Fri, 2016-06-17 at 17:57 +0200, Matthias Brugger wrote:
>>
>> On 17/06/16 10:28, Horng-Shyang Liao wrote:
>>> Hi Matthias,
>>>
>>> On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
>>>> Hi Matthias,
>>>>
>>>> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
>>>>>
>>>>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
>>>>>> Hi Matthias,
>>>>>>
>>>>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
>>>>>>>
>>>>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
>>>>>>>> Hi Matthias,
>>>>>>>>
>>>>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>>>>>>>>
>>>>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>>>>>>>>> Hi Matthias,
>>>>>>>>>>
>>>>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>>>>>>>>
>>>>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>> [...]
>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>>>>>>>>> calling
>>>>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>>>>>>>>> use this
>>>>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>>>>>>>>> currently
>>>>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>>>>>>>>         From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>>>>>>>>         From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>>>>>>>>> when it is running ISR.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>>>>>>>>> we need to do it in a thread context.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>>>>>>>>> else display will drop frame.
>>>>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>>>>>>>>> 32 ms.sometimes.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>>>>>>>>
>>>>>>>>>>>>> The order should be kept.
>>>>>>>>>>>>> Let me use mouse cursor as an example.
>>>>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Got it, thanks for the clarification.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>>>>>>>>> programmed to the time a timeout in the first task in the busy list
>>>>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>>>>>>>>> got finished by the thread), we update the timer. When the timer
>>>>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>>>>>>>>> the busy list elements, until the time is lower then the actual time.
>>>>>>>>>>>
>>>>>>>>>>> At least with this we can reduce the data structures in this driver and
>>>>>>>>>>> make it more lightweight.
>>>>>>>>>>
>>>>>>>>>>      From my understanding, your proposed method can handle timeout case.
>>>>>>>>>>
>>>>>>>>>> However, the workqueue is also in charge of releasing tasks.
>>>>>>>>>> Do you take releasing tasks into consideration by using the proposed
>>>>>>>>>> timer method?
>>>>>>>>>> Furthermore, I think the code will become more complex if we also use
>>>>>>>>>> timer to implement releasing tasks.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Can't we call
>>>>>>>>>              clk_disable_unprepare(cmdq->clock);
>>>>>>>>>              cmdq_task_release(task);
>>>>>>>>> after invoking the callback?
>>>
>>> After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
>>> another BUG.
>>>
>>> (Quote some Linux 4.7 source code.)
>>>
>>>    605 void clk_unprepare(struct clk *clk)
>>>    606 {
>>>    607         if (IS_ERR_OR_NULL(clk))
>>>    608                 return;
>>>    609
>>>    610         clk_prepare_lock();                      // <-- Here
>>>    611         clk_core_unprepare(clk->core);
>>>    612         clk_prepare_unlock();
>>>    613 }
>>>    614 EXPORT_SYMBOL_GPL(clk_unprepare);
>>>
>>>     91 static void clk_prepare_lock(void)
>>>     92 {
>>>     93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
>>>     94                 if (prepare_owner == current) {
>>>     95                         prepare_refcnt++;
>>>     96                         return;
>>>     97                 }
>>>     98                 mutex_lock(&prepare_lock);
>>>     99         }
>>>    100         WARN_ON_ONCE(prepare_owner != NULL);
>>>    101         WARN_ON_ONCE(prepare_refcnt != 0);
>>>    102         prepare_owner = current;
>>>    103         prepare_refcnt = 1;
>>>    104 }
>>>
>>> So, 'unprepare' can sleep and cannot be put into ISR.
>>> I also try to put it into a timer, but the error is the same
>>> since timer callback is executed by softirq.
>>>
>>> We need clk_disable_unprepare() since it can save power consumption
>>> in idle.
>>
>> We can call clk_prepare in probe and then use clk_enable/clk_disable,
>> which don't sleep.
>>
>> Regards,
>> Matthias
>
> Hi Matthias,
>
> Because clock gate and MUX are controlled by clk_enable/clk_disable,
> and PLL is controlled by clk_prepare/clk_unprepare,
> I still need to call clk_unprepare.
>
> After I remove releasing buffer, releasing task, and timeout task from
> work, the work can be detached from task.
>
> Therefore, I can use the following flow to reduce the number of works.
>
> if task_busy_list from empty to non-empty
> 	clk_prepare_enable
> if task_busy_list from non-empty to empty
> 	in ISR, add work for clk_disable_unprepare
>
> What do you think of this solution?

Can't we just call clk_prepare in probe and clk_unprepare in remove? I 
think this could be a good starting point, and if we see, that we need 
to save more energy in the future, we can think of some other mechanism. 
What do you think?

Regards,
Matthias

>
> Thanks,
> HS
>
>>> Therefore, I plan to
>>> (1) move releasing buffer and task into ISR,
>>> (2) move timeout into timer, and
>>> (3) keep workqueue for clk_disable_unprepare().
>>>
>>> What do you think?
>>>
>>> Thanks,
>>> HS
>>>
>>>>>>>>
>>>>>>>> Do you mean just call these two functions in ISR?
>>>>>>>> My major concern is dma_free_coherent() and kfree() in
>>>>>>>> cmdq_task_release(task).
>>>>>>>
>>>>>>> Why do we need the dma calls at all? Can't we just calculate the
>>>>>>> physical address using __pa(x)?
>>>>>>
>>>>>> I prefer to use dma_map_single/dma_unmap_single.
>>>>>>
>>>>>
>>>>> Can you please elaborate why you need this. We don't do dma, so we
>>>>> should not use dma memory for this.
>>>>
>>>> We need a buffer to share between CPU and GCE, so we do need DMA.
>>>> CPU is in charge of writing GCE commands into this buffer.
>>>> GCE is in charge of reading and running GCE commands from this buffer.
>>>> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
>>>> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
>>>>
>>>> However, if we want to use timer to handle timeout, we need to release
>>>> memory in ISR.
>>>> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
>>>> instead of dma_alloc_coherent/dma_free_coherent is an alternative
>>>> solution, but taking care the synchronization between cache and memory
>>>> is the expected overhead.
>>>>
>>>>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
>>>>>>>> dma_alloc_coherent() and kzalloc(). Right?
>>>>>>>
>>>>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
>>>>>>> memory.
>>>>>>
>>>>>> I tested these two functions, and kfree was safe.
>>>>>> However, dma_free_coherent raised BUG.
>>>>>> BUG: failure at
>>>>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
>>>>>
>>>>> Just a general hint. Please try to evaluate on a recent kernel. It looks
>>>>> like as if you tried this on a v3.18 based one.
>>>>
>>>> This driver should be backward compatible to v3.18 for a MTK project.
>>>>
>>>>> Best regards,
>>>>> Matthias
>>>>
>>>> Thanks,
>>>> HS
>>>>
>>>>>> 1512 void vunmap(const void *addr)
>>>>>> 1513 {
>>>>>> 1514         BUG_ON(in_interrupt());		// <-- here
>>>>>> 1515         might_sleep();
>>>>>> 1516         if (addr)
>>>>>> 1517                 __vunmap(addr, 0);
>>>>>> 1518 }
>>>>>> 1519 EXPORT_SYMBOL(vunmap);
>>>>>>
>>>>>> Therefore, I plan to use kmalloc + dma_map_single instead of
>>>>>> dma_alloc_coherent, and dma_unmap_single + kfree instead of
>>>>>> dma_free_coherent.
>>>>>>
>>>>>> What do you think about the function replacement?
>>>>>>
>>>>>>>> If so, I can try to implement timeout by timer, and discuss with you
>>>>>>>> if I have further questions.
>>>>>>>>
>>>>>>>
>>>>>>> Sounds good :)
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Matthias
>>>>>>
>>>>>> Thanks,
>>>>>> HS
>>>>>>
>>>>>>>>> Regrading the clock, wouldn't it be easier to handle the clock
>>>>>>>>> enable/disable depending on the state of task_busy_list? I suppose we
>>>>>>>>> can't as we would need to check the task_busy_list of all threads, right?
>>>>>>>>>
>>>>>>>>> Regards,
>>>>>>>>> Matthias
>>>>>>>>
>>>>>>>> Thanks,
>>>>>>>> HS
>
>


From gschriss at gmail.com  Tue Jun 21 16:09:15 2016
From: gschriss at gmail.com (George Chriss)
Date: Tue, 21 Jun 2016 19:09:15 -0400
Subject: MediaTek MT53xx/MT588x Source Code / Linux v3.0.13
Message-ID: <CAKcQQDtag8EG3y30ZvDEPgijn3U1stcMvc2AxS1AsQMeZpghvA@mail.gmail.com>

Hi,

Kernel and U-Boot source code provided by Vizio covering LED 'Smart'
TVs is now available:
 https://github.com/spartan263/vizio_oss

It's an older kernel release but I hope it might find a place in kernel hacking.
Provides support for the following chipsets:
 MediaTek MT5368 (Cortex-A9 single-core)
 MediaTek MT5368_FPGA (ARM11)
 MediaTek MT5396 (Cortex-A9 dual-core)
 MediaTek MT5396_FPGA (ARM11)
 MediaTek MT5369 (Cortex-A9 single-core)
 MediaTek MT5389 (Arm11)
 MediaTek MT5398 (Cobra Cortex-A9 dual-core)
 MediaTek MT5880 (Viper Cortex-A9)
 MediaTek MT5881 (Arm11)
 ...in addition to NAND drivers and other hardware components.


Sincerely,
George


From hs.liao at mediatek.com  Tue Jun 21 22:43:13 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Wed, 22 Jun 2016 13:43:13 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <5769440D.5030505@gmail.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41>
 <1466152107.11184.14.camel@mtksdaap41> <57641E01.3070205@gmail.com>
 <1466488358.8045.19.camel@mtksdaap41> <5769440D.5030505@gmail.com>
Message-ID: <1466574193.27740.6.camel@mtksdaap41>

On Tue, 2016-06-21 at 15:41 +0200, Matthias Brugger wrote:
> 
> On 21/06/16 07:52, Horng-Shyang Liao wrote:
> > On Fri, 2016-06-17 at 17:57 +0200, Matthias Brugger wrote:
> >>
> >> On 17/06/16 10:28, Horng-Shyang Liao wrote:
> >>> Hi Matthias,
> >>>
> >>> On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
> >>>> Hi Matthias,
> >>>>
> >>>> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
> >>>>>
> >>>>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
> >>>>>> Hi Matthias,
> >>>>>>
> >>>>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
> >>>>>>>
> >>>>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
> >>>>>>>> Hi Matthias,
> >>>>>>>>
> >>>>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
> >>>>>>>>>
> >>>>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
> >>>>>>>>>> Hi Matthias,
> >>>>>>>>>>
> >>>>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
> >>>>>>>>>>>
> >>>>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
> >>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>> [...]
> >>>>>>>>>>>
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
> >>>>>>>>>>>>>>>>>>>>> +        }
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
> >>>>>>>>>>>>>>>>>>>>> task->command_size,
> >>>>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
> >>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
> >>>>>>>>>>>>>>>>>>>>> +    }
> >>>>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
> >>>>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
> >>>>>>>>>>>>>>>>>>>>> +}
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
> >>>>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
> >>>>>>>>>>>>>>>>>>>>> +{
> >>>>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
> >>>>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
> >>>>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
> >>>>>>>>>>>>>>>>>>>>> +    bool err;
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
> >>>>>>>>>>>>>>>>>>>>> +        err = true;
> >>>>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
> >>>>>>>>>>>>>>>>>>>>> +        err = false;
> >>>>>>>>>>>>>>>>>>>>> +    else
> >>>>>>>>>>>>>>>>>>>>> +        return;
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
> >>>>>>>>>>>>>>>>>>>>> +
> >>>>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
> >>>>>>>>>>>>>>>>>>>>> +                 list_entry) {
> >>>>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
> >>>>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
> >>>>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
> >>>>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
> >>>>>>>>>>>>>>>>>>>> commands in the
> >>>>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
> >>>>>>>>>>>>>>>>>>>> guarantees
> >>>>>>>>>>>>>>>>>>>> about dma_handle?
> >>>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
> >>>>>>>>>>>>>>>>>>> 2. Yes.
> >>>>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
> >>>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
> >>>>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
> >>>>>>>>>>>>>>>>>> calling
> >>>>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
> >>>>>>>>>>>>>>>>>> use this
> >>>>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
> >>>>>>>>>>>>>>>>>> currently
> >>>>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
> >>>>>>>>>>>>>>>>>         From CPU's point of view, tasks are linked by the busy list.
> >>>>>>>>>>>>>>>>>         From GCE's point of view, tasks are linked by the JUMP command.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
> >>>>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
> >>>>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
> >>>>>>>>>>>>>>>>> when it is running ISR.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
> >>>>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
> >>>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
> >>>>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
> >>>>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
> >>>>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
> >>>>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
> >>>>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
> >>>>>>>>>>>>>>>> we need to do it in a thread context.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
> >>>>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
> >>>>>>>>>>>>>>> else display will drop frame.
> >>>>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
> >>>>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
> >>>>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
> >>>>>>>>>>>>>>> 32 ms.sometimes.
> >>>>>>>>>>>>>>>
> >>>>>>>>>>>>>>
> >>>>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
> >>>>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
> >>>>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>> The order should be kept.
> >>>>>>>>>>>>> Let me use mouse cursor as an example.
> >>>>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
> >>>>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
> >>>>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
> >>>>>>>>>>>>>
> >>>>>>>>>>>>
> >>>>>>>>>>>> Got it, thanks for the clarification.
> >>>>>>>>>>>>
> >>>>>>>>>>>
> >>>>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
> >>>>>>>>>>> programmed to the time a timeout in the first task in the busy list
> >>>>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
> >>>>>>>>>>> got finished by the thread), we update the timer. When the timer
> >>>>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
> >>>>>>>>>>> the busy list elements, until the time is lower then the actual time.
> >>>>>>>>>>>
> >>>>>>>>>>> At least with this we can reduce the data structures in this driver and
> >>>>>>>>>>> make it more lightweight.
> >>>>>>>>>>
> >>>>>>>>>>      From my understanding, your proposed method can handle timeout case.
> >>>>>>>>>>
> >>>>>>>>>> However, the workqueue is also in charge of releasing tasks.
> >>>>>>>>>> Do you take releasing tasks into consideration by using the proposed
> >>>>>>>>>> timer method?
> >>>>>>>>>> Furthermore, I think the code will become more complex if we also use
> >>>>>>>>>> timer to implement releasing tasks.
> >>>>>>>>>>
> >>>>>>>>>
> >>>>>>>>> Can't we call
> >>>>>>>>>              clk_disable_unprepare(cmdq->clock);
> >>>>>>>>>              cmdq_task_release(task);
> >>>>>>>>> after invoking the callback?
> >>>
> >>> After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
> >>> another BUG.
> >>>
> >>> (Quote some Linux 4.7 source code.)
> >>>
> >>>    605 void clk_unprepare(struct clk *clk)
> >>>    606 {
> >>>    607         if (IS_ERR_OR_NULL(clk))
> >>>    608                 return;
> >>>    609
> >>>    610         clk_prepare_lock();                      // <-- Here
> >>>    611         clk_core_unprepare(clk->core);
> >>>    612         clk_prepare_unlock();
> >>>    613 }
> >>>    614 EXPORT_SYMBOL_GPL(clk_unprepare);
> >>>
> >>>     91 static void clk_prepare_lock(void)
> >>>     92 {
> >>>     93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
> >>>     94                 if (prepare_owner == current) {
> >>>     95                         prepare_refcnt++;
> >>>     96                         return;
> >>>     97                 }
> >>>     98                 mutex_lock(&prepare_lock);
> >>>     99         }
> >>>    100         WARN_ON_ONCE(prepare_owner != NULL);
> >>>    101         WARN_ON_ONCE(prepare_refcnt != 0);
> >>>    102         prepare_owner = current;
> >>>    103         prepare_refcnt = 1;
> >>>    104 }
> >>>
> >>> So, 'unprepare' can sleep and cannot be put into ISR.
> >>> I also try to put it into a timer, but the error is the same
> >>> since timer callback is executed by softirq.
> >>>
> >>> We need clk_disable_unprepare() since it can save power consumption
> >>> in idle.
> >>
> >> We can call clk_prepare in probe and then use clk_enable/clk_disable,
> >> which don't sleep.
> >>
> >> Regards,
> >> Matthias
> >
> > Hi Matthias,
> >
> > Because clock gate and MUX are controlled by clk_enable/clk_disable,
> > and PLL is controlled by clk_prepare/clk_unprepare,
> > I still need to call clk_unprepare.
> >
> > After I remove releasing buffer, releasing task, and timeout task from
> > work, the work can be detached from task.
> >
> > Therefore, I can use the following flow to reduce the number of works.
> >
> > if task_busy_list from empty to non-empty
> > 	clk_prepare_enable
> > if task_busy_list from non-empty to empty
> > 	in ISR, add work for clk_disable_unprepare
> >
> > What do you think of this solution?
> 
> Can't we just call clk_prepare in probe and clk_unprepare in remove? I 
> think this could be a good starting point, and if we see, that we need 
> to save more energy in the future, we can think of some other mechanism. 
> What do you think?
> 
> Regards,
> Matthias

Hi Matthias,

As far as I know, we should call clk_unprepare to save more energy.

May I call clk_prepare in probe/resume and clk_unprepare in
remove/suspend in this patch, and then prepare another patch to call
clk_unprepare in idle to save more energy?

Thanks,
HS

> >
> > Thanks,
> > HS
> >
> >>> Therefore, I plan to
> >>> (1) move releasing buffer and task into ISR,
> >>> (2) move timeout into timer, and
> >>> (3) keep workqueue for clk_disable_unprepare().
> >>>
> >>> What do you think?
> >>>
> >>> Thanks,
> >>> HS
> >>>
> >>>>>>>>
> >>>>>>>> Do you mean just call these two functions in ISR?
> >>>>>>>> My major concern is dma_free_coherent() and kfree() in
> >>>>>>>> cmdq_task_release(task).
> >>>>>>>
> >>>>>>> Why do we need the dma calls at all? Can't we just calculate the
> >>>>>>> physical address using __pa(x)?
> >>>>>>
> >>>>>> I prefer to use dma_map_single/dma_unmap_single.
> >>>>>>
> >>>>>
> >>>>> Can you please elaborate why you need this. We don't do dma, so we
> >>>>> should not use dma memory for this.
> >>>>
> >>>> We need a buffer to share between CPU and GCE, so we do need DMA.
> >>>> CPU is in charge of writing GCE commands into this buffer.
> >>>> GCE is in charge of reading and running GCE commands from this buffer.
> >>>> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
> >>>> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
> >>>>
> >>>> However, if we want to use timer to handle timeout, we need to release
> >>>> memory in ISR.
> >>>> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
> >>>> instead of dma_alloc_coherent/dma_free_coherent is an alternative
> >>>> solution, but taking care the synchronization between cache and memory
> >>>> is the expected overhead.
> >>>>
> >>>>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
> >>>>>>>> dma_alloc_coherent() and kzalloc(). Right?
> >>>>>>>
> >>>>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
> >>>>>>> memory.
> >>>>>>
> >>>>>> I tested these two functions, and kfree was safe.
> >>>>>> However, dma_free_coherent raised BUG.
> >>>>>> BUG: failure at
> >>>>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
> >>>>>
> >>>>> Just a general hint. Please try to evaluate on a recent kernel. It looks
> >>>>> like as if you tried this on a v3.18 based one.
> >>>>
> >>>> This driver should be backward compatible to v3.18 for a MTK project.
> >>>>
> >>>>> Best regards,
> >>>>> Matthias
> >>>>
> >>>> Thanks,
> >>>> HS
> >>>>
> >>>>>> 1512 void vunmap(const void *addr)
> >>>>>> 1513 {
> >>>>>> 1514         BUG_ON(in_interrupt());		// <-- here
> >>>>>> 1515         might_sleep();
> >>>>>> 1516         if (addr)
> >>>>>> 1517                 __vunmap(addr, 0);
> >>>>>> 1518 }
> >>>>>> 1519 EXPORT_SYMBOL(vunmap);
> >>>>>>
> >>>>>> Therefore, I plan to use kmalloc + dma_map_single instead of
> >>>>>> dma_alloc_coherent, and dma_unmap_single + kfree instead of
> >>>>>> dma_free_coherent.
> >>>>>>
> >>>>>> What do you think about the function replacement?
> >>>>>>
> >>>>>>>> If so, I can try to implement timeout by timer, and discuss with you
> >>>>>>>> if I have further questions.
> >>>>>>>>
> >>>>>>>
> >>>>>>> Sounds good :)
> >>>>>>>
> >>>>>>> Thanks,
> >>>>>>> Matthias
> >>>>>>
> >>>>>> Thanks,
> >>>>>> HS
> >>>>>>
> >>>>>>>>> Regrading the clock, wouldn't it be easier to handle the clock
> >>>>>>>>> enable/disable depending on the state of task_busy_list? I suppose we
> >>>>>>>>> can't as we would need to check the task_busy_list of all threads, right?
> >>>>>>>>>
> >>>>>>>>> Regards,
> >>>>>>>>> Matthias
> >>>>>>>>
> >>>>>>>> Thanks,
> >>>>>>>> HS
> >
> >




From eddie.huang at mediatek.com  Tue Jun 21 22:45:42 2016
From: eddie.huang at mediatek.com (Eddie Huang)
Date: Wed, 22 Jun 2016 13:45:42 +0800
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <20160621095748.GE28248@8bytes.org>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621095748.GE28248@8bytes.org>
Message-ID: <1466574342.27791.8.camel@mtksdaap41>

On Tue, 2016-06-21 at 17:57 +0800, Joerg Roedel wrote:
> On Wed, Jun 08, 2016 at 05:51:01PM +0800, honghui.zhang at mediatek.com wrote:
> > From: Honghui Zhang <honghui.zhang at mediatek.com>
> > 
> > Add the dtsi node of iommu and smi for mt2701.
> > 
> > Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> > ---
> >  arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 51 insertions(+)
> 
> Okay, I pushed my arm/mediatek branch to my tree at
> 
> 	git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
> 
> Please base the patch on that branch and re-send.
> 

I think it is better let Mediatek SoC maintainer Matthias to accept dtsi
patch like other drivers. This can avoid dtsi conflict. As I
remember,last time MT8173 IOMMU dtsi patch accepted in iommu tree and
mt8173.dtsi had conflict with arm soc tree at the merge window. Honghui
should resend this patch to Matthias, and elaborate your dependency with
clock and power domain dtsi, then Matthias know the merge sequence. 

Eddie
Thanks




From erin.lo at mediatek.com  Wed Jun 22 00:40:19 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:19 +0800
Subject: [PATCH v9 00/10] Add clock support for Mediatek MT2701 
Message-ID: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>

This series is based on v4.7-rc1, add clock and reset controller support
for Mediatek MT2701.

This series also refined makefile and Kconfig to support configurable
multiple SoC clock support.

changes since v8:
- Rebase to v4.7-rc1.
- Include mt2701-resets.h in mt2701.dtsi.
- Remove an unused property from apmixedsys DT node.

changes since v7:
- Rebase to clk-next.
- Implement subsystem clocks in seperated files.
- Replace critical clock enabling with CLK_IS_CRITICAL flag.
- Reduce most clock registrations in CLK_OF_DECLARE().
- Remove __init and __initconst from most init fucntions and data,
  and replace driver registration with platform_driver_register().
- Replace some common function or variable names with unique names.
- Use real clock for UARTs.

changes since v6:
- Rebase to v4.6-rc1.
- Register subsystem clocks in probe() instead of CLK_OF_DECLARE().
- Add clocks that referred by subsystem clocks.
- Fix clk_data size of apmixedsys.
- Add config options for each subsystem clock provider.

changes since v5:
- Rebase to v4.5-rc1 and [1].
- Enable critical clocks for MT2701
- Refine dt-binding documents, add reset controller support for hifsys.

changes since v4:
- Rebase to v4.5-rc1.
- Remove CLK_SET_RATE_PARENT from divider flags.
- Add img_jpgdec_smi clock.
- Move clk/mediatek/Kconfig into menu section in clk/Kconfig.

changes since v3:
- Change the parent of mm_mdp_bls_26m from clk26m to pwm_sel.

changes since v2:
- Fix ethsys definition.
- Replace read-modify-write with regmap_update_bits() in clock operations.
- Move mt2701-resets.h to include/dt-bindings/reset/.
- Add hifsys reset patch from John Crispin.

changes since v1:
- Document MT2701 compatible strings.

[1] https://patchwork.kernel.org/patch/8147901/

Erin Lo (1):
  arm: dts: mt2701: Use real clock for UARTs

James Liao (5):
  clk: fix initial state of critical clock's parents
  clk: mediatek: remove __init from clk registration functions
  clk: mediatek: Refine the makefile to support multiple clock drivers
  dt-bindings: ARM: Mediatek: Document bindings for MT2701
  arm: dts: mt2701: Add clock controller device nodes

Shunli Wang (4):
  clk: mediatek: Add dt-bindings for MT2701 clocks
  clk: mediatek: Add MT2701 clock support
  reset: mediatek: Add MT2701 reset controller dt-binding file
  reset: mediatek: Add MT2701 reset driver

 .../bindings/arm/mediatek/mediatek,apmixedsys.txt  |    3 +-
 .../bindings/arm/mediatek/mediatek,bdpsys.txt      |   22 +
 .../bindings/arm/mediatek/mediatek,ethsys.txt      |   22 +
 .../bindings/arm/mediatek/mediatek,hifsys.txt      |   24 +
 .../bindings/arm/mediatek/mediatek,imgsys.txt      |    3 +-
 .../bindings/arm/mediatek/mediatek,infracfg.txt    |    3 +-
 .../bindings/arm/mediatek/mediatek,mmsys.txt       |    3 +-
 .../bindings/arm/mediatek/mediatek,pericfg.txt     |    3 +-
 .../bindings/arm/mediatek/mediatek,topckgen.txt    |    3 +-
 .../bindings/arm/mediatek/mediatek,vdecsys.txt     |    3 +-
 arch/arm/boot/dts/mt2701.dtsi                      |   50 +-
 drivers/clk/Kconfig                                |    1 +
 drivers/clk/clk.c                                  |    9 +-
 drivers/clk/mediatek/Kconfig                       |   73 ++
 drivers/clk/mediatek/Makefile                      |   13 +-
 drivers/clk/mediatek/clk-gate.c                    |   54 +-
 drivers/clk/mediatek/clk-gate.h                    |    2 +
 drivers/clk/mediatek/clk-mt2701-bdp.c              |  140 +++
 drivers/clk/mediatek/clk-mt2701-eth.c              |   82 ++
 drivers/clk/mediatek/clk-mt2701-hif.c              |   81 ++
 drivers/clk/mediatek/clk-mt2701-img.c              |   82 ++
 drivers/clk/mediatek/clk-mt2701-mm.c               |  125 +++
 drivers/clk/mediatek/clk-mt2701-vdec.c             |   93 ++
 drivers/clk/mediatek/clk-mt2701.c                  | 1037 ++++++++++++++++++++
 drivers/clk/mediatek/clk-mtk.c                     |   52 +-
 drivers/clk/mediatek/clk-mtk.h                     |   41 +-
 drivers/clk/mediatek/clk-pll.c                     |    3 +-
 include/dt-bindings/clock/mt2701-clk.h             |  486 +++++++++
 include/dt-bindings/reset/mt2701-resets.h          |   83 ++
 29 files changed, 2567 insertions(+), 29 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
 create mode 100644 drivers/clk/mediatek/Kconfig
 create mode 100644 drivers/clk/mediatek/clk-mt2701-bdp.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-eth.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-hif.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-img.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-mm.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-vdec.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701.c
 create mode 100644 include/dt-bindings/clock/mt2701-clk.h
 create mode 100644 include/dt-bindings/reset/mt2701-resets.h

--
1.7.9.5



From erin.lo at mediatek.com  Wed Jun 22 00:40:20 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:20 +0800
Subject: [PATCH v9 01/10] clk: fix initial state of critical clock's parents
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-2-git-send-email-erin.lo@mediatek.com>

From: James Liao <jamesjj.liao at mediatek.com>

This patch fixed wrong state of parent clocks if they are registered
after critical clocks.

Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
---
 drivers/clk/clk.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index d584004..e9f5f89 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2388,8 +2388,15 @@ static int __clk_core_init(struct clk_core *core)
 	hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
 		struct clk_core *parent = __clk_init_parent(orphan);
 
-		if (parent)
+		if (parent) {
 			clk_core_reparent(orphan, parent);
+
+			if (orphan->prepare_count)
+				clk_core_prepare(parent);
+
+			if (orphan->enable_count)
+				clk_core_enable(parent);
+		}
 	}
 
 	/*
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:22 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:22 +0800
Subject: [PATCH v9 03/10] clk: mediatek: Refine the makefile to support
 multiple clock drivers
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-4-git-send-email-erin.lo@mediatek.com>

From: James Liao <jamesjj.liao at mediatek.com>

Add a Kconfig to define clock configuration for each SoC, and
modify the Makefile to build drivers that only selected in config.

Signed-off-by: Shunli Wang <shunli.wang at mediatek.com>
Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>
---
 drivers/clk/Kconfig           |  1 +
 drivers/clk/mediatek/Kconfig  | 23 +++++++++++++++++++++++
 drivers/clk/mediatek/Makefile |  6 +++---
 3 files changed, 27 insertions(+), 3 deletions(-)
 create mode 100644 drivers/clk/mediatek/Kconfig

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 53ddba2..665c03b 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -208,6 +208,7 @@ config COMMON_CLK_OXNAS
 
 source "drivers/clk/bcm/Kconfig"
 source "drivers/clk/hisilicon/Kconfig"
+source "drivers/clk/mediatek/Kconfig"
 source "drivers/clk/mvebu/Kconfig"
 source "drivers/clk/qcom/Kconfig"
 source "drivers/clk/renesas/Kconfig"
diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
new file mode 100644
index 0000000..dc224e6
--- /dev/null
+++ b/drivers/clk/mediatek/Kconfig
@@ -0,0 +1,23 @@
+#
+# MediaTek SoC drivers
+#
+config COMMON_CLK_MEDIATEK
+	bool
+	---help---
+	  Mediatek SoCs' clock support.
+
+config COMMON_CLK_MT8135
+	bool "Clock driver for Mediatek MT8135"
+	depends on COMMON_CLK
+	select COMMON_CLK_MEDIATEK
+	default ARCH_MEDIATEK
+	---help---
+	  This driver supports Mediatek MT8135 clocks.
+
+config COMMON_CLK_MT8173
+	bool "Clock driver for Mediatek MT8173"
+	depends on COMMON_CLK
+	select COMMON_CLK_MEDIATEK
+	default ARCH_MEDIATEK
+	---help---
+	  This driver supports Mediatek MT8173 clocks.
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 95fdfac..32e7222 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,4 +1,4 @@
-obj-y += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
+obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
 obj-$(CONFIG_RESET_CONTROLLER) += reset.o
-obj-y += clk-mt8135.o
-obj-y += clk-mt8173.o
+obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
+obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:21 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:21 +0800
Subject: [PATCH v9 02/10] clk: mediatek: remove __init from clk registration
 functions
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-3-git-send-email-erin.lo@mediatek.com>

From: James Liao <jamesjj.liao at mediatek.com>

Remove __init from functions that will be used by init functions
that support probe deferral.

Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
---
 drivers/clk/mediatek/clk-gate.c |  2 +-
 drivers/clk/mediatek/clk-mtk.c  | 12 ++++++------
 drivers/clk/mediatek/clk-pll.c  |  2 +-
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index 2a76901..d8787bf 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -97,7 +97,7 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
 	.disable	= mtk_cg_disable_inv,
 };
 
-struct clk * __init mtk_clk_register_gate(
+struct clk *mtk_clk_register_gate(
 		const char *name,
 		const char *parent_name,
 		struct regmap *regmap,
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index 5ada644..bb30f70 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -24,7 +24,7 @@
 #include "clk-mtk.h"
 #include "clk-gate.h"
 
-struct clk_onecell_data * __init mtk_alloc_clk_data(unsigned int clk_num)
+struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num)
 {
 	int i;
 	struct clk_onecell_data *clk_data;
@@ -49,7 +49,7 @@ err_out:
 	return NULL;
 }
 
-void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
+void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
 		int num, struct clk_onecell_data *clk_data)
 {
 	int i;
@@ -72,7 +72,7 @@ void __init mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
 	}
 }
 
-void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
+void mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
 		int num, struct clk_onecell_data *clk_data)
 {
 	int i;
@@ -95,7 +95,7 @@ void __init mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
 	}
 }
 
-int __init mtk_clk_register_gates(struct device_node *node,
+int mtk_clk_register_gates(struct device_node *node,
 		const struct mtk_gate *clks,
 		int num, struct clk_onecell_data *clk_data)
 {
@@ -135,7 +135,7 @@ int __init mtk_clk_register_gates(struct device_node *node,
 	return 0;
 }
 
-struct clk * __init mtk_clk_register_composite(const struct mtk_composite *mc,
+struct clk *mtk_clk_register_composite(const struct mtk_composite *mc,
 		void __iomem *base, spinlock_t *lock)
 {
 	struct clk *clk;
@@ -222,7 +222,7 @@ err_out:
 	return ERR_PTR(ret);
 }
 
-void __init mtk_clk_register_composites(const struct mtk_composite *mcs,
+void mtk_clk_register_composites(const struct mtk_composite *mcs,
 		int num, void __iomem *base, spinlock_t *lock,
 		struct clk_onecell_data *clk_data)
 {
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
index 966cab1..0c2deac 100644
--- a/drivers/clk/mediatek/clk-pll.c
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -313,7 +313,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
 	return clk;
 }
 
-void __init mtk_clk_register_plls(struct device_node *node,
+void mtk_clk_register_plls(struct device_node *node,
 		const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data)
 {
 	void __iomem *base;
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:23 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:23 +0800
Subject: [PATCH v9 04/10] dt-bindings: ARM: Mediatek: Document bindings for
 MT2701
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-5-git-send-email-erin.lo@mediatek.com>

From: James Liao <jamesjj.liao at mediatek.com>

This patch adds the binding documentation for apmixedsys, bdpsys,
ethsys, hifsys, imgsys, infracfg, mmsys, pericfg, topckgen and
vdecsys for Mediatek MT2701.

Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
Acked-by: Rob Herring <robh at kernel.org>
---
 .../bindings/arm/mediatek/mediatek,apmixedsys.txt  |  3 ++-
 .../bindings/arm/mediatek/mediatek,bdpsys.txt      | 22 ++++++++++++++++++++
 .../bindings/arm/mediatek/mediatek,ethsys.txt      | 22 ++++++++++++++++++++
 .../bindings/arm/mediatek/mediatek,hifsys.txt      | 24 ++++++++++++++++++++++
 .../bindings/arm/mediatek/mediatek,imgsys.txt      |  3 ++-
 .../bindings/arm/mediatek/mediatek,infracfg.txt    |  3 ++-
 .../bindings/arm/mediatek/mediatek,mmsys.txt       |  3 ++-
 .../bindings/arm/mediatek/mediatek,pericfg.txt     |  3 ++-
 .../bindings/arm/mediatek/mediatek,topckgen.txt    |  3 ++-
 .../bindings/arm/mediatek/mediatek,vdecsys.txt     |  3 ++-
 10 files changed, 82 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
 create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt

diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
index 936166f..cb0054a 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt
@@ -5,7 +5,8 @@ The Mediatek apmixedsys controller provides the PLLs to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-apmixedsys"
 	- "mediatek,mt8135-apmixedsys"
 	- "mediatek,mt8173-apmixedsys"
 - #clock-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
new file mode 100644
index 0000000..4137196
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,bdpsys.txt
@@ -0,0 +1,22 @@
+Mediatek bdpsys controller
+============================
+
+The Mediatek bdpsys controller provides various clocks to the system.
+
+Required Properties:
+
+- compatible: Should be:
+	- "mediatek,mt2701-bdpsys", "syscon"
+- #clock-cells: Must be 1
+
+The bdpsys controller uses the common clk binding from
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+The available clocks are defined in dt-bindings/clock/mt*-clk.h.
+
+Example:
+
+bdpsys: clock-controller at 1c000000 {
+	compatible = "mediatek,mt2701-bdpsys", "syscon";
+	reg = <0 0x1c000000 0 0x1000>;
+	#clock-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
new file mode 100644
index 0000000..768f3a5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,ethsys.txt
@@ -0,0 +1,22 @@
+Mediatek ethsys controller
+============================
+
+The Mediatek ethsys controller provides various clocks to the system.
+
+Required Properties:
+
+- compatible: Should be:
+	- "mediatek,mt2701-ethsys", "syscon"
+- #clock-cells: Must be 1
+
+The ethsys controller uses the common clk binding from
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+The available clocks are defined in dt-bindings/clock/mt*-clk.h.
+
+Example:
+
+ethsys: clock-controller at 1b000000 {
+	compatible = "mediatek,mt2701-ethsys", "syscon";
+	reg = <0 0x1b000000 0 0x1000>;
+	#clock-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
new file mode 100644
index 0000000..beed7b5
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,hifsys.txt
@@ -0,0 +1,24 @@
+Mediatek hifsys controller
+============================
+
+The Mediatek hifsys controller provides various clocks and reset
+outputs to the system.
+
+Required Properties:
+
+- compatible: Should be:
+	- "mediatek,mt2701-hifsys", "syscon"
+- #clock-cells: Must be 1
+
+The hifsys controller uses the common clk binding from
+Documentation/devicetree/bindings/clock/clock-bindings.txt
+The available clocks are defined in dt-bindings/clock/mt*-clk.h.
+
+Example:
+
+hifsys: clock-controller at 1a000000 {
+	compatible = "mediatek,mt2701-hifsys", "syscon";
+	reg = <0 0x1a000000 0 0x1000>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
index b1f2ce1..f6a9166 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,imgsys.txt
@@ -5,7 +5,8 @@ The Mediatek imgsys controller provides various clocks to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-imgsys", "syscon"
 	- "mediatek,mt8173-imgsys", "syscon"
 - #clock-cells: Must be 1
 
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
index aaf8d14..1620ec2 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt
@@ -6,7 +6,8 @@ outputs to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-infracfg", "syscon"
 	- "mediatek,mt8135-infracfg", "syscon"
 	- "mediatek,mt8173-infracfg", "syscon"
 - #clock-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
index 4385946..67dd2e4 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mmsys.txt
@@ -5,7 +5,8 @@ The Mediatek mmsys controller provides various clocks to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-mmsys", "syscon"
 	- "mediatek,mt8173-mmsys", "syscon"
 - #clock-cells: Must be 1
 
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
index 2f6ff86..e494366 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt
@@ -6,7 +6,8 @@ outputs to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-pericfg", "syscon"
 	- "mediatek,mt8135-pericfg", "syscon"
 	- "mediatek,mt8173-pericfg", "syscon"
 - #clock-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
index f9e9179..9f2fe78 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt
@@ -5,7 +5,8 @@ The Mediatek topckgen controller provides various clocks to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-topckgen"
 	- "mediatek,mt8135-topckgen"
 	- "mediatek,mt8173-topckgen"
 - #clock-cells: Must be 1
diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
index 1faacf1..2440f73 100644
--- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,vdecsys.txt
@@ -5,7 +5,8 @@ The Mediatek vdecsys controller provides various clocks to the system.
 
 Required Properties:
 
-- compatible: Should be:
+- compatible: Should be one of:
+	- "mediatek,mt2701-vdecsys", "syscon"
 	- "mediatek,mt8173-vdecsys", "syscon"
 - #clock-cells: Must be 1
 
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:26 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:26 +0800
Subject: [PATCH v9 07/10] reset: mediatek: Add MT2701 reset controller
 dt-binding file
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-8-git-send-email-erin.lo@mediatek.com>

From: Shunli Wang <shunli.wang at mediatek.com>

Dt-binding file about reset controller is used to provide
kinds of definition, which is referenced by dts file and
IC-specified reset controller driver code.

Signed-off-by: Shunli Wang <shunli.wang at mediatek.com>
Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
Acked-by: Philipp Zabel <p.zabel at pengutronix.de>
---
 include/dt-bindings/reset/mt2701-resets.h | 83 +++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 include/dt-bindings/reset/mt2701-resets.h

diff --git a/include/dt-bindings/reset/mt2701-resets.h b/include/dt-bindings/reset/mt2701-resets.h
new file mode 100644
index 0000000..aaf0305
--- /dev/null
+++ b/include/dt-bindings/reset/mt2701-resets.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015 MediaTek, Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT2701
+#define _DT_BINDINGS_RESET_CONTROLLER_MT2701
+
+/* INFRACFG resets */
+#define MT2701_INFRA_EMI_REG_RST		0
+#define MT2701_INFRA_DRAMC0_A0_RST		1
+#define MT2701_INFRA_FHCTL_RST			2
+#define MT2701_INFRA_APCIRQ_EINT_RST		3
+#define MT2701_INFRA_APXGPT_RST			4
+#define MT2701_INFRA_SCPSYS_RST			5
+#define MT2701_INFRA_KP_RST			6
+#define MT2701_INFRA_PMIC_WRAP_RST		7
+#define MT2701_INFRA_MIPI_RST			8
+#define MT2701_INFRA_IRRX_RST			9
+#define MT2701_INFRA_CEC_RST			10
+#define MT2701_INFRA_EMI_RST			32
+#define MT2701_INFRA_DRAMC0_RST			34
+#define MT2701_INFRA_TRNG_RST			37
+#define MT2701_INFRA_SYSIRQ_RST			38
+
+/*  PERICFG resets */
+#define MT2701_PERI_UART0_SW_RST		0
+#define MT2701_PERI_UART1_SW_RST		1
+#define MT2701_PERI_UART2_SW_RST		2
+#define MT2701_PERI_UART3_SW_RST		3
+#define MT2701_PERI_GCPU_SW_RST			5
+#define MT2701_PERI_BTIF_SW_RST			6
+#define MT2701_PERI_PWM_SW_RST			8
+#define MT2701_PERI_AUXADC_SW_RST		10
+#define MT2701_PERI_DMA_SW_RST			11
+#define MT2701_PERI_NFI_SW_RST			14
+#define MT2701_PERI_NLI_SW_RST			15
+#define MT2701_PERI_THERM_SW_RST		16
+#define MT2701_PERI_MSDC2_SW_RST		17
+#define MT2701_PERI_MSDC0_SW_RST		19
+#define MT2701_PERI_MSDC1_SW_RST		20
+#define MT2701_PERI_I2C0_SW_RST			22
+#define MT2701_PERI_I2C1_SW_RST			23
+#define MT2701_PERI_I2C2_SW_RST			24
+#define MT2701_PERI_I2C3_SW_RST			25
+#define MT2701_PERI_USB_SW_RST			28
+#define MT2701_PERI_ETH_SW_RST			29
+#define MT2701_PERI_SPI0_SW_RST			33
+
+/* TOPRGU resets */
+#define MT2701_TOPRGU_INFRA_RST			0
+#define MT2701_TOPRGU_MM_RST			1
+#define MT2701_TOPRGU_MFG_RST			2
+#define MT2701_TOPRGU_ETHDMA_RST		3
+#define MT2701_TOPRGU_VDEC_RST			4
+#define MT2701_TOPRGU_VENC_IMG_RST		5
+#define MT2701_TOPRGU_DDRPHY_RST		6
+#define MT2701_TOPRGU_MD_RST			7
+#define MT2701_TOPRGU_INFRA_AO_RST		8
+#define MT2701_TOPRGU_CONN_RST			9
+#define MT2701_TOPRGU_APMIXED_RST		10
+#define MT2701_TOPRGU_HIFSYS_RST		11
+#define MT2701_TOPRGU_CONN_MCU_RST		12
+#define MT2701_TOPRGU_BDP_DISP_RST		13
+
+/* HIFSYS resets */
+#define MT2701_HIFSYS_UHOST0_RST		3
+#define MT2701_HIFSYS_UHOST1_RST		4
+#define MT2701_HIFSYS_UPHY0_RST			21
+#define MT2701_HIFSYS_UPHY1_RST			22
+#define MT2701_HIFSYS_PCIE0_RST			24
+#define MT2701_HIFSYS_PCIE1_RST			25
+#define MT2701_HIFSYS_PCIE2_RST			26
+
+#endif  /* _DT_BINDINGS_RESET_CONTROLLER_MT2701 */
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:29 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:29 +0800
Subject: [PATCH v9 10/10] arm: dts: mt2701: Use real clock for UARTs
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-11-git-send-email-erin.lo@mediatek.com>

We used to use a fixed rate clock for the UARTs. Now that we have clock
support we can associate the correct clocks to the UARTs and drop the
26MHz fixed rate UART clock.

Signed-off-by: Erin Lo <erin.lo at mediatek.com>
---
 arch/arm/boot/dts/mt2701.dtsi | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index c9a8dbf..7eab6f4 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -73,12 +73,6 @@
 		#clock-cells = <0>;
 	};
 
-	uart_clk: dummy26m {
-		compatible = "fixed-clock";
-		clock-frequency = <26000000>;
-		#clock-cells = <0>;
-	};
-
 	clk26m: oscillator at 0 {
 		compatible = "fixed-clock";
 		#clock-cells = <0>;
@@ -186,7 +180,8 @@
 			     "mediatek,mt6577-uart";
 		reg = <0 0x11002000 0 0x400>;
 		interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_LOW>;
-		clocks = <&uart_clk>;
+		clocks = <&pericfg CLK_PERI_UART0_SEL>, <&pericfg CLK_PERI_UART0>;
+		clock-names = "baud", "bus";
 		status = "disabled";
 	};
 
@@ -195,7 +190,8 @@
 			     "mediatek,mt6577-uart";
 		reg = <0 0x11003000 0 0x400>;
 		interrupts = <GIC_SPI 52 IRQ_TYPE_LEVEL_LOW>;
-		clocks = <&uart_clk>;
+		clocks = <&pericfg CLK_PERI_UART1_SEL>, <&pericfg CLK_PERI_UART1>;
+		clock-names = "baud", "bus";
 		status = "disabled";
 	};
 
@@ -204,7 +200,8 @@
 			     "mediatek,mt6577-uart";
 		reg = <0 0x11004000 0 0x400>;
 		interrupts = <GIC_SPI 53 IRQ_TYPE_LEVEL_LOW>;
-		clocks = <&uart_clk>;
+		clocks = <&pericfg CLK_PERI_UART2_SEL>, <&pericfg CLK_PERI_UART2>;
+		clock-names = "baud", "bus";
 		status = "disabled";
 	};
 
@@ -213,7 +210,8 @@
 			     "mediatek,mt6577-uart";
 		reg = <0 0x11005000 0 0x400>;
 		interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_LOW>;
-		clocks = <&uart_clk>;
+		clocks = <&pericfg CLK_PERI_UART3_SEL>, <&pericfg CLK_PERI_UART3>;
+		clock-names = "baud", "bus";
 		status = "disabled";
 	};
 };
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:27 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:27 +0800
Subject: [PATCH v9 08/10] reset: mediatek: Add MT2701 reset driver
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-9-git-send-email-erin.lo@mediatek.com>

From: Shunli Wang <shunli.wang at mediatek.com>

In infrasys and perifsys, there are many reset
control bits for kinds of modules. These bits are
used as actual reset controllers to be registered
into kernel's generic reset controller framework.

Signed-off-by: Shunli Wang <shunli.wang at mediatek.com>
Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
Acked-by: Philipp Zabel <p.zabel at pengutronix.de>
---
 drivers/clk/mediatek/clk-mt2701-hif.c | 2 ++
 drivers/clk/mediatek/clk-mt2701.c     | 4 ++++
 2 files changed, 6 insertions(+)

diff --git a/drivers/clk/mediatek/clk-mt2701-hif.c b/drivers/clk/mediatek/clk-mt2701-hif.c
index 3f6cea2..28014bf 100644
--- a/drivers/clk/mediatek/clk-mt2701-hif.c
+++ b/drivers/clk/mediatek/clk-mt2701-hif.c
@@ -55,6 +55,8 @@ static void mtk_hifsys_init(struct device_node *node)
 	if (r)
 		pr_err("%s(): could not register clock provider: %d\n",
 			__func__, r);
+
+	mtk_register_reset_controller(node, 1, 0x34);
 }
 
 static const struct of_device_id of_match_clk_mt2701_hif[] = {
diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
index 08a2954..b3cde20 100644
--- a/drivers/clk/mediatek/clk-mt2701.c
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -791,6 +791,8 @@ static void mtk_infrasys_init(struct device_node *node)
 	if (r)
 		pr_err("%s(): could not register clock provider: %d\n",
 			__func__, r);
+
+	mtk_register_reset_controller(node, 2, 0x30);
 }
 
 static const struct mtk_gate_regs peri0_cg_regs = {
@@ -911,6 +913,8 @@ static void mtk_pericfg_init(struct device_node *node)
 	if (r)
 		pr_err("%s(): could not register clock provider: %d\n",
 			__func__, r);
+
+	mtk_register_reset_controller(node, 2, 0x0);
 }
 
 #define MT8590_PLL_FMAX		(2000 * MHZ)
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:28 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:28 +0800
Subject: [PATCH v9 09/10] arm: dts: mt2701: Add clock controller device nodes
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-10-git-send-email-erin.lo@mediatek.com>

From: James Liao <jamesjj.liao at mediatek.com>

Add clock controller nodes for MT2701, include topckgen, infracfg,
pericfg, apmixedsys, mmsys, imgsys, vdecsys, hifsys, ethsys and
bdpsys. This patch also add two oscillators that provide clocks for
MT2701.

Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
---
 arch/arm/boot/dts/mt2701.dtsi | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 18596a2..c9a8dbf 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -12,8 +12,10 @@
  * GNU General Public License for more details.
  */
 
+#include <dt-bindings/clock/mt2701-clk.h>
 #include <dt-bindings/interrupt-controller/irq.h>
 #include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/reset/mt2701-resets.h>
 #include "skeleton64.dtsi"
 #include "mt2701-pinfunc.h"
 
@@ -77,6 +79,20 @@
 		#clock-cells = <0>;
 	};
 
+	clk26m: oscillator at 0 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <26000000>;
+		clock-output-names = "clk26m";
+	};
+
+	rtc32k: oscillator at 1 {
+		compatible = "fixed-clock";
+		#clock-cells = <0>;
+		clock-frequency = <32000>;
+		clock-output-names = "rtc32k";
+	};
+
 	timer {
 		compatible = "arm,armv7-timer";
 		interrupt-parent = <&gic>;
@@ -104,6 +120,26 @@
 		reg = <0 0x10005000 0 0x1000>;
 	};
 
+	topckgen: syscon at 10000000 {
+		compatible = "mediatek,mt2701-topckgen", "syscon";
+		reg = <0 0x10000000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
+	infracfg: syscon at 10001000 {
+		compatible = "mediatek,mt2701-infracfg", "syscon";
+		reg = <0 0x10001000 0 0x1000>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+	};
+
+	pericfg: syscon at 10003000 {
+		compatible = "mediatek,mt2701-pericfg", "syscon";
+		reg = <0 0x10003000 0 0x1000>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+	};
+
 	watchdog: watchdog at 10007000 {
 		compatible = "mediatek,mt2701-wdt",
 			     "mediatek,mt6589-wdt";
@@ -128,6 +164,12 @@
 		reg = <0 0x10200100 0 0x1c>;
 	};
 
+	apmixedsys: syscon at 10209000 {
+		compatible = "mediatek,mt2701-apmixedsys", "syscon";
+		reg = <0 0x10209000 0 0x1000>;
+		#clock-cells = <1>;
+	};
+
 	gic: interrupt-controller at 10211000 {
 		compatible = "arm,cortex-a7-gic";
 		interrupt-controller;
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:24 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:24 +0800
Subject: [PATCH v9 05/10] clk: mediatek: Add dt-bindings for MT2701 clocks
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-6-git-send-email-erin.lo@mediatek.com>

From: Shunli Wang <shunli.wang at mediatek.com>

Add MT2701 clock dt-bindings, include topckgen, apmixedsys,
infracfg, pericfg and subsystem clocks.

Signed-off-by: Shunli Wang <shunli.wang at mediatek.com>
Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>
---
 include/dt-bindings/clock/mt2701-clk.h | 486 +++++++++++++++++++++++++++++++++
 1 file changed, 486 insertions(+)
 create mode 100644 include/dt-bindings/clock/mt2701-clk.h

diff --git a/include/dt-bindings/clock/mt2701-clk.h b/include/dt-bindings/clock/mt2701-clk.h
new file mode 100644
index 0000000..2062c67
--- /dev/null
+++ b/include/dt-bindings/clock/mt2701-clk.h
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DT_BINDINGS_CLK_MT2701_H
+#define _DT_BINDINGS_CLK_MT2701_H
+
+/* TOPCKGEN */
+#define CLK_TOP_SYSPLL				1
+#define CLK_TOP_SYSPLL_D2			2
+#define CLK_TOP_SYSPLL_D3			3
+#define CLK_TOP_SYSPLL_D5			4
+#define CLK_TOP_SYSPLL_D7			5
+#define CLK_TOP_SYSPLL1_D2			6
+#define CLK_TOP_SYSPLL1_D4			7
+#define CLK_TOP_SYSPLL1_D8			8
+#define CLK_TOP_SYSPLL1_D16			9
+#define CLK_TOP_SYSPLL2_D2			10
+#define CLK_TOP_SYSPLL2_D4			11
+#define CLK_TOP_SYSPLL2_D8			12
+#define CLK_TOP_SYSPLL3_D2			13
+#define CLK_TOP_SYSPLL3_D4			14
+#define CLK_TOP_SYSPLL4_D2			15
+#define CLK_TOP_SYSPLL4_D4			16
+#define CLK_TOP_UNIVPLL				17
+#define CLK_TOP_UNIVPLL_D2			18
+#define CLK_TOP_UNIVPLL_D3			19
+#define CLK_TOP_UNIVPLL_D5			20
+#define CLK_TOP_UNIVPLL_D7			21
+#define CLK_TOP_UNIVPLL_D26			22
+#define CLK_TOP_UNIVPLL_D52			23
+#define CLK_TOP_UNIVPLL_D108			24
+#define CLK_TOP_USB_PHY48M			25
+#define CLK_TOP_UNIVPLL1_D2			26
+#define CLK_TOP_UNIVPLL1_D4			27
+#define CLK_TOP_UNIVPLL1_D8			28
+#define CLK_TOP_UNIVPLL2_D2			29
+#define CLK_TOP_UNIVPLL2_D4			30
+#define CLK_TOP_UNIVPLL2_D8			31
+#define CLK_TOP_UNIVPLL2_D16			32
+#define CLK_TOP_UNIVPLL2_D32			33
+#define CLK_TOP_UNIVPLL3_D2			34
+#define CLK_TOP_UNIVPLL3_D4			35
+#define CLK_TOP_UNIVPLL3_D8			36
+#define CLK_TOP_MSDCPLL				37
+#define CLK_TOP_MSDCPLL_D2			38
+#define CLK_TOP_MSDCPLL_D4			39
+#define CLK_TOP_MSDCPLL_D8			40
+#define CLK_TOP_MMPLL				41
+#define CLK_TOP_MMPLL_D2			42
+#define CLK_TOP_DMPLL				43
+#define CLK_TOP_DMPLL_D2			44
+#define CLK_TOP_DMPLL_D4			45
+#define CLK_TOP_DMPLL_X2			46
+#define CLK_TOP_TVDPLL				47
+#define CLK_TOP_TVDPLL_D2			48
+#define CLK_TOP_TVDPLL_D4			49
+#define CLK_TOP_TVD2PLL				50
+#define CLK_TOP_TVD2PLL_D2			51
+#define CLK_TOP_HADDS2PLL_98M			52
+#define CLK_TOP_HADDS2PLL_294M			53
+#define CLK_TOP_HADDS2_FB			54
+#define CLK_TOP_MIPIPLL_D2			55
+#define CLK_TOP_MIPIPLL_D4			56
+#define CLK_TOP_HDMIPLL				57
+#define CLK_TOP_HDMIPLL_D2			58
+#define CLK_TOP_HDMIPLL_D3			59
+#define CLK_TOP_HDMI_SCL_RX			60
+#define CLK_TOP_HDMI_0_PIX340M			61
+#define CLK_TOP_HDMI_0_DEEP340M			62
+#define CLK_TOP_HDMI_0_PLL340M			63
+#define CLK_TOP_AUD1PLL_98M			64
+#define CLK_TOP_AUD2PLL_90M			65
+#define CLK_TOP_AUDPLL				66
+#define CLK_TOP_AUDPLL_D4			67
+#define CLK_TOP_AUDPLL_D8			68
+#define CLK_TOP_AUDPLL_D16			69
+#define CLK_TOP_AUDPLL_D24			70
+#define CLK_TOP_ETHPLL_500M			71
+#define CLK_TOP_VDECPLL				72
+#define CLK_TOP_VENCPLL				73
+#define CLK_TOP_MIPIPLL				74
+#define CLK_TOP_ARMPLL_1P3G			75
+
+#define CLK_TOP_MM_SEL				76
+#define CLK_TOP_DDRPHYCFG_SEL			77
+#define CLK_TOP_MEM_SEL				78
+#define CLK_TOP_AXI_SEL				79
+#define CLK_TOP_CAMTG_SEL			80
+#define CLK_TOP_MFG_SEL				81
+#define CLK_TOP_VDEC_SEL			82
+#define CLK_TOP_PWM_SEL				83
+#define CLK_TOP_MSDC30_0_SEL			84
+#define CLK_TOP_USB20_SEL			85
+#define CLK_TOP_SPI0_SEL			86
+#define CLK_TOP_UART_SEL			87
+#define CLK_TOP_AUDINTBUS_SEL			88
+#define CLK_TOP_AUDIO_SEL			89
+#define CLK_TOP_MSDC30_2_SEL			90
+#define CLK_TOP_MSDC30_1_SEL			91
+#define CLK_TOP_DPI1_SEL			92
+#define CLK_TOP_DPI0_SEL			93
+#define CLK_TOP_SCP_SEL				94
+#define CLK_TOP_PMICSPI_SEL			95
+#define CLK_TOP_APLL_SEL			96
+#define CLK_TOP_HDMI_SEL			97
+#define CLK_TOP_TVE_SEL				98
+#define CLK_TOP_EMMC_HCLK_SEL			99
+#define CLK_TOP_NFI2X_SEL			100
+#define CLK_TOP_RTC_SEL				101
+#define CLK_TOP_OSD_SEL				102
+#define CLK_TOP_NR_SEL				103
+#define CLK_TOP_DI_SEL				104
+#define CLK_TOP_FLASH_SEL			105
+#define CLK_TOP_ASM_M_SEL			106
+#define CLK_TOP_ASM_I_SEL			107
+#define CLK_TOP_INTDIR_SEL			108
+#define CLK_TOP_HDMIRX_BIST_SEL			109
+#define CLK_TOP_ETHIF_SEL			110
+#define CLK_TOP_MS_CARD_SEL			111
+#define CLK_TOP_ASM_H_SEL			112
+#define CLK_TOP_SPI1_SEL			113
+#define CLK_TOP_CMSYS_SEL			114
+#define CLK_TOP_MSDC30_3_SEL			115
+#define CLK_TOP_HDMIRX26_24_SEL			116
+#define CLK_TOP_AUD2DVD_SEL			117
+#define CLK_TOP_8BDAC_SEL			118
+#define CLK_TOP_SPI2_SEL			119
+#define CLK_TOP_AUD_MUX1_SEL			120
+#define CLK_TOP_AUD_MUX2_SEL			121
+#define CLK_TOP_AUDPLL_MUX_SEL			122
+#define CLK_TOP_AUD_K1_SRC_SEL			123
+#define CLK_TOP_AUD_K2_SRC_SEL			124
+#define CLK_TOP_AUD_K3_SRC_SEL			125
+#define CLK_TOP_AUD_K4_SRC_SEL			126
+#define CLK_TOP_AUD_K5_SRC_SEL			127
+#define CLK_TOP_AUD_K6_SRC_SEL			128
+#define CLK_TOP_PADMCLK_SEL			129
+#define CLK_TOP_AUD_EXTCK1_DIV			130
+#define CLK_TOP_AUD_EXTCK2_DIV			131
+#define CLK_TOP_AUD_MUX1_DIV			132
+#define CLK_TOP_AUD_MUX2_DIV			133
+#define CLK_TOP_AUD_K1_SRC_DIV			134
+#define CLK_TOP_AUD_K2_SRC_DIV			135
+#define CLK_TOP_AUD_K3_SRC_DIV			136
+#define CLK_TOP_AUD_K4_SRC_DIV			137
+#define CLK_TOP_AUD_K5_SRC_DIV			138
+#define CLK_TOP_AUD_K6_SRC_DIV			139
+#define CLK_TOP_AUD_I2S1_MCLK			140
+#define CLK_TOP_AUD_I2S2_MCLK			141
+#define CLK_TOP_AUD_I2S3_MCLK			142
+#define CLK_TOP_AUD_I2S4_MCLK			143
+#define CLK_TOP_AUD_I2S5_MCLK			144
+#define CLK_TOP_AUD_I2S6_MCLK			145
+#define CLK_TOP_AUD_48K_TIMING			146
+#define CLK_TOP_AUD_44K_TIMING			147
+
+#define CLK_TOP_32K_INTERNAL			148
+#define CLK_TOP_32K_EXTERNAL			149
+#define CLK_TOP_CLK26M_D8			150
+#define CLK_TOP_8BDAC				151
+#define CLK_TOP_WBG_DIG_416M			152
+#define CLK_TOP_DPI				153
+#define CLK_TOP_HDMITX_CLKDIG_CTS		154
+#define CLK_TOP_DSI0_LNTC_DSI			155
+#define CLK_TOP_AUD_EXT1			156
+#define CLK_TOP_AUD_EXT2			157
+#define CLK_TOP_NFI1X_PAD			158
+#define CLK_TOP_NR				159
+
+/* APMIXEDSYS */
+
+#define CLK_APMIXED_ARMPLL			1
+#define CLK_APMIXED_MAINPLL			2
+#define CLK_APMIXED_UNIVPLL			3
+#define CLK_APMIXED_MMPLL			4
+#define CLK_APMIXED_MSDCPLL			5
+#define CLK_APMIXED_TVDPLL			6
+#define CLK_APMIXED_AUD1PLL			7
+#define CLK_APMIXED_TRGPLL			8
+#define CLK_APMIXED_ETHPLL			9
+#define CLK_APMIXED_VDECPLL			10
+#define CLK_APMIXED_HADDS2PLL			11
+#define CLK_APMIXED_AUD2PLL			12
+#define CLK_APMIXED_TVD2PLL			13
+#define CLK_APMIXED_NR				14
+
+/* DDRPHY */
+
+#define CLK_DDRPHY_VENCPLL			1
+#define CLK_DDRPHY_NR				2
+
+/* INFRACFG */
+
+#define CLK_INFRA_DBG				1
+#define CLK_INFRA_SMI				2
+#define CLK_INFRA_QAXI_CM4			3
+#define CLK_INFRA_AUD_SPLIN_B			4
+#define CLK_INFRA_AUDIO				5
+#define CLK_INFRA_EFUSE				6
+#define CLK_INFRA_L2C_SRAM			7
+#define CLK_INFRA_M4U				8
+#define CLK_INFRA_CONNMCU			9
+#define CLK_INFRA_TRNG				10
+#define CLK_INFRA_RAMBUFIF			11
+#define CLK_INFRA_CPUM				12
+#define CLK_INFRA_KP				13
+#define CLK_INFRA_CEC				14
+#define CLK_INFRA_IRRX				15
+#define CLK_INFRA_PMICSPI			16
+#define CLK_INFRA_PMICWRAP			17
+#define CLK_INFRA_DDCCI				18
+#define CLK_INFRA_CLK_13M			19
+#define CLK_INFRA_NR				20
+
+/* PERICFG */
+
+#define CLK_PERI_NFI				1
+#define CLK_PERI_THERM				2
+#define CLK_PERI_PWM1				3
+#define CLK_PERI_PWM2				4
+#define CLK_PERI_PWM3				5
+#define CLK_PERI_PWM4				6
+#define CLK_PERI_PWM5				7
+#define CLK_PERI_PWM6				8
+#define CLK_PERI_PWM7				9
+#define CLK_PERI_PWM				10
+#define CLK_PERI_USB0				11
+#define CLK_PERI_USB1				12
+#define CLK_PERI_AP_DMA				13
+#define CLK_PERI_MSDC30_0			14
+#define CLK_PERI_MSDC30_1			15
+#define CLK_PERI_MSDC30_2			16
+#define CLK_PERI_MSDC30_3			17
+#define CLK_PERI_MSDC50_3			18
+#define CLK_PERI_NLI				19
+#define CLK_PERI_UART0				20
+#define CLK_PERI_UART1				21
+#define CLK_PERI_UART2				22
+#define CLK_PERI_UART3				23
+#define CLK_PERI_BTIF				24
+#define CLK_PERI_I2C0				25
+#define CLK_PERI_I2C1				26
+#define CLK_PERI_I2C2				27
+#define CLK_PERI_I2C3				28
+#define CLK_PERI_AUXADC				29
+#define CLK_PERI_SPI0				30
+#define CLK_PERI_ETH				31
+#define CLK_PERI_USB0_MCU			32
+
+#define CLK_PERI_USB1_MCU			33
+#define CLK_PERI_USB_SLV			34
+#define CLK_PERI_GCPU				35
+#define CLK_PERI_NFI_ECC			36
+#define CLK_PERI_NFI_PAD			37
+#define CLK_PERI_FLASH				38
+#define CLK_PERI_HOST89_INT			39
+#define CLK_PERI_HOST89_SPI			40
+#define CLK_PERI_HOST89_DVD			41
+#define CLK_PERI_SPI1				42
+#define CLK_PERI_SPI2				43
+#define CLK_PERI_FCI				44
+
+#define CLK_PERI_UART0_SEL			45
+#define CLK_PERI_UART1_SEL			46
+#define CLK_PERI_UART2_SEL			47
+#define CLK_PERI_UART3_SEL			48
+#define CLK_PERI_NR				49
+
+/* AUDIO */
+
+#define CLK_AUD_AFE				1
+#define CLK_AUD_LRCK_DETECT			2
+#define CLK_AUD_I2S				3
+#define CLK_AUD_APLL_TUNER			4
+#define CLK_AUD_HDMI				5
+#define CLK_AUD_SPDF				6
+#define CLK_AUD_SPDF2				7
+#define CLK_AUD_APLL				8
+#define CLK_AUD_TML				9
+#define CLK_AUD_AHB_IDLE_EXT			10
+#define CLK_AUD_AHB_IDLE_INT			11
+
+#define CLK_AUD_I2SIN1				12
+#define CLK_AUD_I2SIN2				13
+#define CLK_AUD_I2SIN3				14
+#define CLK_AUD_I2SIN4				15
+#define CLK_AUD_I2SIN5				16
+#define CLK_AUD_I2SIN6				17
+#define CLK_AUD_I2SO1				18
+#define CLK_AUD_I2SO2				19
+#define CLK_AUD_I2SO3				20
+#define CLK_AUD_I2SO4				21
+#define CLK_AUD_I2SO5				22
+#define CLK_AUD_I2SO6				23
+#define CLK_AUD_ASRCI1				24
+#define CLK_AUD_ASRCI2				25
+#define CLK_AUD_ASRCO1				26
+#define CLK_AUD_ASRCO2				27
+#define CLK_AUD_ASRC11				28
+#define CLK_AUD_ASRC12				29
+#define CLK_AUD_HDMIRX				30
+#define CLK_AUD_INTDIR				31
+#define CLK_AUD_A1SYS				32
+#define CLK_AUD_A2SYS				33
+#define CLK_AUD_AFE_CONN			34
+#define CLK_AUD_AFE_PCMIF			35
+#define CLK_AUD_AFE_MRGIF			36
+
+#define CLK_AUD_MMIF_UL1			37
+#define CLK_AUD_MMIF_UL2			38
+#define CLK_AUD_MMIF_UL3			39
+#define CLK_AUD_MMIF_UL4			40
+#define CLK_AUD_MMIF_UL5			41
+#define CLK_AUD_MMIF_UL6			42
+#define CLK_AUD_MMIF_DL1			43
+#define CLK_AUD_MMIF_DL2			44
+#define CLK_AUD_MMIF_DL3			45
+#define CLK_AUD_MMIF_DL4			46
+#define CLK_AUD_MMIF_DL5			47
+#define CLK_AUD_MMIF_DL6			48
+#define CLK_AUD_MMIF_DLMCH			49
+#define CLK_AUD_MMIF_ARB1			50
+#define CLK_AUD_MMIF_AWB1			51
+#define CLK_AUD_MMIF_AWB2			52
+#define CLK_AUD_MMIF_DAI			53
+
+#define CLK_AUD_DMIC1				54
+#define CLK_AUD_DMIC2				55
+#define CLK_AUD_ASRCI3				56
+#define CLK_AUD_ASRCI4				57
+#define CLK_AUD_ASRCI5				58
+#define CLK_AUD_ASRCI6				59
+#define CLK_AUD_ASRCO3				60
+#define CLK_AUD_ASRCO4				61
+#define CLK_AUD_ASRCO5				62
+#define CLK_AUD_ASRCO6				63
+#define CLK_AUD_MEM_ASRC1			64
+#define CLK_AUD_MEM_ASRC2			65
+#define CLK_AUD_MEM_ASRC3			66
+#define CLK_AUD_MEM_ASRC4			67
+#define CLK_AUD_MEM_ASRC5			68
+#define CLK_AUD_DSD_ENC				69
+#define CLK_AUD_ASRC_BRG			70
+#define CLK_AUD_NR				71
+
+/* MMSYS */
+
+#define CLK_MM_SMI_COMMON			1
+#define CLK_MM_SMI_LARB0			2
+#define CLK_MM_CMDQ				3
+#define CLK_MM_MUTEX				4
+#define CLK_MM_DISP_COLOR			5
+#define CLK_MM_DISP_BLS				6
+#define CLK_MM_DISP_WDMA			7
+#define CLK_MM_DISP_RDMA			8
+#define CLK_MM_DISP_OVL				9
+#define CLK_MM_MDP_TDSHP			10
+#define CLK_MM_MDP_WROT				11
+#define CLK_MM_MDP_WDMA				12
+#define CLK_MM_MDP_RSZ1				13
+#define CLK_MM_MDP_RSZ0				14
+#define CLK_MM_MDP_RDMA				15
+#define CLK_MM_MDP_BLS_26M			16
+#define CLK_MM_CAM_MDP				17
+#define CLK_MM_FAKE_ENG				18
+#define CLK_MM_MUTEX_32K			19
+#define CLK_MM_DISP_RDMA1			20
+#define CLK_MM_DISP_UFOE			21
+
+#define CLK_MM_DSI_ENGINE			22
+#define CLK_MM_DSI_DIG				23
+#define CLK_MM_DPI_DIGL				24
+#define CLK_MM_DPI_ENGINE			25
+#define CLK_MM_DPI1_DIGL			26
+#define CLK_MM_DPI1_ENGINE			27
+#define CLK_MM_TVE_OUTPUT			28
+#define CLK_MM_TVE_INPUT			29
+#define CLK_MM_HDMI_PIXEL			30
+#define CLK_MM_HDMI_PLL				31
+#define CLK_MM_HDMI_AUDIO			32
+#define CLK_MM_HDMI_SPDIF			33
+#define CLK_MM_TVE_FMM				34
+#define CLK_MM_NR				35
+
+/* IMGSYS */
+
+#define CLK_IMG_SMI_COMM			1
+#define CLK_IMG_RESZ				2
+#define CLK_IMG_JPGDEC_SMI			3
+#define CLK_IMG_JPGDEC				4
+#define CLK_IMG_VENC_LT				5
+#define CLK_IMG_VENC				6
+#define CLK_IMG_NR				7
+
+/* VDEC */
+
+#define CLK_VDEC_CKGEN				1
+#define CLK_VDEC_LARB				2
+#define CLK_VDEC_NR				3
+
+/* HIFSYS */
+
+#define CLK_HIFSYS_USB0PHY			1
+#define CLK_HIFSYS_USB1PHY			2
+#define CLK_HIFSYS_PCIE0			3
+#define CLK_HIFSYS_PCIE1			4
+#define CLK_HIFSYS_PCIE2			5
+#define CLK_HIFSYS_NR				6
+
+/* ETHSYS */
+#define CLK_ETHSYS_HSDMA			1
+#define CLK_ETHSYS_ESW				2
+#define CLK_ETHSYS_GP2				3
+#define CLK_ETHSYS_GP1				4
+#define CLK_ETHSYS_PCM				5
+#define CLK_ETHSYS_GDMA				6
+#define CLK_ETHSYS_I2S				7
+#define CLK_ETHSYS_CRYPTO			8
+#define CLK_ETHSYS_NR				9
+
+/* BDP */
+
+#define CLK_BDP_BRG_BA				1
+#define CLK_BDP_BRG_DRAM			2
+#define CLK_BDP_LARB_DRAM			3
+#define CLK_BDP_WR_VDI_PXL			4
+#define CLK_BDP_WR_VDI_DRAM			5
+#define CLK_BDP_WR_B				6
+#define CLK_BDP_DGI_IN				7
+#define CLK_BDP_DGI_OUT				8
+#define CLK_BDP_FMT_MAST_27			9
+#define CLK_BDP_FMT_B				10
+#define CLK_BDP_OSD_B				11
+#define CLK_BDP_OSD_DRAM			12
+#define CLK_BDP_OSD_AGENT			13
+#define CLK_BDP_OSD_PXL				14
+#define CLK_BDP_RLE_B				15
+#define CLK_BDP_RLE_AGENT			16
+#define CLK_BDP_RLE_DRAM			17
+#define CLK_BDP_F27M				18
+#define CLK_BDP_F27M_VDOUT			19
+#define CLK_BDP_F27_74_74			20
+#define CLK_BDP_F2FS				21
+#define CLK_BDP_F2FS74_148			22
+#define CLK_BDP_FB				23
+#define CLK_BDP_VDO_DRAM			24
+#define CLK_BDP_VDO_2FS				25
+#define CLK_BDP_VDO_B				26
+#define CLK_BDP_WR_DI_PXL			27
+#define CLK_BDP_WR_DI_DRAM			28
+#define CLK_BDP_WR_DI_B				29
+#define CLK_BDP_NR_PXL				30
+#define CLK_BDP_NR_DRAM				31
+#define CLK_BDP_NR_B				32
+
+#define CLK_BDP_RX_F				33
+#define CLK_BDP_RX_X				34
+#define CLK_BDP_RXPDT				35
+#define CLK_BDP_RX_CSCL_N			36
+#define CLK_BDP_RX_CSCL				37
+#define CLK_BDP_RX_DDCSCL_N			38
+#define CLK_BDP_RX_DDCSCL			39
+#define CLK_BDP_RX_VCO				40
+#define CLK_BDP_RX_DP				41
+#define CLK_BDP_RX_P				42
+#define CLK_BDP_RX_M				43
+#define CLK_BDP_RX_PLL				44
+#define CLK_BDP_BRG_RT_B			45
+#define CLK_BDP_BRG_RT_DRAM			46
+#define CLK_BDP_LARBRT_DRAM			47
+#define CLK_BDP_TMDS_SYN			48
+#define CLK_BDP_HDMI_MON			49
+#define CLK_BDP_NR				50
+
+#endif /* _DT_BINDINGS_CLK_MT2701_H */
-- 
1.9.1



From erin.lo at mediatek.com  Wed Jun 22 00:40:25 2016
From: erin.lo at mediatek.com (Erin Lo)
Date: Wed, 22 Jun 2016 15:40:25 +0800
Subject: [PATCH v9 06/10] clk: mediatek: Add MT2701 clock support
In-Reply-To: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
References: <1466581229-2342-1-git-send-email-erin.lo@mediatek.com>
Message-ID: <1466581229-2342-7-git-send-email-erin.lo@mediatek.com>

From: Shunli Wang <shunli.wang at mediatek.com>

Add MT2701 clock support, include topckgen, apmixedsys,
infracfg, pericfg and subsystem clocks.

Signed-off-by: Shunli Wang <shunli.wang at mediatek.com>
Signed-off-by: James Liao <jamesjj.liao at mediatek.com>
Signed-off-by: Erin Lo <erin.lo at mediatek.com>
Tested-by: John Crispin <blogic at openwrt.org>
---
 drivers/clk/mediatek/Kconfig           |   50 ++
 drivers/clk/mediatek/Makefile          |    7 +
 drivers/clk/mediatek/clk-gate.c        |   52 ++
 drivers/clk/mediatek/clk-gate.h        |    2 +
 drivers/clk/mediatek/clk-mt2701-bdp.c  |  140 +++++
 drivers/clk/mediatek/clk-mt2701-eth.c  |   82 +++
 drivers/clk/mediatek/clk-mt2701-hif.c  |   79 +++
 drivers/clk/mediatek/clk-mt2701-img.c  |   82 +++
 drivers/clk/mediatek/clk-mt2701-mm.c   |  125 ++++
 drivers/clk/mediatek/clk-mt2701-vdec.c |   93 +++
 drivers/clk/mediatek/clk-mt2701.c      | 1033 ++++++++++++++++++++++++++++++++
 drivers/clk/mediatek/clk-mtk.c         |   40 ++
 drivers/clk/mediatek/clk-mtk.h         |   41 +-
 drivers/clk/mediatek/clk-pll.c         |    1 +
 14 files changed, 1822 insertions(+), 5 deletions(-)
 create mode 100644 drivers/clk/mediatek/clk-mt2701-bdp.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-eth.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-hif.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-img.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-mm.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701-vdec.c
 create mode 100644 drivers/clk/mediatek/clk-mt2701.c

diff --git a/drivers/clk/mediatek/Kconfig b/drivers/clk/mediatek/Kconfig
index dc224e6..5aa6204 100644
--- a/drivers/clk/mediatek/Kconfig
+++ b/drivers/clk/mediatek/Kconfig
@@ -6,6 +6,56 @@ config COMMON_CLK_MEDIATEK
 	---help---
 	  Mediatek SoCs' clock support.
 
+config COMMON_CLK_MT2701
+	bool "Clock driver for Mediatek MT2701"
+	depends on COMMON_CLK
+	select COMMON_CLK_MEDIATEK
+	default ARCH_MEDIATEK
+	---help---
+	  This driver supports Mediatek MT2701 basic clocks.
+
+config COMMON_CLK_MT2701_MMSYS
+	bool "Clock driver for Mediatek MT2701 mmsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 mmsys clocks.
+
+config COMMON_CLK_MT2701_IMGSYS
+	bool "Clock driver for Mediatek MT2701 imgsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 imgsys clocks.
+
+config COMMON_CLK_MT2701_VDECSYS
+	bool "Clock driver for Mediatek MT2701 vdecsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 vdecsys clocks.
+
+config COMMON_CLK_MT2701_HIFSYS
+	bool "Clock driver for Mediatek MT2701 hifsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 hifsys clocks.
+
+config COMMON_CLK_MT2701_ETHSYS
+	bool "Clock driver for Mediatek MT2701 ethsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 ethsys clocks.
+
+config COMMON_CLK_MT2701_BDPSYS
+	bool "Clock driver for Mediatek MT2701 bdpsys"
+	depends on COMMON_CLK
+	select COMMON_CLK_MT2701
+	---help---
+	  This driver supports Mediatek MT2701 bdpsys clocks.
+
 config COMMON_CLK_MT8135
 	bool "Clock driver for Mediatek MT8135"
 	depends on COMMON_CLK
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 32e7222..19ae7ef 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -1,4 +1,11 @@
 obj-$(CONFIG_COMMON_CLK_MEDIATEK) += clk-mtk.o clk-pll.o clk-gate.o clk-apmixed.o
 obj-$(CONFIG_RESET_CONTROLLER) += reset.o
+obj-$(CONFIG_COMMON_CLK_MT2701) += clk-mt2701.o
+obj-$(CONFIG_COMMON_CLK_MT2701_BDPSYS) += clk-mt2701-bdp.o
+obj-$(CONFIG_COMMON_CLK_MT2701_ETHSYS) += clk-mt2701-eth.o
+obj-$(CONFIG_COMMON_CLK_MT2701_HIFSYS) += clk-mt2701-hif.o
+obj-$(CONFIG_COMMON_CLK_MT2701_IMGSYS) += clk-mt2701-img.o
+obj-$(CONFIG_COMMON_CLK_MT2701_MMSYS) += clk-mt2701-mm.o
+obj-$(CONFIG_COMMON_CLK_MT2701_VDECSYS) += clk-mt2701-vdec.o
 obj-$(CONFIG_COMMON_CLK_MT8135) += clk-mt8135.o
 obj-$(CONFIG_COMMON_CLK_MT8173) += clk-mt8173.o
diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c
index d8787bf..934bf0e 100644
--- a/drivers/clk/mediatek/clk-gate.c
+++ b/drivers/clk/mediatek/clk-gate.c
@@ -61,6 +61,22 @@ static void mtk_cg_clr_bit(struct clk_hw *hw)
 	regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
 }
 
+static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
+{
+	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
+	u32 cgbit = BIT(cg->bit);
+
+	regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, cgbit);
+}
+
+static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
+{
+	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
+	u32 cgbit = BIT(cg->bit);
+
+	regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, 0);
+}
+
 static int mtk_cg_enable(struct clk_hw *hw)
 {
 	mtk_cg_clr_bit(hw);
@@ -85,6 +101,30 @@ static void mtk_cg_disable_inv(struct clk_hw *hw)
 	mtk_cg_clr_bit(hw);
 }
 
+static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
+{
+	mtk_cg_clr_bit_no_setclr(hw);
+
+	return 0;
+}
+
+static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
+{
+	mtk_cg_set_bit_no_setclr(hw);
+}
+
+static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
+{
+	mtk_cg_set_bit_no_setclr(hw);
+
+	return 0;
+}
+
+static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
+{
+	mtk_cg_clr_bit_no_setclr(hw);
+}
+
 const struct clk_ops mtk_clk_gate_ops_setclr = {
 	.is_enabled	= mtk_cg_bit_is_cleared,
 	.enable		= mtk_cg_enable,
@@ -97,6 +137,18 @@ const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
 	.disable	= mtk_cg_disable_inv,
 };
 
+const struct clk_ops mtk_clk_gate_ops_no_setclr = {
+	.is_enabled	= mtk_cg_bit_is_cleared,
+	.enable		= mtk_cg_enable_no_setclr,
+	.disable	= mtk_cg_disable_no_setclr,
+};
+
+const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
+	.is_enabled	= mtk_cg_bit_is_set,
+	.enable		= mtk_cg_enable_inv_no_setclr,
+	.disable	= mtk_cg_disable_inv_no_setclr,
+};
+
 struct clk *mtk_clk_register_gate(
 		const char *name,
 		const char *parent_name,
diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h
index b182160..72ef89b 100644
--- a/drivers/clk/mediatek/clk-gate.h
+++ b/drivers/clk/mediatek/clk-gate.h
@@ -36,6 +36,8 @@ static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
 
 extern const struct clk_ops mtk_clk_gate_ops_setclr;
 extern const struct clk_ops mtk_clk_gate_ops_setclr_inv;
+extern const struct clk_ops mtk_clk_gate_ops_no_setclr;
+extern const struct clk_ops mtk_clk_gate_ops_no_setclr_inv;
 
 struct clk *mtk_clk_register_gate(
 		const char *name,
diff --git a/drivers/clk/mediatek/clk-mt2701-bdp.c b/drivers/clk/mediatek/clk-mt2701-bdp.c
new file mode 100644
index 0000000..81f628c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-bdp.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs bdp0_cg_regs = {
+	.set_ofs = 0x0104,
+	.clr_ofs = 0x0108,
+	.sta_ofs = 0x0100,
+};
+
+static const struct mtk_gate_regs bdp1_cg_regs = {
+	.set_ofs = 0x0114,
+	.clr_ofs = 0x0118,
+	.sta_ofs = 0x0110,
+};
+
+#define GATE_BDP0(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &bdp0_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr_inv,	\
+	}
+
+#define GATE_BDP1(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &bdp1_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr_inv,	\
+	}
+
+static const struct mtk_gate bdp_clks[] = {
+	GATE_BDP0(CLK_BDP_BRG_BA, "brg_baclk", "mm_sel", 0),
+	GATE_BDP0(CLK_BDP_BRG_DRAM, "brg_dram", "mm_sel", 1),
+	GATE_BDP0(CLK_BDP_LARB_DRAM, "larb_dram", "mm_sel", 2),
+	GATE_BDP0(CLK_BDP_WR_VDI_PXL, "wr_vdi_pxl", "hdmi_0_deep340m", 3),
+	GATE_BDP0(CLK_BDP_WR_VDI_DRAM, "wr_vdi_dram", "mm_sel", 4),
+	GATE_BDP0(CLK_BDP_WR_B, "wr_bclk", "mm_sel", 5),
+	GATE_BDP0(CLK_BDP_DGI_IN, "dgi_in", "dpi1_sel", 6),
+	GATE_BDP0(CLK_BDP_DGI_OUT, "dgi_out", "dpi1_sel", 7),
+	GATE_BDP0(CLK_BDP_FMT_MAST_27, "fmt_mast_27", "dpi1_sel", 8),
+	GATE_BDP0(CLK_BDP_FMT_B, "fmt_bclk", "mm_sel", 9),
+	GATE_BDP0(CLK_BDP_OSD_B, "osd_bclk", "mm_sel", 10),
+	GATE_BDP0(CLK_BDP_OSD_DRAM, "osd_dram", "mm_sel", 11),
+	GATE_BDP0(CLK_BDP_OSD_AGENT, "osd_agent", "osd_sel", 12),
+	GATE_BDP0(CLK_BDP_OSD_PXL, "osd_pxl", "dpi1_sel", 13),
+	GATE_BDP0(CLK_BDP_RLE_B, "rle_bclk", "mm_sel", 14),
+	GATE_BDP0(CLK_BDP_RLE_AGENT, "rle_agent", "mm_sel", 15),
+	GATE_BDP0(CLK_BDP_RLE_DRAM, "rle_dram", "mm_sel", 16),
+	GATE_BDP0(CLK_BDP_F27M, "f27m", "di_sel", 17),
+	GATE_BDP0(CLK_BDP_F27M_VDOUT, "f27m_vdout", "di_sel", 18),
+	GATE_BDP0(CLK_BDP_F27_74_74, "f27_74_74", "di_sel", 19),
+	GATE_BDP0(CLK_BDP_F2FS, "f2fs", "di_sel", 20),
+	GATE_BDP0(CLK_BDP_F2FS74_148, "f2fs74_148", "di_sel", 21),
+	GATE_BDP0(CLK_BDP_FB, "fbclk", "mm_sel", 22),
+	GATE_BDP0(CLK_BDP_VDO_DRAM, "vdo_dram", "mm_sel", 23),
+	GATE_BDP0(CLK_BDP_VDO_2FS, "vdo_2fs", "di_sel", 24),
+	GATE_BDP0(CLK_BDP_VDO_B, "vdo_bclk", "mm_sel", 25),
+	GATE_BDP0(CLK_BDP_WR_DI_PXL, "wr_di_pxl", "di_sel", 26),
+	GATE_BDP0(CLK_BDP_WR_DI_DRAM, "wr_di_dram", "mm_sel", 27),
+	GATE_BDP0(CLK_BDP_WR_DI_B, "wr_di_bclk", "mm_sel", 28),
+	GATE_BDP0(CLK_BDP_NR_PXL, "nr_pxl", "nr_sel", 29),
+	GATE_BDP0(CLK_BDP_NR_DRAM, "nr_dram", "mm_sel", 30),
+	GATE_BDP0(CLK_BDP_NR_B, "nr_bclk", "mm_sel", 31),
+	GATE_BDP1(CLK_BDP_RX_F, "rx_fclk", "hadds2_fbclk", 0),
+	GATE_BDP1(CLK_BDP_RX_X, "rx_xclk", "clk26m", 1),
+	GATE_BDP1(CLK_BDP_RXPDT, "rxpdtclk", "hdmi_0_pix340m", 2),
+	GATE_BDP1(CLK_BDP_RX_CSCL_N, "rx_cscl_n", "clk26m", 3),
+	GATE_BDP1(CLK_BDP_RX_CSCL, "rx_cscl", "clk26m", 4),
+	GATE_BDP1(CLK_BDP_RX_DDCSCL_N, "rx_ddcscl_n", "hdmi_scl_rx", 5),
+	GATE_BDP1(CLK_BDP_RX_DDCSCL, "rx_ddcscl", "hdmi_scl_rx", 6),
+	GATE_BDP1(CLK_BDP_RX_VCO, "rx_vcoclk", "hadds2pll_294m", 7),
+	GATE_BDP1(CLK_BDP_RX_DP, "rx_dpclk", "hdmi_0_pll340m", 8),
+	GATE_BDP1(CLK_BDP_RX_P, "rx_pclk", "hdmi_0_pll340m", 9),
+	GATE_BDP1(CLK_BDP_RX_M, "rx_mclk", "hadds2pll_294m", 10),
+	GATE_BDP1(CLK_BDP_RX_PLL, "rx_pllclk", "hdmi_0_pix340m", 11),
+	GATE_BDP1(CLK_BDP_BRG_RT_B, "brg_rt_bclk", "mm_sel", 12),
+	GATE_BDP1(CLK_BDP_BRG_RT_DRAM, "brg_rt_dram", "mm_sel", 13),
+	GATE_BDP1(CLK_BDP_LARBRT_DRAM, "larbrt_dram", "mm_sel", 14),
+	GATE_BDP1(CLK_BDP_TMDS_SYN, "tmds_syn", "hdmi_0_pll340m", 15),
+	GATE_BDP1(CLK_BDP_HDMI_MON, "hdmi_mon", "hdmi_0_pll340m", 16),
+};
+
+static void mtk_bdpsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_BDP_NR);
+
+	mtk_clk_register_gates(node, bdp_clks, ARRAY_SIZE(bdp_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_bdp[] = {
+	{ .compatible = "mediatek,mt2701-bdpsys", },
+	{}
+};
+
+static int clk_mt2701_bdp_probe(struct platform_device *pdev)
+{
+	mtk_bdpsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_bdp_drv = {
+	.probe = clk_mt2701_bdp_probe,
+	.driver = {
+		.name = "clk-mt2701-bdp",
+		.of_match_table = of_match_clk_mt2701_bdp,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_bdp_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701-eth.c b/drivers/clk/mediatek/clk-mt2701-eth.c
new file mode 100644
index 0000000..22163eb
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-eth.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs eth_cg_regs = {
+	.sta_ofs = 0x0030,
+};
+
+#define GATE_ETH(_id, _name, _parent, _shift) {		\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &eth_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_no_setclr_inv,	\
+	}
+
+static const struct mtk_gate eth_clks[] = {
+	GATE_ETH(CLK_ETHSYS_HSDMA, "hsdma_clk", "ethif_sel", 5),
+	GATE_ETH(CLK_ETHSYS_ESW, "esw_clk", "ethpll_500m_ck", 6),
+	GATE_ETH(CLK_ETHSYS_GP2, "gp2_clk", "trgpll", 7),
+	GATE_ETH(CLK_ETHSYS_GP1, "gp1_clk", "ethpll_500m_ck", 8),
+	GATE_ETH(CLK_ETHSYS_PCM, "pcm_clk", "ethif_sel", 11),
+	GATE_ETH(CLK_ETHSYS_GDMA, "gdma_clk", "ethif_sel", 14),
+	GATE_ETH(CLK_ETHSYS_I2S, "i2s_clk", "ethif_sel", 17),
+	GATE_ETH(CLK_ETHSYS_CRYPTO, "crypto_clk", "ethif_sel", 29),
+};
+
+static void mtk_ethsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_ETHSYS_NR);
+
+	mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_eth[] = {
+	{ .compatible = "mediatek,mt2701-ethsys", },
+	{}
+};
+
+static int clk_mt2701_eth_probe(struct platform_device *pdev)
+{
+	mtk_ethsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_eth_drv = {
+	.probe = clk_mt2701_eth_probe,
+	.driver = {
+		.name = "clk-mt2701-eth",
+		.of_match_table = of_match_clk_mt2701_eth,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_eth_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701-hif.c b/drivers/clk/mediatek/clk-mt2701-hif.c
new file mode 100644
index 0000000..3f6cea2
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-hif.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs hif_cg_regs = {
+	.sta_ofs = 0x0008,
+};
+
+#define GATE_HIF(_id, _name, _parent, _shift) {		\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &hif_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_no_setclr_inv,	\
+	}
+
+static const struct mtk_gate hif_clks[] = {
+	GATE_HIF(CLK_HIFSYS_USB0PHY, "usb0_phy_clk", "ethpll_500m_ck", 21),
+	GATE_HIF(CLK_HIFSYS_USB1PHY, "usb1_phy_clk", "ethpll_500m_ck", 22),
+	GATE_HIF(CLK_HIFSYS_PCIE0, "pcie0_clk", "ethpll_500m_ck", 24),
+	GATE_HIF(CLK_HIFSYS_PCIE1, "pcie1_clk", "ethpll_500m_ck", 25),
+	GATE_HIF(CLK_HIFSYS_PCIE2, "pcie2_clk", "ethpll_500m_ck", 26),
+};
+
+static void mtk_hifsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_HIFSYS_NR);
+
+	mtk_clk_register_gates(node, hif_clks, ARRAY_SIZE(hif_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_hif[] = {
+	{ .compatible = "mediatek,mt2701-hifsys", },
+	{}
+};
+
+static int clk_mt2701_hif_probe(struct platform_device *pdev)
+{
+	mtk_hifsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_hif_drv = {
+	.probe = clk_mt2701_hif_probe,
+	.driver = {
+		.name = "clk-mt2701-hif",
+		.of_match_table = of_match_clk_mt2701_hif,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_hif_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701-img.c b/drivers/clk/mediatek/clk-mt2701-img.c
new file mode 100644
index 0000000..e324079
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-img.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs img_cg_regs = {
+	.set_ofs = 0x0004,
+	.clr_ofs = 0x0008,
+	.sta_ofs = 0x0000,
+};
+
+#define GATE_IMG(_id, _name, _parent, _shift) {		\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &img_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+static const struct mtk_gate img_clks[] = {
+	GATE_IMG(CLK_IMG_SMI_COMM, "img_smi_comm", "mm_sel", 0),
+	GATE_IMG(CLK_IMG_RESZ, "img_resz", "mm_sel", 1),
+	GATE_IMG(CLK_IMG_JPGDEC_SMI, "img_jpgdec_smi", "mm_sel", 5),
+	GATE_IMG(CLK_IMG_JPGDEC, "img_jpgdec", "mm_sel", 6),
+	GATE_IMG(CLK_IMG_VENC_LT, "img_venc_lt", "mm_sel", 8),
+	GATE_IMG(CLK_IMG_VENC, "img_venc", "mm_sel", 9),
+};
+
+static void mtk_imgsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_IMG_NR);
+
+	mtk_clk_register_gates(node, img_clks, ARRAY_SIZE(img_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_img[] = {
+	{ .compatible = "mediatek,mt2701-imgsys", },
+	{}
+};
+
+static int clk_mt2701_img_probe(struct platform_device *pdev)
+{
+	mtk_imgsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_img_drv = {
+	.probe = clk_mt2701_img_probe,
+	.driver = {
+		.name = "clk-mt2701-img",
+		.of_match_table = of_match_clk_mt2701_img,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_img_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701-mm.c b/drivers/clk/mediatek/clk-mt2701-mm.c
new file mode 100644
index 0000000..b1711f8
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-mm.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs disp0_cg_regs = {
+	.set_ofs = 0x0104,
+	.clr_ofs = 0x0108,
+	.sta_ofs = 0x0100,
+};
+
+static const struct mtk_gate_regs disp1_cg_regs = {
+	.set_ofs = 0x0114,
+	.clr_ofs = 0x0118,
+	.sta_ofs = 0x0110,
+};
+
+#define GATE_DISP0(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &disp0_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+#define GATE_DISP1(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &disp1_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+static const struct mtk_gate mm_clks[] = {
+	GATE_DISP0(CLK_MM_SMI_COMMON, "mm_smi_comm", "mm_sel", 0),
+	GATE_DISP0(CLK_MM_SMI_LARB0, "mm_smi_larb0", "mm_sel", 1),
+	GATE_DISP0(CLK_MM_CMDQ, "mm_cmdq", "mm_sel", 2),
+	GATE_DISP0(CLK_MM_MUTEX, "mm_mutex", "mm_sel", 3),
+	GATE_DISP0(CLK_MM_DISP_COLOR, "mm_disp_color", "mm_sel", 4),
+	GATE_DISP0(CLK_MM_DISP_BLS, "mm_disp_bls", "mm_sel", 5),
+	GATE_DISP0(CLK_MM_DISP_WDMA, "mm_disp_wdma", "mm_sel", 6),
+	GATE_DISP0(CLK_MM_DISP_RDMA, "mm_disp_rdma", "mm_sel", 7),
+	GATE_DISP0(CLK_MM_DISP_OVL, "mm_disp_ovl", "mm_sel", 8),
+	GATE_DISP0(CLK_MM_MDP_TDSHP, "mm_mdp_tdshp", "mm_sel", 9),
+	GATE_DISP0(CLK_MM_MDP_WROT, "mm_mdp_wrot", "mm_sel", 10),
+	GATE_DISP0(CLK_MM_MDP_WDMA, "mm_mdp_wdma", "mm_sel", 11),
+	GATE_DISP0(CLK_MM_MDP_RSZ1, "mm_mdp_rsz1", "mm_sel", 12),
+	GATE_DISP0(CLK_MM_MDP_RSZ0, "mm_mdp_rsz0", "mm_sel", 13),
+	GATE_DISP0(CLK_MM_MDP_RDMA, "mm_mdp_rdma", "mm_sel", 14),
+	GATE_DISP0(CLK_MM_MDP_BLS_26M, "mm_mdp_bls_26m", "pwm_sel", 15),
+	GATE_DISP0(CLK_MM_CAM_MDP, "mm_cam_mdp", "mm_sel", 16),
+	GATE_DISP0(CLK_MM_FAKE_ENG, "mm_fake_eng", "mm_sel", 17),
+	GATE_DISP0(CLK_MM_MUTEX_32K, "mm_mutex_32k", "rtc_sel", 18),
+	GATE_DISP0(CLK_MM_DISP_RDMA1, "mm_disp_rdma1", "mm_sel", 19),
+	GATE_DISP0(CLK_MM_DISP_UFOE, "mm_disp_ufoe", "mm_sel", 20),
+	GATE_DISP1(CLK_MM_DSI_ENGINE, "mm_dsi_eng", "mm_sel", 0),
+	GATE_DISP1(CLK_MM_DSI_DIG, "mm_dsi_dig", "dsi0_lntc_dsi", 1),
+	GATE_DISP1(CLK_MM_DPI_DIGL, "mm_dpi_digl", "dpi0_sel", 2),
+	GATE_DISP1(CLK_MM_DPI_ENGINE, "mm_dpi_eng", "mm_sel", 3),
+	GATE_DISP1(CLK_MM_DPI1_DIGL, "mm_dpi1_digl", "dpi1_sel", 4),
+	GATE_DISP1(CLK_MM_DPI1_ENGINE, "mm_dpi1_eng", "mm_sel", 5),
+	GATE_DISP1(CLK_MM_TVE_OUTPUT, "mm_tve_output", "tve_sel", 6),
+	GATE_DISP1(CLK_MM_TVE_INPUT, "mm_tve_input", "dpi0_sel", 7),
+	GATE_DISP1(CLK_MM_HDMI_PIXEL, "mm_hdmi_pixel", "dpi1_sel", 8),
+	GATE_DISP1(CLK_MM_HDMI_PLL, "mm_hdmi_pll", "hdmi_sel", 9),
+	GATE_DISP1(CLK_MM_HDMI_AUDIO, "mm_hdmi_audio", "apll_sel", 10),
+	GATE_DISP1(CLK_MM_HDMI_SPDIF, "mm_hdmi_spdif", "apll_sel", 11),
+	GATE_DISP1(CLK_MM_TVE_FMM, "mm_tve_fmm", "mm_sel", 14),
+};
+
+static void mtk_mmsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_MM_NR);
+
+	mtk_clk_register_gates(node, mm_clks, ARRAY_SIZE(mm_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_mm[] = {
+	{ .compatible = "mediatek,mt2701-mmsys", },
+	{}
+};
+
+static int clk_mt2701_mm_probe(struct platform_device *pdev)
+{
+	mtk_mmsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_mm_drv = {
+	.probe = clk_mt2701_mm_probe,
+	.driver = {
+		.name = "clk-mt2701-mm",
+		.of_match_table = of_match_clk_mt2701_mm,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_mm_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701-vdec.c b/drivers/clk/mediatek/clk-mt2701-vdec.c
new file mode 100644
index 0000000..613bb33
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701-vdec.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+static const struct mtk_gate_regs vdec0_cg_regs = {
+	.set_ofs = 0x0000,
+	.clr_ofs = 0x0004,
+	.sta_ofs = 0x0000,
+};
+
+static const struct mtk_gate_regs vdec1_cg_regs = {
+	.set_ofs = 0x0008,
+	.clr_ofs = 0x000c,
+	.sta_ofs = 0x0008,
+};
+
+#define GATE_VDEC0(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &vdec0_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr_inv,	\
+	}
+
+#define GATE_VDEC1(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &vdec1_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr_inv,	\
+	}
+
+static const struct mtk_gate vdec_clks[] = {
+	GATE_VDEC0(CLK_VDEC_CKGEN, "vdec_cken", "vdec_sel", 0),
+	GATE_VDEC1(CLK_VDEC_LARB, "vdec_larb_cken", "mm_sel", 0),
+};
+
+static void mtk_vdecsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_VDEC_NR);
+
+	mtk_clk_register_gates(node, vdec_clks, ARRAY_SIZE(vdec_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701_vdec[] = {
+	{ .compatible = "mediatek,mt2701-vdecsys", },
+	{}
+};
+
+static int clk_mt2701_vdec_probe(struct platform_device *pdev)
+{
+	mtk_vdecsys_init(pdev->dev.of_node);
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_vdec_drv = {
+	.probe = clk_mt2701_vdec_probe,
+	.driver = {
+		.name = "clk-mt2701-vdec",
+		.of_match_table = of_match_clk_mt2701_vdec,
+	},
+};
+
+builtin_platform_driver(clk_mt2701_vdec_drv);
diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c
new file mode 100644
index 0000000..08a2954
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt2701.c
@@ -0,0 +1,1033 @@
+/*
+ * Copyright (c) 2014 MediaTek Inc.
+ * Author: Shunli Wang <shunli.wang at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-gate.h"
+
+#include <dt-bindings/clock/mt2701-clk.h>
+
+/*
+ * For some clocks, we don't care what their actual rates are. And these
+ * clocks may change their rate on different products or different scenarios.
+ * So we model these clocks' rate as 0, to denote it's not an actual rate.
+ */
+#define DUMMY_RATE		0
+
+static DEFINE_SPINLOCK(lock);
+
+static const struct mtk_fixed_clk top_fixed_clks[] = {
+	FIXED_CLK(CLK_TOP_DPI, "dpi_ck", "clk26m",
+		108 * MHZ),
+	FIXED_CLK(CLK_TOP_DMPLL, "dmpll_ck", "clk26m",
+		400 * MHZ),
+	FIXED_CLK(CLK_TOP_VENCPLL, "vencpll_ck", "clk26m",
+		295750000),
+	FIXED_CLK(CLK_TOP_HDMI_0_PIX340M, "hdmi_0_pix340m", "clk26m",
+		340 * MHZ),
+	FIXED_CLK(CLK_TOP_HDMI_0_DEEP340M, "hdmi_0_deep340m", "clk26m",
+		340 * MHZ),
+	FIXED_CLK(CLK_TOP_HDMI_0_PLL340M, "hdmi_0_pll340m", "clk26m",
+		340 * MHZ),
+	FIXED_CLK(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_dig_cts", "clk26m",
+		300 * MHZ),
+	FIXED_CLK(CLK_TOP_HADDS2_FB, "hadds2_fbclk", "clk26m",
+		27 * MHZ),
+	FIXED_CLK(CLK_TOP_WBG_DIG_416M, "wbg_dig_ck_416m", "clk26m",
+		416 * MHZ),
+	FIXED_CLK(CLK_TOP_DSI0_LNTC_DSI, "dsi0_lntc_dsi", "clk26m",
+		143 * MHZ),
+	FIXED_CLK(CLK_TOP_HDMI_SCL_RX, "hdmi_scl_rx", "clk26m",
+		27 * MHZ),
+	FIXED_CLK(CLK_TOP_AUD_EXT1, "aud_ext1", "clk26m",
+		DUMMY_RATE),
+	FIXED_CLK(CLK_TOP_AUD_EXT2, "aud_ext2", "clk26m",
+		DUMMY_RATE),
+	FIXED_CLK(CLK_TOP_NFI1X_PAD, "nfi1x_pad", "clk26m",
+		DUMMY_RATE),
+};
+
+static const struct mtk_fixed_factor top_fixed_divs[] = {
+	FACTOR(CLK_TOP_SYSPLL, "syspll_ck", "mainpll", 1, 1),
+	FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll", 1, 2),
+	FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll", 1, 3),
+	FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll", 1, 5),
+	FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "mainpll", 1, 7),
+	FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "syspll_d2", 1, 2),
+	FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "syspll_d2", 1, 4),
+	FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "syspll_d2", 1, 8),
+	FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "syspll_d2", 1, 16),
+	FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "syspll_d3", 1, 2),
+	FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "syspll_d3", 1, 4),
+	FACTOR(CLK_TOP_SYSPLL2_D8, "syspll2_d8", "syspll_d3", 1, 8),
+	FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "syspll_d5", 1, 2),
+	FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "syspll_d5", 1, 4),
+	FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "syspll_d7", 1, 2),
+	FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "syspll_d7", 1, 4),
+
+	FACTOR(CLK_TOP_UNIVPLL, "univpll_ck", "univpll", 1, 1),
+	FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univpll", 1, 2),
+	FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll", 1, 3),
+	FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll", 1, 5),
+	FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll", 1, 7),
+	FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll", 1, 26),
+	FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univpll", 1, 52),
+	FACTOR(CLK_TOP_UNIVPLL_D108, "univpll_d108", "univpll", 1, 108),
+	FACTOR(CLK_TOP_USB_PHY48M, "usb_phy48m_ck", "univpll", 1, 26),
+	FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_d2", 1, 2),
+	FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_d2", 1, 4),
+	FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_d2", 1, 8),
+	FACTOR(CLK_TOP_8BDAC, "8bdac_ck", "univpll_d2", 1, 1),
+	FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_d3", 1, 2),
+	FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_d3", 1, 4),
+	FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_d3", 1, 8),
+	FACTOR(CLK_TOP_UNIVPLL2_D16, "univpll2_d16", "univpll_d3", 1, 16),
+	FACTOR(CLK_TOP_UNIVPLL2_D32, "univpll2_d32", "univpll_d3", 1, 32),
+	FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univpll_d5", 1, 2),
+	FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univpll_d5", 1, 4),
+	FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univpll_d5", 1, 8),
+
+	FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1),
+	FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2),
+	FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4),
+	FACTOR(CLK_TOP_MSDCPLL_D8, "msdcpll_d8", "msdcpll", 1, 8),
+
+	FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1),
+	FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2),
+
+	FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "dmpll_ck", 1, 2),
+	FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "dmpll_ck", 1, 4),
+	FACTOR(CLK_TOP_DMPLL_X2, "dmpll_x2", "dmpll_ck", 1, 1),
+
+	FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll", 1, 1),
+	FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll", 1, 2),
+	FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll", 1, 4),
+
+	FACTOR(CLK_TOP_VDECPLL, "vdecpll_ck", "vdecpll", 1, 1),
+	FACTOR(CLK_TOP_TVD2PLL, "tvd2pll_ck", "tvd2pll", 1, 1),
+	FACTOR(CLK_TOP_TVD2PLL_D2, "tvd2pll_d2", "tvd2pll", 1, 2),
+
+	FACTOR(CLK_TOP_MIPIPLL, "mipipll", "dpi_ck", 1, 1),
+	FACTOR(CLK_TOP_MIPIPLL_D2, "mipipll_d2", "dpi_ck", 1, 2),
+	FACTOR(CLK_TOP_MIPIPLL_D4, "mipipll_d4", "dpi_ck", 1, 4),
+
+	FACTOR(CLK_TOP_HDMIPLL, "hdmipll_ck", "hdmitx_dig_cts", 1, 1),
+	FACTOR(CLK_TOP_HDMIPLL_D2, "hdmipll_d2", "hdmitx_dig_cts", 1, 2),
+	FACTOR(CLK_TOP_HDMIPLL_D3, "hdmipll_d3", "hdmitx_dig_cts", 1, 3),
+
+	FACTOR(CLK_TOP_ARMPLL_1P3G, "armpll_1p3g_ck", "armpll", 1, 1),
+
+	FACTOR(CLK_TOP_AUDPLL, "audpll", "audpll_sel", 1, 1),
+	FACTOR(CLK_TOP_AUDPLL_D4, "audpll_d4", "audpll_sel", 1, 4),
+	FACTOR(CLK_TOP_AUDPLL_D8, "audpll_d8", "audpll_sel", 1, 8),
+	FACTOR(CLK_TOP_AUDPLL_D16, "audpll_d16", "audpll_sel", 1, 16),
+	FACTOR(CLK_TOP_AUDPLL_D24, "audpll_d24", "audpll_sel", 1, 24),
+
+	FACTOR(CLK_TOP_AUD1PLL_98M, "aud1pll_98m_ck", "aud1pll", 1, 3),
+	FACTOR(CLK_TOP_AUD2PLL_90M, "aud2pll_90m_ck", "aud2pll", 1, 3),
+	FACTOR(CLK_TOP_HADDS2PLL_98M, "hadds2pll_98m", "hadds2pll", 1, 3),
+	FACTOR(CLK_TOP_HADDS2PLL_294M, "hadds2pll_294m", "hadds2pll", 1, 1),
+	FACTOR(CLK_TOP_ETHPLL_500M, "ethpll_500m_ck", "ethpll", 1, 1),
+	FACTOR(CLK_TOP_CLK26M_D8, "clk26m_d8", "clk26m", 1, 8),
+	FACTOR(CLK_TOP_32K_INTERNAL, "32k_internal", "clk26m", 1, 793),
+	FACTOR(CLK_TOP_32K_EXTERNAL, "32k_external", "rtc32k", 1, 1),
+};
+
+static const char * const axi_parents[] = {
+	"clk26m",
+	"syspll1_d2",
+	"syspll_d5",
+	"syspll1_d4",
+	"univpll_d5",
+	"univpll2_d2",
+	"mmpll_d2",
+	"dmpll_d2"
+};
+
+static const char * const mem_parents[] = {
+	"clk26m",
+	"dmpll_ck"
+};
+
+static const char * const ddrphycfg_parents[] = {
+	"clk26m",
+	"syspll1_d8"
+};
+
+static const char * const mm_parents[] = {
+	"clk26m",
+	"vencpll_ck",
+	"syspll1_d2",
+	"syspll1_d4",
+	"univpll_d5",
+	"univpll1_d2",
+	"univpll2_d2",
+	"dmpll_ck"
+};
+
+static const char * const pwm_parents[] = {
+	"clk26m",
+	"univpll2_d4",
+	"univpll3_d2",
+	"univpll1_d4",
+};
+
+static const char * const vdec_parents[] = {
+	"clk26m",
+	"vdecpll_ck",
+	"syspll_d5",
+	"syspll1_d4",
+	"univpll_d5",
+	"univpll2_d2",
+	"vencpll_ck",
+	"msdcpll_d2",
+	"mmpll_d2"
+};
+
+static const char * const mfg_parents[] = {
+	"clk26m",
+	"mmpll_ck",
+	"dmpll_x2_ck",
+	"msdcpll_ck",
+	"clk26m",
+	"syspll_d3",
+	"univpll_d3",
+	"univpll1_d2"
+};
+
+static const char * const camtg_parents[] = {
+	"clk26m",
+	"univpll_d26",
+	"univpll2_d2",
+	"syspll3_d2",
+	"syspll3_d4",
+	"msdcpll_d2",
+	"mmpll_d2"
+};
+
+static const char * const uart_parents[] = {
+	"clk26m",
+	"univpll2_d8"
+};
+
+static const char * const spi_parents[] = {
+	"clk26m",
+	"syspll3_d2",
+	"syspll4_d2",
+	"univpll2_d4",
+	"univpll1_d8"
+};
+
+static const char * const usb20_parents[] = {
+	"clk26m",
+	"univpll1_d8",
+	"univpll3_d4"
+};
+
+static const char * const msdc30_parents[] = {
+	"clk26m",
+	"msdcpll_d2",
+	"syspll2_d2",
+	"syspll1_d4",
+	"univpll1_d4",
+	"univpll2_d4"
+};
+
+static const char * const audio_parents[] = {
+	"clk26m",
+	"syspll1_d16"
+};
+
+static const char * const aud_intbus_parents[] = {
+	"clk26m",
+	"syspll1_d4",
+	"syspll3_d2",
+	"syspll4_d2",
+	"univpll3_d2",
+	"univpll2_d4"
+};
+
+static const char * const pmicspi_parents[] = {
+	"clk26m",
+	"syspll1_d8",
+	"syspll2_d4",
+	"syspll4_d2",
+	"syspll3_d4",
+	"syspll2_d8",
+	"syspll1_d16",
+	"univpll3_d4",
+	"univpll_d26",
+	"dmpll_d2",
+	"dmpll_d4"
+};
+
+static const char * const scp_parents[] = {
+	"clk26m",
+	"syspll1_d8",
+	"dmpll_d2",
+	"dmpll_d4"
+};
+
+static const char * const dpi0_parents[] = {
+	"clk26m",
+	"mipipll",
+	"mipipll_d2",
+	"mipipll_d4",
+	"clk26m",
+	"tvdpll_ck",
+	"tvdpll_d2",
+	"tvdpll_d4"
+};
+
+static const char * const dpi1_parents[] = {
+	"clk26m",
+	"tvdpll_ck",
+	"tvdpll_d2",
+	"tvdpll_d4"
+};
+
+static const char * const tve_parents[] = {
+	"clk26m",
+	"mipipll",
+	"mipipll_d2",
+	"mipipll_d4",
+	"clk26m",
+	"tvdpll_ck",
+	"tvdpll_d2",
+	"tvdpll_d4"
+};
+
+static const char * const hdmi_parents[] = {
+	"clk26m",
+	"hdmipll_ck",
+	"hdmipll_d2",
+	"hdmipll_d3"
+};
+
+static const char * const apll_parents[] = {
+	"clk26m",
+	"audpll",
+	"audpll_d4",
+	"audpll_d8",
+	"audpll_d16",
+	"audpll_d24",
+	"clk26m",
+	"clk26m"
+};
+
+static const char * const rtc_parents[] = {
+	"32k_internal",
+	"32k_external",
+	"clk26m",
+	"univpll3_d8"
+};
+
+static const char * const nfi2x_parents[] = {
+	"clk26m",
+	"syspll2_d2",
+	"syspll_d7",
+	"univpll3_d2",
+	"syspll2_d4",
+	"univpll3_d4",
+	"syspll4_d4",
+	"clk26m"
+};
+
+static const char * const emmc_hclk_parents[] = {
+	"clk26m",
+	"syspll1_d2",
+	"syspll1_d4",
+	"syspll2_d2"
+};
+
+static const char * const flash_parents[] = {
+	"clk26m_d8",
+	"clk26m",
+	"syspll2_d8",
+	"syspll3_d4",
+	"univpll3_d4",
+	"syspll4_d2",
+	"syspll2_d4",
+	"univpll2_d4"
+};
+
+static const char * const di_parents[] = {
+	"clk26m",
+	"tvd2pll_ck",
+	"tvd2pll_d2",
+	"clk26m"
+};
+
+static const char * const nr_osd_parents[] = {
+	"clk26m",
+	"vencpll_ck",
+	"syspll1_d2",
+	"syspll1_d4",
+	"univpll_d5",
+	"univpll1_d2",
+	"univpll2_d2",
+	"dmpll_ck"
+};
+
+static const char * const hdmirx_bist_parents[] = {
+	"clk26m",
+	"syspll_d3",
+	"clk26m",
+	"syspll1_d16",
+	"syspll4_d2",
+	"syspll1_d4",
+	"vencpll_ck",
+	"clk26m"
+};
+
+static const char * const intdir_parents[] = {
+	"clk26m",
+	"mmpll_ck",
+	"syspll_d2",
+	"univpll_d2"
+};
+
+static const char * const asm_parents[] = {
+	"clk26m",
+	"univpll2_d4",
+	"univpll2_d2",
+	"syspll_d5"
+};
+
+static const char * const ms_card_parents[] = {
+	"clk26m",
+	"univpll3_d8",
+	"syspll4_d4"
+};
+
+static const char * const ethif_parents[] = {
+	"clk26m",
+	"syspll1_d2",
+	"syspll_d5",
+	"syspll1_d4",
+	"univpll_d5",
+	"univpll1_d2",
+	"dmpll_ck",
+	"dmpll_d2"
+};
+
+static const char * const hdmirx_parents[] = {
+	"clk26m",
+	"univpll_d52"
+};
+
+static const char * const cmsys_parents[] = {
+	"clk26m",
+	"syspll1_d2",
+	"univpll1_d2",
+	"univpll_d5",
+	"syspll_d5",
+	"syspll2_d2",
+	"syspll1_d4",
+	"syspll3_d2",
+	"syspll2_d4",
+	"syspll1_d8",
+	"clk26m",
+	"clk26m",
+	"clk26m",
+	"clk26m",
+	"clk26m"
+};
+
+static const char * const clk_8bdac_parents[] = {
+	"32k_internal",
+	"8bdac_ck",
+	"clk26m",
+	"clk26m"
+};
+
+static const char * const aud2dvd_parents[] = {
+	"a1sys_hp_ck",
+	"a2sys_hp_ck"
+};
+
+static const char * const padmclk_parents[] = {
+	"clk26m",
+	"univpll_d26",
+	"univpll_d52",
+	"univpll_d108",
+	"univpll2_d8",
+	"univpll2_d16",
+	"univpll2_d32"
+};
+
+static const char * const aud_mux_parents[] = {
+	"clk26m",
+	"aud1pll_98m_ck",
+	"aud2pll_90m_ck",
+	"hadds2pll_98m",
+	"audio_ext1_ck",
+	"audio_ext2_ck"
+};
+
+static const char * const aud_src_parents[] = {
+	"aud_mux1_sel",
+	"aud_mux2_sel"
+};
+
+static const char * const cpu_parents[] = {
+	"clk26m",
+	"armpll",
+	"mainpll",
+	"mmpll"
+};
+
+static const struct mtk_composite top_muxes[] = {
+	MUX_GATE_FLAGS(CLK_TOP_AXI_SEL, "axi_sel", axi_parents,
+		0x0040, 0, 3, INVALID_MUX_GATE_BIT, CLK_IS_CRITICAL),
+	MUX_GATE_FLAGS(CLK_TOP_MEM_SEL, "mem_sel", mem_parents,
+		0x0040, 8, 1, 15, CLK_IS_CRITICAL),
+	MUX_GATE_FLAGS(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel",
+		ddrphycfg_parents, 0x0040, 16, 1, 23, CLK_IS_CRITICAL),
+	MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents,
+		0x0040, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents,
+		0x0050, 0, 2, 7),
+	MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents,
+		0x0050, 8, 4, 15),
+	MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents,
+		0x0050, 16, 3, 23),
+	MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents,
+		0x0050, 24, 3, 31),
+	MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents,
+		0x0060, 0, 1, 7),
+
+	MUX_GATE(CLK_TOP_SPI0_SEL, "spi0_sel", spi_parents,
+		0x0060, 8, 3, 15),
+	MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents,
+		0x0060, 16, 2, 23),
+	MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents,
+		0x0060, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents,
+		0x0070, 0, 3, 7),
+	MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents,
+		0x0070, 8, 3, 15),
+	MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", msdc30_parents,
+		0x0070, 16, 1, 23),
+	MUX_GATE(CLK_TOP_AUDINTBUS_SEL, "aud_intbus_sel", aud_intbus_parents,
+		0x0070, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents,
+		0x0080, 0, 4, 7),
+	MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents,
+		0x0080, 8, 2, 15),
+	MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents,
+		0x0080, 16, 3, 23),
+	MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents,
+		0x0080, 24, 2, 31),
+
+	MUX_GATE(CLK_TOP_TVE_SEL, "tve_sel", tve_parents,
+		0x0090, 0, 3, 7),
+	MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents,
+		0x0090, 8, 2, 15),
+	MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents,
+		0x0090, 16, 3, 23),
+
+	MUX_GATE_FLAGS(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents,
+		0x00A0, 0, 2, 7, CLK_IS_CRITICAL),
+	MUX_GATE(CLK_TOP_NFI2X_SEL, "nfi2x_sel", nfi2x_parents,
+		0x00A0, 8, 3, 15),
+	MUX_GATE(CLK_TOP_EMMC_HCLK_SEL, "emmc_hclk_sel", emmc_hclk_parents,
+		0x00A0, 24, 2, 31),
+
+	MUX_GATE(CLK_TOP_FLASH_SEL, "flash_sel", flash_parents,
+		0x00B0, 0, 3, 7),
+	MUX_GATE(CLK_TOP_DI_SEL, "di_sel", di_parents,
+		0x00B0, 8, 2, 15),
+	MUX_GATE(CLK_TOP_NR_SEL, "nr_sel", nr_osd_parents,
+		0x00B0, 16, 3, 23),
+	MUX_GATE(CLK_TOP_OSD_SEL, "osd_sel", nr_osd_parents,
+		0x00B0, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_HDMIRX_BIST_SEL, "hdmirx_bist_sel",
+		hdmirx_bist_parents, 0x00C0, 0, 3, 7),
+	MUX_GATE(CLK_TOP_INTDIR_SEL, "intdir_sel", intdir_parents,
+		0x00C0, 8, 2, 15),
+	MUX_GATE(CLK_TOP_ASM_I_SEL, "asm_i_sel", asm_parents,
+		0x00C0, 16, 2, 23),
+	MUX_GATE(CLK_TOP_ASM_M_SEL, "asm_m_sel", asm_parents,
+		0x00C0, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_ASM_H_SEL, "asm_h_sel", asm_parents,
+		0x00D0, 0, 2, 7),
+	MUX_GATE(CLK_TOP_MS_CARD_SEL, "ms_card_sel", ms_card_parents,
+		0x00D0, 16, 2, 23),
+	MUX_GATE(CLK_TOP_ETHIF_SEL, "ethif_sel", ethif_parents,
+		0x00D0, 24, 3, 31),
+
+	MUX_GATE(CLK_TOP_HDMIRX26_24_SEL, "hdmirx26_24_sel", hdmirx_parents,
+		0x00E0, 0, 1, 7),
+	MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents,
+		0x00E0, 8, 3, 15),
+	MUX_GATE(CLK_TOP_CMSYS_SEL, "cmsys_sel", cmsys_parents,
+		0x00E0, 16, 4, 23),
+
+	MUX_GATE(CLK_TOP_SPI1_SEL, "spi2_sel", spi_parents,
+		0x00E0, 24, 3, 31),
+	MUX_GATE(CLK_TOP_SPI2_SEL, "spi1_sel", spi_parents,
+		0x00F0, 0, 3, 7),
+	MUX_GATE(CLK_TOP_8BDAC_SEL, "8bdac_sel", clk_8bdac_parents,
+		0x00F0, 8, 2, 15),
+	MUX_GATE(CLK_TOP_AUD2DVD_SEL, "aud2dvd_sel", aud2dvd_parents,
+		0x00F0, 16, 1, 23),
+
+	MUX(CLK_TOP_PADMCLK_SEL, "padmclk_sel", padmclk_parents,
+		0x0100, 0, 3),
+
+	MUX(CLK_TOP_AUD_MUX1_SEL, "aud_mux1_sel", aud_mux_parents,
+		0x012c, 0, 3),
+	MUX(CLK_TOP_AUD_MUX2_SEL, "aud_mux2_sel", aud_mux_parents,
+		0x012c, 3, 3),
+	MUX(CLK_TOP_AUDPLL_MUX_SEL, "audpll_sel", aud_mux_parents,
+		0x012c, 6, 3),
+	MUX_GATE(CLK_TOP_AUD_K1_SRC_SEL, "aud_k1_src_sel", aud_src_parents,
+		0x012c, 15, 1, 23),
+	MUX_GATE(CLK_TOP_AUD_K2_SRC_SEL, "aud_k2_src_sel", aud_src_parents,
+		0x012c, 16, 1, 24),
+	MUX_GATE(CLK_TOP_AUD_K3_SRC_SEL, "aud_k3_src_sel", aud_src_parents,
+		0x012c, 17, 1, 25),
+	MUX_GATE(CLK_TOP_AUD_K4_SRC_SEL, "aud_k4_src_sel", aud_src_parents,
+		0x012c, 18, 1, 26),
+	MUX_GATE(CLK_TOP_AUD_K5_SRC_SEL, "aud_k5_src_sel", aud_src_parents,
+		0x012c, 19, 1, 27),
+	MUX_GATE(CLK_TOP_AUD_K6_SRC_SEL, "aud_k6_src_sel", aud_src_parents,
+		0x012c, 20, 1, 28),
+};
+
+static const struct mtk_clk_divider top_adj_divs[] = {
+	DIV_ADJ(CLK_TOP_AUD_EXTCK1_DIV, "audio_ext1_ck", "aud_ext1",
+		0x0120, 0, 8),
+	DIV_ADJ(CLK_TOP_AUD_EXTCK2_DIV, "audio_ext2_ck", "aud_ext2",
+		0x0120, 8, 8),
+	DIV_ADJ(CLK_TOP_AUD_MUX1_DIV, "aud_mux1_div", "aud_mux1_sel",
+		0x0120, 16, 8),
+	DIV_ADJ(CLK_TOP_AUD_MUX2_DIV, "aud_mux2_div", "aud_mux2_sel",
+		0x0120, 24, 8),
+	DIV_ADJ(CLK_TOP_AUD_K1_SRC_DIV, "aud_k1_src_div", "aud_k1_src_sel",
+		0x0124, 0, 8),
+	DIV_ADJ(CLK_TOP_AUD_K2_SRC_DIV, "aud_k2_src_div", "aud_k2_src_sel",
+		0x0124, 8, 8),
+	DIV_ADJ(CLK_TOP_AUD_K3_SRC_DIV, "aud_k3_src_div", "aud_k3_src_sel",
+		0x0124, 16, 8),
+	DIV_ADJ(CLK_TOP_AUD_K4_SRC_DIV, "aud_k4_src_div", "aud_k4_src_sel",
+		0x0124, 24, 8),
+	DIV_ADJ(CLK_TOP_AUD_K5_SRC_DIV, "aud_k5_src_div", "aud_k5_src_sel",
+		0x0128, 0, 8),
+	DIV_ADJ(CLK_TOP_AUD_K6_SRC_DIV, "aud_k6_src_div", "aud_k6_src_sel",
+		0x0128, 8, 8),
+};
+
+static const struct mtk_gate_regs top_aud_cg_regs = {
+	.sta_ofs = 0x012C,
+};
+
+#define GATE_TOP_AUD(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &top_aud_cg_regs,		\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_no_setclr,	\
+	}
+
+static const struct mtk_gate top_clks[] = {
+	GATE_TOP_AUD(CLK_TOP_AUD_48K_TIMING, "a1sys_hp_ck", "aud_mux1_div",
+		21),
+	GATE_TOP_AUD(CLK_TOP_AUD_44K_TIMING, "a2sys_hp_ck", "aud_mux2_div",
+		22),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S1_MCLK, "aud_i2s1_mclk", "aud_k1_src_div",
+		23),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S2_MCLK, "aud_i2s2_mclk", "aud_k2_src_div",
+		24),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S3_MCLK, "aud_i2s3_mclk", "aud_k3_src_div",
+		25),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S4_MCLK, "aud_i2s4_mclk", "aud_k4_src_div",
+		26),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S5_MCLK, "aud_i2s5_mclk", "aud_k5_src_div",
+		27),
+	GATE_TOP_AUD(CLK_TOP_AUD_I2S6_MCLK, "aud_i2s6_mclk", "aud_k6_src_div",
+		28),
+};
+
+static void mtk_topckgen_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	void __iomem *base;
+	int r;
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("%s(): ioremap failed\n", __func__);
+		return;
+	}
+
+	clk_data = mtk_alloc_clk_data(CLK_TOP_NR);
+
+	mtk_clk_register_fixed_clks(top_fixed_clks, ARRAY_SIZE(top_fixed_clks),
+								clk_data);
+
+	mtk_clk_register_factors(top_fixed_divs, ARRAY_SIZE(top_fixed_divs),
+								clk_data);
+
+	mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes),
+				base, &lock, clk_data);
+
+	mtk_clk_register_dividers(top_adj_divs, ARRAY_SIZE(top_adj_divs),
+				base, &lock, clk_data);
+
+	mtk_clk_register_gates(node, top_clks, ARRAY_SIZE(top_clks),
+						clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct mtk_gate_regs infra_cg_regs = {
+	.set_ofs = 0x0040,
+	.clr_ofs = 0x0044,
+	.sta_ofs = 0x0048,
+};
+
+#define GATE_ICG(_id, _name, _parent, _shift) {		\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &infra_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+static const struct mtk_gate infra_clks[] = {
+	GATE_ICG(CLK_INFRA_DBG, "dbgclk", "axi_sel", 0),
+	GATE_ICG(CLK_INFRA_SMI, "smi_ck", "mm_sel", 1),
+	GATE_ICG(CLK_INFRA_QAXI_CM4, "cm4_ck", "axi_sel", 2),
+	GATE_ICG(CLK_INFRA_AUD_SPLIN_B, "audio_splin_bck", "hadds2pll_294m", 4),
+	GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "clk26m", 5),
+	GATE_ICG(CLK_INFRA_EFUSE, "efuse_ck", "clk26m", 6),
+	GATE_ICG(CLK_INFRA_L2C_SRAM, "l2c_sram_ck", "mm_sel", 7),
+	GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8),
+	GATE_ICG(CLK_INFRA_CONNMCU, "connsys_bus", "wbg_dig_ck_416m", 12),
+	GATE_ICG(CLK_INFRA_TRNG, "trng_ck", "axi_sel", 13),
+	GATE_ICG(CLK_INFRA_RAMBUFIF, "rambufif_ck", "mem_sel", 14),
+	GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "mem_sel", 15),
+	GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16),
+	GATE_ICG(CLK_INFRA_CEC, "cec_ck", "rtc_sel", 18),
+	GATE_ICG(CLK_INFRA_IRRX, "irrx_ck", "axi_sel", 19),
+	GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22),
+	GATE_ICG(CLK_INFRA_PMICWRAP, "pmicwrap_ck", "axi_sel", 23),
+	GATE_ICG(CLK_INFRA_DDCCI, "ddcci_ck", "axi_sel", 24),
+};
+
+static const struct mtk_fixed_factor infra_fixed_divs[] = {
+	FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2),
+};
+
+static struct clk_onecell_data *infra_clk_data;
+
+static void __init mtk_infrasys_init_early(struct device_node *node)
+{
+	int r, i;
+
+	if (!infra_clk_data) {
+		infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+
+		for (i = 0; i < CLK_INFRA_NR; i++)
+			infra_clk_data->clks[i] = ERR_PTR(-EPROBE_DEFER);
+	}
+
+	mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
+						infra_clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, infra_clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+CLK_OF_DECLARE(mtk_infra, "mediatek,mt2701-infracfg", mtk_infrasys_init_early);
+
+static void mtk_infrasys_init(struct device_node *node)
+{
+	int r, i;
+
+	if (!infra_clk_data) {
+		infra_clk_data = mtk_alloc_clk_data(CLK_INFRA_NR);
+	} else {
+		for (i = 0; i < CLK_INFRA_NR; i++) {
+			if (infra_clk_data->clks[i] == ERR_PTR(-EPROBE_DEFER))
+				infra_clk_data->clks[i] = ERR_PTR(-ENOENT);
+		}
+	}
+
+	mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks),
+						infra_clk_data);
+	mtk_clk_register_factors(infra_fixed_divs, ARRAY_SIZE(infra_fixed_divs),
+						infra_clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, infra_clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct mtk_gate_regs peri0_cg_regs = {
+	.set_ofs = 0x0008,
+	.clr_ofs = 0x0010,
+	.sta_ofs = 0x0018,
+};
+
+static const struct mtk_gate_regs peri1_cg_regs = {
+	.set_ofs = 0x000c,
+	.clr_ofs = 0x0014,
+	.sta_ofs = 0x001c,
+};
+
+#define GATE_PERI0(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &peri0_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+#define GATE_PERI1(_id, _name, _parent, _shift) {	\
+		.id = _id,				\
+		.name = _name,				\
+		.parent_name = _parent,			\
+		.regs = &peri1_cg_regs,			\
+		.shift = _shift,			\
+		.ops = &mtk_clk_gate_ops_setclr,	\
+	}
+
+static const struct mtk_gate peri_clks[] = {
+	GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 31),
+	GATE_PERI1(CLK_PERI_ETH, "eth_ck", "clk26m", 30),
+	GATE_PERI1(CLK_PERI_SPI0, "spi0_ck", "spi0_sel", 29),
+	GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 28),
+	GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "clk26m", 27),
+	GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 26),
+	GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 25),
+	GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 24),
+	GATE_PERI0(CLK_PERI_BTIF, "bitif_ck", "axi_sel", 23),
+	GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 22),
+	GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 21),
+	GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 20),
+	GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 19),
+	GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 18),
+	GATE_PERI0(CLK_PERI_MSDC50_3, "msdc50_3_ck", "emmc_hclk_sel", 17),
+	GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_3_sel", 16),
+	GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_2_sel", 15),
+	GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_1_sel", 14),
+	GATE_PERI0(CLK_PERI_MSDC30_0, "msdc30_0_ck", "msdc30_0_sel", 13),
+	GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12),
+	GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11),
+	GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10),
+	GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9),
+	GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8),
+	GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7),
+	GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6),
+	GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5),
+	GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4),
+	GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3),
+	GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2),
+	GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1),
+	GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "nfi2x_sel", 0),
+
+	GATE_PERI1(CLK_PERI_FCI, "fci_ck", "ms_card_sel", 11),
+	GATE_PERI1(CLK_PERI_SPI2, "spi2_ck", "spi2_sel", 10),
+	GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi1_sel", 9),
+	GATE_PERI1(CLK_PERI_HOST89_DVD, "host89_dvd_ck", "aud2dvd_sel", 8),
+	GATE_PERI1(CLK_PERI_HOST89_SPI, "host89_spi_ck", "spi0_sel", 7),
+	GATE_PERI1(CLK_PERI_HOST89_INT, "host89_int_ck", "axi_sel", 6),
+	GATE_PERI1(CLK_PERI_FLASH, "flash_ck", "nfi2x_sel", 5),
+	GATE_PERI1(CLK_PERI_NFI_PAD, "nfi_pad_ck", "nfi1x_pad", 4),
+	GATE_PERI1(CLK_PERI_NFI_ECC, "nfi_ecc_ck", "nfi1x_pad", 3),
+	GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "axi_sel", 2),
+	GATE_PERI1(CLK_PERI_USB_SLV, "usbslv_ck", "axi_sel", 1),
+	GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 0),
+};
+
+static const char * const uart_ck_sel_parents[] = {
+	"clk26m",
+	"uart_sel",
+};
+
+static const struct mtk_composite peri_muxs[] = {
+	MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents,
+		0x40c, 0, 1),
+	MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents,
+		0x40c, 1, 1),
+	MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents,
+		0x40c, 2, 1),
+	MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents,
+		0x40c, 3, 1),
+};
+
+static void mtk_pericfg_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	void __iomem *base;
+	int r;
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("%s(): ioremap failed\n", __func__);
+		return;
+	}
+
+	clk_data = mtk_alloc_clk_data(CLK_PERI_NR);
+
+	mtk_clk_register_gates(node, peri_clks, ARRAY_SIZE(peri_clks),
+						clk_data);
+
+	mtk_clk_register_composites(peri_muxs, ARRAY_SIZE(peri_muxs), base,
+			&lock, clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+#define MT8590_PLL_FMAX		(2000 * MHZ)
+#define CON0_MT8590_RST_BAR	BIT(27)
+
+#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \
+			_pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) {	\
+		.id = _id,						\
+		.name = _name,						\
+		.reg = _reg,						\
+		.pwr_reg = _pwr_reg,					\
+		.en_mask = _en_mask,					\
+		.flags = _flags,					\
+		.rst_bar_mask = CON0_MT8590_RST_BAR,			\
+		.fmax = MT8590_PLL_FMAX,				\
+		.pcwbits = _pcwbits,					\
+		.pd_reg = _pd_reg,					\
+		.pd_shift = _pd_shift,					\
+		.tuner_reg = _tuner_reg,				\
+		.pcw_reg = _pcw_reg,					\
+		.pcw_shift = _pcw_shift,				\
+	}
+
+static const struct mtk_pll_data apmixed_plls[] = {
+	PLL(CLK_APMIXED_ARMPLL, "armpll", 0x200, 0x20c, 0x80000001,
+			PLL_AO, 21, 0x204, 24, 0x0, 0x204, 0),
+	PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x210, 0x21c, 0xf0000001,
+		  HAVE_RST_BAR, 21, 0x210, 4, 0x0, 0x214, 0),
+	PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x220, 0x22c, 0xf3000001,
+		  HAVE_RST_BAR, 7, 0x220, 4, 0x0, 0x224, 14),
+	PLL(CLK_APMIXED_MMPLL, "mmpll", 0x230, 0x23c, 0x00000001, 0,
+				21, 0x230, 4, 0x0, 0x234, 0),
+	PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x240, 0x24c, 0x00000001, 0,
+				21, 0x240, 4, 0x0, 0x244, 0),
+	PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x250, 0x25c, 0x00000001, 0,
+				21, 0x250, 4, 0x0, 0x254, 0),
+	PLL(CLK_APMIXED_AUD1PLL, "aud1pll", 0x270, 0x27c, 0x00000001, 0,
+				31, 0x270, 4, 0x0, 0x274, 0),
+	PLL(CLK_APMIXED_TRGPLL, "trgpll", 0x280, 0x28c, 0x00000001, 0,
+				31, 0x280, 4, 0x0, 0x284, 0),
+	PLL(CLK_APMIXED_ETHPLL, "ethpll", 0x290, 0x29c, 0x00000001, 0,
+				31, 0x290, 4, 0x0, 0x294, 0),
+	PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x2a0, 0x2ac, 0x00000001, 0,
+				31, 0x2a0, 4, 0x0, 0x2a4, 0),
+	PLL(CLK_APMIXED_HADDS2PLL, "hadds2pll", 0x2b0, 0x2bc, 0x00000001, 0,
+				31, 0x2b0, 4, 0x0, 0x2b4, 0),
+	PLL(CLK_APMIXED_AUD2PLL, "aud2pll", 0x2c0, 0x2cc, 0x00000001, 0,
+				31, 0x2c0, 4, 0x0, 0x2c4, 0),
+	PLL(CLK_APMIXED_TVD2PLL, "tvd2pll", 0x2d0, 0x2dc, 0x00000001, 0,
+				21, 0x2d0, 4, 0x0, 0x2d4, 0),
+};
+
+static void mtk_apmixedsys_init(struct device_node *node)
+{
+	struct clk_onecell_data *clk_data;
+	int r;
+
+	clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR);
+	if (!clk_data)
+		return;
+
+	mtk_clk_register_plls(node, apmixed_plls, ARRAY_SIZE(apmixed_plls),
+								clk_data);
+
+	r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+	if (r)
+		pr_err("%s(): could not register clock provider: %d\n",
+			__func__, r);
+}
+
+static const struct of_device_id of_match_clk_mt2701[] = {
+	{
+		.compatible = "mediatek,mt2701-topckgen",
+		.data = mtk_topckgen_init,
+	}, {
+		.compatible = "mediatek,mt2701-infracfg",
+		.data = mtk_infrasys_init,
+	}, {
+		.compatible = "mediatek,mt2701-pericfg",
+		.data = mtk_pericfg_init,
+	}, {
+		.compatible = "mediatek,mt2701-apmixedsys",
+		.data = mtk_apmixedsys_init,
+	}, {
+		/* sentinel */
+	}
+};
+
+static int clk_mt2701_probe(struct platform_device *pdev)
+{
+	void (*clk_init)(struct device_node *);
+	const struct of_device_id *of_id;
+
+	pr_warn("%s():%d: %s\n", __func__, __LINE__, pdev->name);
+
+	of_id = of_match_node(of_match_clk_mt2701, pdev->dev.of_node);
+	if (!of_id || !of_id->data)
+		return -EINVAL;
+
+	clk_init = of_id->data;
+	clk_init(pdev->dev.of_node);
+
+	return 0;
+}
+
+static struct platform_driver clk_mt2701_drv = {
+	.probe = clk_mt2701_probe,
+	.driver = {
+		.name = "clk-mt2701",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_clk_mt2701,
+	},
+};
+
+static int __init clk_mt2701_init(void)
+{
+	return platform_driver_register(&clk_mt2701_drv);
+}
+
+arch_initcall(clk_mt2701_init);
diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c
index bb30f70..6a015a8 100644
--- a/drivers/clk/mediatek/clk-mtk.c
+++ b/drivers/clk/mediatek/clk-mtk.c
@@ -58,6 +58,9 @@ void mtk_clk_register_fixed_clks(const struct mtk_fixed_clk *clks,
 	for (i = 0; i < num; i++) {
 		const struct mtk_fixed_clk *rc = &clks[i];
 
+		if (!IS_ERR_OR_NULL(clk_data->clks[rc->id]))
+			continue;
+
 		clk = clk_register_fixed_rate(NULL, rc->name, rc->parent, 0,
 					      rc->rate);
 
@@ -81,6 +84,9 @@ void mtk_clk_register_factors(const struct mtk_fixed_factor *clks,
 	for (i = 0; i < num; i++) {
 		const struct mtk_fixed_factor *ff = &clks[i];
 
+		if (!IS_ERR_OR_NULL(clk_data->clks[ff->id]))
+			continue;
+
 		clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name,
 				CLK_SET_RATE_PARENT, ff->mult, ff->div);
 
@@ -116,6 +122,9 @@ int mtk_clk_register_gates(struct device_node *node,
 	for (i = 0; i < num; i++) {
 		const struct mtk_gate *gate = &clks[i];
 
+		if (!IS_ERR_OR_NULL(clk_data->clks[gate->id]))
+			continue;
+
 		clk = mtk_clk_register_gate(gate->name, gate->parent_name,
 				regmap,
 				gate->regs->set_ofs,
@@ -232,6 +241,9 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs,
 	for (i = 0; i < num; i++) {
 		const struct mtk_composite *mc = &mcs[i];
 
+		if (!IS_ERR_OR_NULL(clk_data->clks[mc->id]))
+			continue;
+
 		clk = mtk_clk_register_composite(mc, base, lock);
 
 		if (IS_ERR(clk)) {
@@ -244,3 +256,31 @@ void mtk_clk_register_composites(const struct mtk_composite *mcs,
 			clk_data->clks[mc->id] = clk;
 	}
 }
+
+void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
+			int num, void __iomem *base, spinlock_t *lock,
+				struct clk_onecell_data *clk_data)
+{
+	struct clk *clk;
+	int i;
+
+	for (i = 0; i <  num; i++) {
+		const struct mtk_clk_divider *mcd = &mcds[i];
+
+		if (!IS_ERR_OR_NULL(clk_data->clks[mcd->id]))
+			continue;
+
+		clk = clk_register_divider(NULL, mcd->name, mcd->parent_name,
+			mcd->flags, base +  mcd->div_reg, mcd->div_shift,
+			mcd->div_width, mcd->clk_divider_flags, lock);
+
+		if (IS_ERR(clk)) {
+			pr_err("Failed to register clk %s: %ld\n",
+				mcd->name, PTR_ERR(clk));
+			continue;
+		}
+
+		if (clk_data)
+			clk_data->clks[mcd->id] = clk;
+	}
+}
diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h
index 9f24fcf..f5d6b70 100644
--- a/drivers/clk/mediatek/clk-mtk.h
+++ b/drivers/clk/mediatek/clk-mtk.h
@@ -87,7 +87,8 @@ struct mtk_composite {
  * In case the rate change propagation to parent clocks is undesirable,
  * this macro allows to specify the clock flags manually.
  */
-#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, _flags) {	\
+#define MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width,	\
+			_gate, _flags) {				\
 		.id = _id,						\
 		.name = _name,						\
 		.mux_reg = _reg,					\
@@ -106,7 +107,8 @@ struct mtk_composite {
  * parent clock by default.
  */
 #define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate)	\
-	MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width, _gate, CLK_SET_RATE_PARENT)
+	MUX_GATE_FLAGS(_id, _name, _parents, _reg, _shift, _width,	\
+		_gate, CLK_SET_RATE_PARENT)
 
 #define MUX(_id, _name, _parents, _reg, _shift, _width) {		\
 		.id = _id,						\
@@ -121,7 +123,8 @@ struct mtk_composite {
 		.flags = CLK_SET_RATE_PARENT,				\
 	}
 
-#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) {	\
+#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg,	\
+					_div_width, _div_shift) {	\
 		.id = _id,						\
 		.parent = _parent,					\
 		.name = _name,						\
@@ -156,12 +159,40 @@ struct mtk_gate {
 	const struct clk_ops *ops;
 };
 
-int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks,
-		int num, struct clk_onecell_data *clk_data);
+int mtk_clk_register_gates(struct device_node *node,
+			const struct mtk_gate *clks, int num,
+			struct clk_onecell_data *clk_data);
+
+struct mtk_clk_divider {
+	int id;
+	const char *name;
+	const char *parent_name;
+	unsigned long flags;
+
+	u32 div_reg;
+	unsigned char div_shift;
+	unsigned char div_width;
+	unsigned char clk_divider_flags;
+	const struct clk_div_table *clk_div_table;
+};
+
+#define DIV_ADJ(_id, _name, _parent, _reg, _shift, _width) {	\
+		.id = _id,					\
+		.name = _name,					\
+		.parent_name = _parent,				\
+		.div_reg = _reg,				\
+		.div_shift = _shift,				\
+		.div_width = _width,				\
+}
+
+void mtk_clk_register_dividers(const struct mtk_clk_divider *mcds,
+			int num, void __iomem *base, spinlock_t *lock,
+				struct clk_onecell_data *clk_data);
 
 struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num);
 
 #define HAVE_RST_BAR	BIT(0)
+#define PLL_AO		BIT(1)
 
 struct mtk_pll_div_table {
 	u32 div;
diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c
index 0c2deac..a409142 100644
--- a/drivers/clk/mediatek/clk-pll.c
+++ b/drivers/clk/mediatek/clk-pll.c
@@ -301,6 +301,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data,
 	pll->data = data;
 
 	init.name = data->name;
+	init.flags = (data->flags & PLL_AO) ? CLK_IS_CRITICAL : 0;
 	init.ops = &mtk_pll_ops;
 	init.parent_names = &parent_name;
 	init.num_parents = 1;
-- 
1.9.1



From honghui.zhang at mediatek.com  Wed Jun 22 00:45:47 2016
From: honghui.zhang at mediatek.com (Honghui Zhang)
Date: Wed, 22 Jun 2016 15:45:47 +0800
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <1466574342.27791.8.camel@mtksdaap41>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621095748.GE28248@8bytes.org> <1466574342.27791.8.camel@mtksdaap41>
Message-ID: <1466581547.8787.18.camel@mtksdaap41>

On Wed, 2016-06-22 at 13:45 +0800, Eddie Huang wrote:
> On Tue, 2016-06-21 at 17:57 +0800, Joerg Roedel wrote:
> > On Wed, Jun 08, 2016 at 05:51:01PM +0800, honghui.zhang at mediatek.com wrote:
> > > From: Honghui Zhang <honghui.zhang at mediatek.com>
> > > 
> > > Add the dtsi node of iommu and smi for mt2701.
> > > 
> > > Signed-off-by: Honghui Zhang <honghui.zhang at mediatek.com>
> > > ---
> > >  arch/arm/boot/dts/mt2701.dtsi | 51 +++++++++++++++++++++++++++++++++++++++++++
> > >  1 file changed, 51 insertions(+)
> > 
> > Okay, I pushed my arm/mediatek branch to my tree at
> > 
> > 	git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu.git
> > 
> > Please base the patch on that branch and re-send.
> > 
> 
> I think it is better let Mediatek SoC maintainer Matthias to accept dtsi
> patch like other drivers. This can avoid dtsi conflict. As I
> remember,last time MT8173 IOMMU dtsi patch accepted in iommu tree and
> mt8173.dtsi had conflict with arm soc tree at the merge window. Honghui
> should resend this patch to Matthias, and elaborate your dependency with
> clock and power domain dtsi, then Matthias know the merge sequence. 
> 

Thanks, Eddie.

Hi, Joerg, 
This one is based on CCF "arm: dts: mt2701: Add clock controller device
nodes"[1] and power domain patch "Mediatek MT2701 SCPSYS power domain
support v7"[2],
But these two patchset are still being review now.

Do you think it's better that I send this one later after ccf and power
domain patch got merged, and let Matthias take it?


Thanks.
[1] https://patchwork.kernel.org/patch/9109081
[2]
http://lists.infradead.org/pipermail/linux-mediatek/2016-May/005429.html

> Eddie
> Thanks 
> 
> 




From joro at 8bytes.org  Wed Jun 22 01:31:17 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Wed, 22 Jun 2016 10:31:17 +0200
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <1466581547.8787.18.camel@mtksdaap41>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621095748.GE28248@8bytes.org>
 <1466574342.27791.8.camel@mtksdaap41>
 <1466581547.8787.18.camel@mtksdaap41>
Message-ID: <20160622083117.GG11432@8bytes.org>

On Wed, Jun 22, 2016 at 03:45:47PM +0800, Honghui Zhang wrote:
> This one is based on CCF "arm: dts: mt2701: Add clock controller device
> nodes"[1] and power domain patch "Mediatek MT2701 SCPSYS power domain
> support v7"[2],
> But these two patchset are still being review now.
> 
> Do you think it's better that I send this one later after ccf and power
> domain patch got merged, and let Matthias take it?

Sure, but the patches I already merged can stay in the tree for now?



	Joerg



From honghui.zhang at mediatek.com  Wed Jun 22 01:39:01 2016
From: honghui.zhang at mediatek.com (Honghui Zhang)
Date: Wed, 22 Jun 2016 16:39:01 +0800
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <20160622083117.GG11432@8bytes.org>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621095748.GE28248@8bytes.org> <1466574342.27791.8.camel@mtksdaap41>
 <1466581547.8787.18.camel@mtksdaap41> <20160622083117.GG11432@8bytes.org>
Message-ID: <1466584741.8787.22.camel@mtksdaap41>

On Wed, 2016-06-22 at 10:31 +0200, Joerg Roedel wrote:
> On Wed, Jun 22, 2016 at 03:45:47PM +0800, Honghui Zhang wrote:
> > This one is based on CCF "arm: dts: mt2701: Add clock controller device
> > nodes"[1] and power domain patch "Mediatek MT2701 SCPSYS power domain
> > support v7"[2],
> > But these two patchset are still being review now.
> > 
> > Do you think it's better that I send this one later after ccf and power
> > domain patch got merged, and let Matthias take it?
> 
> Sure, but the patches I already merged can stay in the tree for now?
> 
> 

I think that driver and dtsi could merged separately, I would prefer
those previous patches stay in the tree if it's OK for you.

Thanks.
> 
> 	Joerg
> 




From joro at 8bytes.org  Wed Jun 22 01:58:29 2016
From: joro at 8bytes.org (Joerg Roedel)
Date: Wed, 22 Jun 2016 10:58:29 +0200
Subject: [PATCH v4 5/5] ARM: dts: mt2701: add iommu/smi dtsi node for
 mt2701
In-Reply-To: <1466584741.8787.22.camel@mtksdaap41>
References: <1465379461-14757-1-git-send-email-honghui.zhang@mediatek.com>
 <1465379461-14757-6-git-send-email-honghui.zhang@mediatek.com>
 <20160621095748.GE28248@8bytes.org>
 <1466574342.27791.8.camel@mtksdaap41>
 <1466581547.8787.18.camel@mtksdaap41>
 <20160622083117.GG11432@8bytes.org>
 <1466584741.8787.22.camel@mtksdaap41>
Message-ID: <20160622085829.GH11432@8bytes.org>

On Wed, Jun 22, 2016 at 04:39:01PM +0800, Honghui Zhang wrote:
> I think that driver and dtsi could merged separately, I would prefer
> those previous patches stay in the tree if it's OK for you.

Sure, I send them upstream through my tree.


Thanks,

	Joerg



From matthias.bgg at gmail.com  Wed Jun 22 02:58:19 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Wed, 22 Jun 2016 11:58:19 +0200
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466574193.27740.6.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <574C5CBF.7060002@gmail.com> <1464683762.14604.59.camel@mtksdaap41>
 <574DEE40.9010008@gmail.com> <1464775020.11122.40.camel@mtksdaap41>
 <574FF264.7050209@gmail.com> <1464934356.15175.31.camel@mtksdaap41>
 <57516774.5080008@gmail.com> <1464956037.16029.8.camel@mtksdaap41>
 <575181E5.6090603@gmail.com> <5756FD73.3050607@gmail.com>
 <1465364427.9963.13.camel@mtksdaap41> <5757F762.4020908@gmail.com>
 <1465388727.21326.8.camel@mtksdaap41> <57583B45.2080504@gmail.com>
 <1465890268.7191.13.camel@mtksdaap41> <575FD9BA.8040708@gmail.com>
 <1465906063.20796.20.camel@mtksdaap41> <1466152107.11184.14.camel@mtksdaap41>
 <57641E01.3070205@gmail.com> <1466488358.8045.19.camel@mtksdaap41>
 <5769440D.5030505@gmail.com> <1466574193.27740.6.camel@mtksdaap41>
Message-ID: <8aec0411-a85a-f669-b73e-578471493858@gmail.com>



On 06/22/2016 07:43 AM, Horng-Shyang Liao wrote:
> On Tue, 2016-06-21 at 15:41 +0200, Matthias Brugger wrote:
>>
>> On 21/06/16 07:52, Horng-Shyang Liao wrote:
>>> On Fri, 2016-06-17 at 17:57 +0200, Matthias Brugger wrote:
>>>>
>>>> On 17/06/16 10:28, Horng-Shyang Liao wrote:
>>>>> Hi Matthias,
>>>>>
>>>>> On Tue, 2016-06-14 at 20:07 +0800, Horng-Shyang Liao wrote:
>>>>>> Hi Matthias,
>>>>>>
>>>>>> On Tue, 2016-06-14 at 12:17 +0200, Matthias Brugger wrote:
>>>>>>>
>>>>>>> On 14/06/16 09:44, Horng-Shyang Liao wrote:
>>>>>>>> Hi Matthias,
>>>>>>>>
>>>>>>>> On Wed, 2016-06-08 at 17:35 +0200, Matthias Brugger wrote:
>>>>>>>>>
>>>>>>>>> On 08/06/16 14:25, Horng-Shyang Liao wrote:
>>>>>>>>>> Hi Matthias,
>>>>>>>>>>
>>>>>>>>>> On Wed, 2016-06-08 at 12:45 +0200, Matthias Brugger wrote:
>>>>>>>>>>>
>>>>>>>>>>> On 08/06/16 07:40, Horng-Shyang Liao wrote:
>>>>>>>>>>>> Hi Matthias,
>>>>>>>>>>>>
>>>>>>>>>>>> On Tue, 2016-06-07 at 18:59 +0200, Matthias Brugger wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>> On 03/06/16 15:11, Matthias Brugger wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>> [...]
>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +            smp_mb(); /* modify jump before enable thread */
>>>>>>>>>>>>>>>>>>>>>>> +        }
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_writel(thread, task->pa_base +
>>>>>>>>>>>>>>>>>>>>>>> task->command_size,
>>>>>>>>>>>>>>>>>>>>>>> +                   CMDQ_THR_END_ADDR);
>>>>>>>>>>>>>>>>>>>>>>> +        cmdq_thread_resume(thread);
>>>>>>>>>>>>>>>>>>>>>>> +    }
>>>>>>>>>>>>>>>>>>>>>>> +    list_move_tail(&task->list_entry, &thread->task_busy_list);
>>>>>>>>>>>>>>>>>>>>>>> +    spin_unlock_irqrestore(&cmdq->exec_lock, flags);
>>>>>>>>>>>>>>>>>>>>>>> +}
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +static void cmdq_handle_error_done(struct cmdq *cmdq,
>>>>>>>>>>>>>>>>>>>>>>> +                   struct cmdq_thread *thread, u32 irq_flag)
>>>>>>>>>>>>>>>>>>>>>>> +{
>>>>>>>>>>>>>>>>>>>>>>> +    struct cmdq_task *task, *tmp, *curr_task = NULL;
>>>>>>>>>>>>>>>>>>>>>>> +    u32 curr_pa;
>>>>>>>>>>>>>>>>>>>>>>> +    struct cmdq_cb_data cmdq_cb_data;
>>>>>>>>>>>>>>>>>>>>>>> +    bool err;
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    if (irq_flag & CMDQ_THR_IRQ_ERROR)
>>>>>>>>>>>>>>>>>>>>>>> +        err = true;
>>>>>>>>>>>>>>>>>>>>>>> +    else if (irq_flag & CMDQ_THR_IRQ_DONE)
>>>>>>>>>>>>>>>>>>>>>>> +        err = false;
>>>>>>>>>>>>>>>>>>>>>>> +    else
>>>>>>>>>>>>>>>>>>>>>>> +        return;
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    curr_pa = cmdq_thread_readl(thread, CMDQ_THR_CURR_ADDR);
>>>>>>>>>>>>>>>>>>>>>>> +
>>>>>>>>>>>>>>>>>>>>>>> +    list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
>>>>>>>>>>>>>>>>>>>>>>> +                 list_entry) {
>>>>>>>>>>>>>>>>>>>>>>> +        if (curr_pa >= task->pa_base &&
>>>>>>>>>>>>>>>>>>>>>>> +            curr_pa < (task->pa_base + task->command_size))
>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>>> What are you checking here? It seems as if you make some implcit
>>>>>>>>>>>>>>>>>>>>>> assumptions about pa_base and the order of execution of
>>>>>>>>>>>>>>>>>>>>>> commands in the
>>>>>>>>>>>>>>>>>>>>>> thread. Is it save to do so? Does dma_alloc_coherent give any
>>>>>>>>>>>>>>>>>>>>>> guarantees
>>>>>>>>>>>>>>>>>>>>>> about dma_handle?
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>> 1. Check what is the current running task in this GCE thread.
>>>>>>>>>>>>>>>>>>>>> 2. Yes.
>>>>>>>>>>>>>>>>>>>>> 3. Yes, CMDQ doesn't use iommu, so physical address is continuous.
>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> Yes, physical addresses might be continous, but AFAIK there is no
>>>>>>>>>>>>>>>>>>>> guarantee that the dma_handle address is steadily growing, when
>>>>>>>>>>>>>>>>>>>> calling
>>>>>>>>>>>>>>>>>>>> dma_alloc_coherent. And if I understand the code correctly, you
>>>>>>>>>>>>>>>>>>>> use this
>>>>>>>>>>>>>>>>>>>> assumption to decide if the task picked from task_busy_list is
>>>>>>>>>>>>>>>>>>>> currently
>>>>>>>>>>>>>>>>>>>> executing. So I think this mecanism is not working.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> I don't use dma_handle address, and just use physical addresses.
>>>>>>>>>>>>>>>>>>>         From CPU's point of view, tasks are linked by the busy list.
>>>>>>>>>>>>>>>>>>>         From GCE's point of view, tasks are linked by the JUMP command.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>>> In which cases does the HW thread raise an interrupt.
>>>>>>>>>>>>>>>>>>>> In case of error. When does CMDQ_THR_IRQ_DONE get raised?
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> GCE will raise interrupt if any task is done or error.
>>>>>>>>>>>>>>>>>>> However, GCE is fast, so CPU may get multiple done tasks
>>>>>>>>>>>>>>>>>>> when it is running ISR.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>> In case of error, that GCE thread will pause and raise interrupt.
>>>>>>>>>>>>>>>>>>> So, CPU may get multiple done tasks and one error task.
>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>> I think we should reimplement the ISR mechanism. Can't we just read
>>>>>>>>>>>>>>>>>> CURR_IRQ_STATUS and THR_IRQ_STATUS in the handler and leave
>>>>>>>>>>>>>>>>>> cmdq_handle_error_done to the thread_fn? You will need to pass
>>>>>>>>>>>>>>>>>> information from the handler to thread_fn, but that shouldn't be an
>>>>>>>>>>>>>>>>>> issue. AFAIK interrupts are disabled in the handler, so we should stay
>>>>>>>>>>>>>>>>>> there as short as possible. Traversing task_busy_list is expensive, so
>>>>>>>>>>>>>>>>>> we need to do it in a thread context.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>> Actually, our initial implementation is similar to your suggestion,
>>>>>>>>>>>>>>>>> but display needs CMDQ to return callback function very precisely,
>>>>>>>>>>>>>>>>> else display will drop frame.
>>>>>>>>>>>>>>>>> For display, CMDQ interrupt will be raised every 16 ~ 17 ms,
>>>>>>>>>>>>>>>>> and CMDQ needs to call callback function in ISR.
>>>>>>>>>>>>>>>>> If we defer callback to workqueue, the time interval may be larger than
>>>>>>>>>>>>>>>>> 32 ms.sometimes.
>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>> I think the problem is, that you implemented the workqueue as a ordered
>>>>>>>>>>>>>>>> workqueue, so there is no parallel processing. I'm still not sure why
>>>>>>>>>>>>>>>> you need the workqueue to be ordered. Can you please explain.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> The order should be kept.
>>>>>>>>>>>>>>> Let me use mouse cursor as an example.
>>>>>>>>>>>>>>> If task 1 means move mouse cursor to point A, task 2 means point B,
>>>>>>>>>>>>>>> and task 3 means point C, our expected result is A -> B -> C.
>>>>>>>>>>>>>>> If the order is not kept, the result could become A -> C -> B.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Got it, thanks for the clarification.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> I think a way to get rid of the workqueue is to use a timer, which gets
>>>>>>>>>>>>> programmed to the time a timeout in the first task in the busy list
>>>>>>>>>>>>> would happen. Everytime we update the busy list (e.g. because of task
>>>>>>>>>>>>> got finished by the thread), we update the timer. When the timer
>>>>>>>>>>>>> triggers, which hopefully won't happen too often, we return timeout on
>>>>>>>>>>>>> the busy list elements, until the time is lower then the actual time.
>>>>>>>>>>>>>
>>>>>>>>>>>>> At least with this we can reduce the data structures in this driver and
>>>>>>>>>>>>> make it more lightweight.
>>>>>>>>>>>>
>>>>>>>>>>>>      From my understanding, your proposed method can handle timeout case.
>>>>>>>>>>>>
>>>>>>>>>>>> However, the workqueue is also in charge of releasing tasks.
>>>>>>>>>>>> Do you take releasing tasks into consideration by using the proposed
>>>>>>>>>>>> timer method?
>>>>>>>>>>>> Furthermore, I think the code will become more complex if we also use
>>>>>>>>>>>> timer to implement releasing tasks.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Can't we call
>>>>>>>>>>>              clk_disable_unprepare(cmdq->clock);
>>>>>>>>>>>              cmdq_task_release(task);
>>>>>>>>>>> after invoking the callback?
>>>>>
>>>>> After I put clk_disable_unprepare(cmdq->clock) into ISR, I encounter
>>>>> another BUG.
>>>>>
>>>>> (Quote some Linux 4.7 source code.)
>>>>>
>>>>>    605 void clk_unprepare(struct clk *clk)
>>>>>    606 {
>>>>>    607         if (IS_ERR_OR_NULL(clk))
>>>>>    608                 return;
>>>>>    609
>>>>>    610         clk_prepare_lock();                      // <-- Here
>>>>>    611         clk_core_unprepare(clk->core);
>>>>>    612         clk_prepare_unlock();
>>>>>    613 }
>>>>>    614 EXPORT_SYMBOL_GPL(clk_unprepare);
>>>>>
>>>>>     91 static void clk_prepare_lock(void)
>>>>>     92 {
>>>>>     93         if (!mutex_trylock(&prepare_lock)) {     // <-- Here
>>>>>     94                 if (prepare_owner == current) {
>>>>>     95                         prepare_refcnt++;
>>>>>     96                         return;
>>>>>     97                 }
>>>>>     98                 mutex_lock(&prepare_lock);
>>>>>     99         }
>>>>>    100         WARN_ON_ONCE(prepare_owner != NULL);
>>>>>    101         WARN_ON_ONCE(prepare_refcnt != 0);
>>>>>    102         prepare_owner = current;
>>>>>    103         prepare_refcnt = 1;
>>>>>    104 }
>>>>>
>>>>> So, 'unprepare' can sleep and cannot be put into ISR.
>>>>> I also try to put it into a timer, but the error is the same
>>>>> since timer callback is executed by softirq.
>>>>>
>>>>> We need clk_disable_unprepare() since it can save power consumption
>>>>> in idle.
>>>>
>>>> We can call clk_prepare in probe and then use clk_enable/clk_disable,
>>>> which don't sleep.
>>>>
>>>> Regards,
>>>> Matthias
>>>
>>> Hi Matthias,
>>>
>>> Because clock gate and MUX are controlled by clk_enable/clk_disable,
>>> and PLL is controlled by clk_prepare/clk_unprepare,
>>> I still need to call clk_unprepare.
>>>
>>> After I remove releasing buffer, releasing task, and timeout task from
>>> work, the work can be detached from task.
>>>
>>> Therefore, I can use the following flow to reduce the number of works.
>>>
>>> if task_busy_list from empty to non-empty
>>> 	clk_prepare_enable
>>> if task_busy_list from non-empty to empty
>>> 	in ISR, add work for clk_disable_unprepare
>>>
>>> What do you think of this solution?
>>
>> Can't we just call clk_prepare in probe and clk_unprepare in remove? I
>> think this could be a good starting point, and if we see, that we need
>> to save more energy in the future, we can think of some other mechanism.
>> What do you think?
>>
>> Regards,
>> Matthias
>
> Hi Matthias,
>
> As far as I know, we should call clk_unprepare to save more energy.
>
> May I call clk_prepare in probe/resume and clk_unprepare in
> remove/suspend in this patch, and then prepare another patch to call
> clk_unprepare in idle to save more energy?
>

Sure. This was just a suggestion to a first working version of the 
driver to which we can add step-by-step new functionality.

Regards,
Matthias

> Thanks,
> HS
>
>>>
>>> Thanks,
>>> HS
>>>
>>>>> Therefore, I plan to
>>>>> (1) move releasing buffer and task into ISR,
>>>>> (2) move timeout into timer, and
>>>>> (3) keep workqueue for clk_disable_unprepare().
>>>>>
>>>>> What do you think?
>>>>>
>>>>> Thanks,
>>>>> HS
>>>>>
>>>>>>>>>>
>>>>>>>>>> Do you mean just call these two functions in ISR?
>>>>>>>>>> My major concern is dma_free_coherent() and kfree() in
>>>>>>>>>> cmdq_task_release(task).
>>>>>>>>>
>>>>>>>>> Why do we need the dma calls at all? Can't we just calculate the
>>>>>>>>> physical address using __pa(x)?
>>>>>>>>
>>>>>>>> I prefer to use dma_map_single/dma_unmap_single.
>>>>>>>>
>>>>>>>
>>>>>>> Can you please elaborate why you need this. We don't do dma, so we
>>>>>>> should not use dma memory for this.
>>>>>>
>>>>>> We need a buffer to share between CPU and GCE, so we do need DMA.
>>>>>> CPU is in charge of writing GCE commands into this buffer.
>>>>>> GCE is in charge of reading and running GCE commands from this buffer.
>>>>>> When we chain CMDQ tasks, we also need to modify GCE JUMP command.
>>>>>> Therefore, I prefer to use dma_alloc_coherent and dma_free_coherent.
>>>>>>
>>>>>> However, if we want to use timer to handle timeout, we need to release
>>>>>> memory in ISR.
>>>>>> In this case, using kmalloc/kfree + dma_map_single/dma_unmap_single
>>>>>> instead of dma_alloc_coherent/dma_free_coherent is an alternative
>>>>>> solution, but taking care the synchronization between cache and memory
>>>>>> is the expected overhead.
>>>>>>
>>>>>>>>>> Therefore, your suggestion is to use GFP_ATOMIC for both
>>>>>>>>>> dma_alloc_coherent() and kzalloc(). Right?
>>>>>>>>>
>>>>>>>>> I don't think we need GFP_ATOMIC, the critical path will just free the
>>>>>>>>> memory.
>>>>>>>>
>>>>>>>> I tested these two functions, and kfree was safe.
>>>>>>>> However, dma_free_coherent raised BUG.
>>>>>>>> BUG: failure at
>>>>>>>> /mnt/host/source/src/third_party/kernel/v3.18/mm/vmalloc.c:1514/vunmap()!
>>>>>>>
>>>>>>> Just a general hint. Please try to evaluate on a recent kernel. It looks
>>>>>>> like as if you tried this on a v3.18 based one.
>>>>>>
>>>>>> This driver should be backward compatible to v3.18 for a MTK project.
>>>>>>
>>>>>>> Best regards,
>>>>>>> Matthias
>>>>>>
>>>>>> Thanks,
>>>>>> HS
>>>>>>
>>>>>>>> 1512 void vunmap(const void *addr)
>>>>>>>> 1513 {
>>>>>>>> 1514         BUG_ON(in_interrupt());		// <-- here
>>>>>>>> 1515         might_sleep();
>>>>>>>> 1516         if (addr)
>>>>>>>> 1517                 __vunmap(addr, 0);
>>>>>>>> 1518 }
>>>>>>>> 1519 EXPORT_SYMBOL(vunmap);
>>>>>>>>
>>>>>>>> Therefore, I plan to use kmalloc + dma_map_single instead of
>>>>>>>> dma_alloc_coherent, and dma_unmap_single + kfree instead of
>>>>>>>> dma_free_coherent.
>>>>>>>>
>>>>>>>> What do you think about the function replacement?
>>>>>>>>
>>>>>>>>>> If so, I can try to implement timeout by timer, and discuss with you
>>>>>>>>>> if I have further questions.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Sounds good :)
>>>>>>>>>
>>>>>>>>> Thanks,
>>>>>>>>> Matthias
>>>>>>>>
>>>>>>>> Thanks,
>>>>>>>> HS
>>>>>>>>
>>>>>>>>>>> Regrading the clock, wouldn't it be easier to handle the clock
>>>>>>>>>>> enable/disable depending on the state of task_busy_list? I suppose we
>>>>>>>>>>> can't as we would need to check the task_busy_list of all threads, right?
>>>>>>>>>>>
>>>>>>>>>>> Regards,
>>>>>>>>>>> Matthias
>>>>>>>>>>
>>>>>>>>>> Thanks,
>>>>>>>>>> HS
>>>
>>>
>
>


From tj at kernel.org  Wed Jun 22 08:21:26 2016
From: tj at kernel.org (Tejun Heo)
Date: Wed, 22 Jun 2016 11:21:26 -0400
Subject: [PATCH] PM / suspend: show workqueues busy name in suspend flow
In-Reply-To: <1466586509-32400-1-git-send-email-roger.lu@mediatek.com>
References: <1466586509-32400-1-git-send-email-roger.lu@mediatek.com>
Message-ID: <20160622152126.GV3262@mtj.duckdns.org>

Hello,

On Wed, Jun 22, 2016 at 05:08:29PM +0800, Roger Lu wrote:
> When suspend flow is aborted because of workqueues busy,
> show workqueues busy name for debug purpose.
> 
> Signed-off-by: Roger Lu <roger.lu at mediatek.com>
> ---
>  include/linux/workqueue.h |  1 +
>  kernel/power/process.c    |  3 +++
>  kernel/workqueue.c        | 35 +++++++++++++++++++++++++++++++++++
>  3 files changed, 39 insertions(+)
> 
> diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h
> index ca73c50..c9f9b24 100644
> --- a/include/linux/workqueue.h
> +++ b/include/linux/workqueue.h
> @@ -609,6 +609,7 @@ long work_on_cpu(int cpu, long (*fn)(void *), void
>  *arg);
>  #ifdef CONFIG_FREEZER
>  extern void freeze_workqueues_begin(void);
>  extern bool freeze_workqueues_busy(void);
> +extern void show_workqueues_busy(void);
>  extern void thaw_workqueues(void);
>  #endif /* CONFIG_FREEZER */
>  
> diff --git a/kernel/power/process.c b/kernel/power/process.c
> index df058be..b538881 100644
> --- a/kernel/power/process.c
> +++ b/kernel/power/process.c
> @@ -89,6 +89,9 @@ static int try_to_freeze_tasks(bool user_only)
>  		       elapsed_msecs / 1000, elapsed_msecs % 1000,
>  		       todo - wq_busy, wq_busy);
>  
> +		if (wq_busy)
> +			show_workqueues_busy();

I think it'd probably be better to call show_workqueue_state() instead
so that we can get a full picture of what's going on.

Thanks.

-- 
tejun


From ck.hu at mediatek.com  Wed Jun 22 23:03:49 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Thu, 23 Jun 2016 14:03:49 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
Message-ID: <1466661829.15112.26.camel@mtksdaap41>

Hi, HS:

On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> This patch is first version of Mediatek Command Queue(CMDQ) driver. The
> CMDQ is used to help read/write registers with critical time limitation,
> such as updating display configuration during the vblank. It controls
> Global Command Engine (GCE) hardware to achieve this requirement.
> Currently, CMDQ only supports display related hardwares, but we expect
> it can be extended to other hardwares for future requirements.
> 
> Signed-off-by: HS Liao <hs.liao at mediatek.com>
> Signed-off-by: CK Hu <ck.hu at mediatek.com>
> ---
>  drivers/soc/mediatek/Kconfig    |  10 +
>  drivers/soc/mediatek/Makefile   |   1 +
>  drivers/soc/mediatek/mtk-cmdq.c | 943 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/mediatek/cmdq.h     | 197 +++++++++
>  4 files changed, 1151 insertions(+)
>  create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
>  create mode 100644 include/soc/mediatek/cmdq.h

[...]

> +
> +/* events for CMDQ and display */
> +enum cmdq_event {
> +	/* Display start of frame(SOF) events */
> +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> +	/* Display end of frame(EOF) events */
> +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> +	/* Mutex end of frame(EOF) events */
> +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> +	/* Display underrun events */
> +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> +	/* Keep this at the end of HW events */
> +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> +};

The value of these symbol is just the GCE-HW-defined value. I think it's
not appropriate to expose HW-defined value to client. For another SoC
GCE HW, these definition may change.

One way to solve this problem is to translate symbol to value
internally. But these events looks like interrupt and the value may vary
by each SoC, to prevent driver modified frequently, it's better to place
the value definition in device tree. It may looks like:

mmsys: clock-controller at 14000000 {
	compatible = "mediatek,mt8173-mmsys";
	mediatek,gce = <&gce>;
	gce-events = <53 54>;
	gce-event-names = "MUTEX0_EOF","MUTEX1_EOF";
}

For cmdq driver, you just need modify interface from

int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)

to

int cmdq_rec_wfe(struct cmdq_rec *rec, u32 event)
int cmdq_rec_clear_event(struct cmdq_rec *rec, u32 event)

Regards,
CK




From hs.liao at mediatek.com  Thu Jun 23 00:54:40 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Thu, 23 Jun 2016 15:54:40 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466661829.15112.26.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466661829.15112.26.camel@mtksdaap41>
Message-ID: <1466668480.12124.15.camel@mtksdaap41>

Hi CK,

On Thu, 2016-06-23 at 14:03 +0800, CK Hu wrote:
> Hi, HS:
> 
> On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> [...]
> 
> > +
> > +/* events for CMDQ and display */
> > +enum cmdq_event {
> > +	/* Display start of frame(SOF) events */
> > +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> > +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> > +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> > +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> > +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> > +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> > +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> > +	/* Display end of frame(EOF) events */
> > +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> > +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> > +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> > +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> > +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> > +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> > +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> > +	/* Mutex end of frame(EOF) events */
> > +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> > +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> > +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> > +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> > +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> > +	/* Display underrun events */
> > +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> > +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> > +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> > +	/* Keep this at the end of HW events */
> > +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> > +};
> 
> The value of these symbol is just the GCE-HW-defined value. I think it's
> not appropriate to expose HW-defined value to client. For another SoC
> GCE HW, these definition may change.

Agree.

> One way to solve this problem is to translate symbol to value
> internally. But these events looks like interrupt and the value may vary
> by each SoC, to prevent driver modified frequently, it's better to place
> the value definition in device tree. It may looks like:
> 
> mmsys: clock-controller at 14000000 {
> 	compatible = "mediatek,mt8173-mmsys";
> 	mediatek,gce = <&gce>;
> 	gce-events = <53 54>;

However, client still needs to know the HW-defined value by using
this solution.

> 	gce-event-names = "MUTEX0_EOF","MUTEX1_EOF";
> }
> 
> For cmdq driver, you just need modify interface from
> 
> int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
> 
> to
> 
> int cmdq_rec_wfe(struct cmdq_rec *rec, u32 event)
> int cmdq_rec_clear_event(struct cmdq_rec *rec, u32 event)

I think an easy way for this issue is just removing HW-defined values
from include/soc/mediatek/cmdq.h (but keeping enum), and then mapping
abstract values into real values in driver.
The benefit of this solution is that we can keep interface and leave SoC
specific code into driver.
What do you think?

> Regards,
> CK

Thanks,
HS




From ck.hu at mediatek.com  Thu Jun 23 04:44:11 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Thu, 23 Jun 2016 19:44:11 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466668480.12124.15.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466661829.15112.26.camel@mtksdaap41>
 <1466668480.12124.15.camel@mtksdaap41>
Message-ID: <1466682251.12354.0.camel@mtksdaap41>

On Thu, 2016-06-23 at 15:54 +0800, Horng-Shyang Liao wrote:
> Hi CK,
> 
> On Thu, 2016-06-23 at 14:03 +0800, CK Hu wrote:
> > Hi, HS:
> > 
> > On Mon, 2016-05-30 at 11:19 +0800, HS Liao wrote:
> > [...]
> > 
> > > +
> > > +/* events for CMDQ and display */
> > > +enum cmdq_event {
> > > +	/* Display start of frame(SOF) events */
> > > +	CMDQ_EVENT_DISP_OVL0_SOF = 11,
> > > +	CMDQ_EVENT_DISP_OVL1_SOF = 12,
> > > +	CMDQ_EVENT_DISP_RDMA0_SOF = 13,
> > > +	CMDQ_EVENT_DISP_RDMA1_SOF = 14,
> > > +	CMDQ_EVENT_DISP_RDMA2_SOF = 15,
> > > +	CMDQ_EVENT_DISP_WDMA0_SOF = 16,
> > > +	CMDQ_EVENT_DISP_WDMA1_SOF = 17,
> > > +	/* Display end of frame(EOF) events */
> > > +	CMDQ_EVENT_DISP_OVL0_EOF = 39,
> > > +	CMDQ_EVENT_DISP_OVL1_EOF = 40,
> > > +	CMDQ_EVENT_DISP_RDMA0_EOF = 41,
> > > +	CMDQ_EVENT_DISP_RDMA1_EOF = 42,
> > > +	CMDQ_EVENT_DISP_RDMA2_EOF = 43,
> > > +	CMDQ_EVENT_DISP_WDMA0_EOF = 44,
> > > +	CMDQ_EVENT_DISP_WDMA1_EOF = 45,
> > > +	/* Mutex end of frame(EOF) events */
> > > +	CMDQ_EVENT_MUTEX0_STREAM_EOF = 53,
> > > +	CMDQ_EVENT_MUTEX1_STREAM_EOF = 54,
> > > +	CMDQ_EVENT_MUTEX2_STREAM_EOF = 55,
> > > +	CMDQ_EVENT_MUTEX3_STREAM_EOF = 56,
> > > +	CMDQ_EVENT_MUTEX4_STREAM_EOF = 57,
> > > +	/* Display underrun events */
> > > +	CMDQ_EVENT_DISP_RDMA0_UNDERRUN = 63,
> > > +	CMDQ_EVENT_DISP_RDMA1_UNDERRUN = 64,
> > > +	CMDQ_EVENT_DISP_RDMA2_UNDERRUN = 65,
> > > +	/* Keep this at the end of HW events */
> > > +	CMDQ_MAX_HW_EVENT_COUNT = 260,
> > > +};
> > 
> > The value of these symbol is just the GCE-HW-defined value. I think it's
> > not appropriate to expose HW-defined value to client. For another SoC
> > GCE HW, these definition may change.
> 
> Agree.
> 
> > One way to solve this problem is to translate symbol to value
> > internally. But these events looks like interrupt and the value may vary
> > by each SoC, to prevent driver modified frequently, it's better to place
> > the value definition in device tree. It may looks like:
> > 
> > mmsys: clock-controller at 14000000 {
> > 	compatible = "mediatek,mt8173-mmsys";
> > 	mediatek,gce = <&gce>;
> > 	gce-events = <53 54>;
> 
> However, client still needs to know the HW-defined value by using
> this solution.
> 
> > 	gce-event-names = "MUTEX0_EOF","MUTEX1_EOF";
> > }
> > 
> > For cmdq driver, you just need modify interface from
> > 
> > int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
> > int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
> > 
> > to
> > 
> > int cmdq_rec_wfe(struct cmdq_rec *rec, u32 event)
> > int cmdq_rec_clear_event(struct cmdq_rec *rec, u32 event)
> 
> I think an easy way for this issue is just removing HW-defined values
> from include/soc/mediatek/cmdq.h (but keeping enum), and then mapping
> abstract values into real values in driver.
> The benefit of this solution is that we can keep interface and leave SoC
> specific code into driver.
> What do you think?

Hi, HS:

OK, that's fine.

Regards,
CK

> 
> > Regards,
> > CK
> 
> Thanks,
> HS
> 
> 




From stefan.wahren at i2se.com  Thu Jun 23 11:07:13 2016
From: stefan.wahren at i2se.com (Stefan Wahren)
Date: Thu, 23 Jun 2016 20:07:13 +0200 (CEST)
Subject: [PATCH v3 0/2] nvmem: remove regmap dependency
In-Reply-To: <20160601175542.GA19241@kroah.com>
References: <1462214174-469-1-git-send-email-srinivas.kandagatla@linaro.org>
 <7eec3215-b455-6567-28a2-e2a860e1d3ec@i2se.com>
 <20160601175542.GA19241@kroah.com>
Message-ID: <541115749.18852.dd1915ce-f885-44a4-a743-86ca28f8b9fd.open-xchange@email.1und1.de>

Hi Greg,

> Greg Kroah-Hartman <gregkh at linuxfoundation.org> hat am 1. Juni 2016 um 19:55
> geschrieben:
> 
> 
> On Wed, Jun 01, 2016 at 10:27:49AM +0200, Stefan Wahren wrote:
> > Hi Greg,
> > 
> > Am 02.05.2016 um 20:36 schrieb Srinivas Kandagatla:
> > > Hi Greg, 
> > >
> > > This is v3 patchset for the leftover 2 patches for nvmem regmap
> > > removal series [1]. These patches are based on char-misc tree.
> > >
> > > nvmem uses regmap_raw_read/write apis to read/write data from providers,
> > > With recent patch 922a9f936e40 ("regmap: mmio: Convert to regmap_bus
> > > and fix accessor usage") nvmem providers based on regmap-mmio stopped
> > > working, as nvmem core was using raw accessors.
> > > This issue can be fixed temporarly by moving to other regmap apis,
> > > but we might hit same issue in future, and regmap looks like an
> > > overdo for nvmem. Moving to interfaces based on read/write callbacks
> > > from providers would be more robust.
> > >
> > > This patchset converts the nvmem core and nvmem provider drivers to
> > > use the new callbacks. Tested this patchset on qfprom and at24 drivers.
> > > Other driver are only compile tested, any testing on them would be great.
> > >
> > > Most of the patches have been applied to char-misc tree, these are the
> > > two patches which had some outstanding comments on mxs nvmemprovider,
> > > which are now fixed.
> > >
> > > Changes since v2:
> > >  - Fixed the mxs size and dt data pointer spotted by Stefan and Fabio
> > >
> > > Changes since v1:
> > >  - rebased mtk-efuse on top of char-misc
> > >  - addressed concerns raised by Stefan Wahren.
> > >
> > > [1]
> > > https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1130026.html
> > >
> > > Thanks,
> > > srini
> > >
> > > Srinivas Kandagatla (2):
> > >   nvmem: mtk-efuse: remove nvmem regmap dependency
> > >   nvmem: mxs-ocotp: remove nvmem regmap dependency
> > >
> > >  drivers/nvmem/Kconfig     |  1 -
> > >  drivers/nvmem/mtk-efuse.c | 47 ++++++++++++++++++---------
> > >  drivers/nvmem/mxs-ocotp.c | 83
> > > +++++++++++++----------------------------------
> > >  3 files changed, 54 insertions(+), 77 deletions(-)
> > >
> > 
> > any objections about this series or is it still in queue?
> 
> still in my queue...
> 

gently ping ...


From wens at csie.org  Fri Jun 24 00:13:20 2016
From: wens at csie.org (Chen-Yu Tsai)
Date: Fri, 24 Jun 2016 15:13:20 +0800
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <CAGb2v64Qk822h2Mqp1Moc1yV7uXSEQBGiurkyFLSZdJ6NOO3Vg@mail.gmail.com>

On Fri, Jun 17, 2016 at 5:27 AM, Daniel Lezcano
<daniel.lezcano at linaro.org> wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
>
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
>
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  drivers/clocksource/sun4i_timer.c         |  2 +-
>  drivers/clocksource/timer-sun5i.c         |  4 ++--

Acked-by: Chen-Yu Tsai <wens at csie.org>


From bibby.hsieh at mediatek.com  Fri Jun 24 00:27:18 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Fri, 24 Jun 2016 15:27:18 +0800
Subject: [GIT PULL] drm/mediatek: MT8173 gamma & dither support
Message-ID: <1466753238-17216-1-git-send-email-bibby.hsieh@mediatek.com>

Hi Dave,

Please consider merging this tag, which contains the v2 MT8173 gamma & dither function patches I sent on 2016-06-17, rebased onto v4.7-rc1. There have been no further comments.

Thanks
Bibby

The following changes since commit 1a695a905c18548062509178b98bc91e67510864:

  Linux 4.7-rc1 (2016-05-29 16:29:24 GMT)

are available in the git repository at:

  git at github.com:BibbyHsieh/linux4.7-rc1.git

for you to fetch changes up to dd0eb773bc125f5e6bca735d19c08500dc3730f9:

  drm/mediatek: Add gamma correction

-------------------------------------------------------------------------
This is MT8173 gamma & dither support PATCH v2, based on 4.7-rc1.

Changes since v1:
 -According to the suggestion from Daniel,
  we used the new atomic color management.
 -Applied gamma function at GAMMA engine (path 2).
 -Made dithering function support hardware mirroring well.
 -Removed the bpc variable from mtk_drm_crtc struct.
-------------------------------------------------------------------------

Bibby Hsieh (2):
  drm/mediatek: Add gamma correction
  drm/mediatek: set mt8173 dithering function

 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   26 ++++-
 drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |  147 ++++++++++++++++++++++++++-
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   16 ++-
 6 files changed, 184 insertions(+), 12 deletions(-)

-- 
1.7.9.5



From pmeerw at pmeerw.net  Fri Jun 24 01:51:19 2016
From: pmeerw at pmeerw.net (Peter Meerwald-Stadler)
Date: Fri, 24 Jun 2016 10:51:19 +0200 (CEST)
Subject: [PATCH 2/3] auxadc: Add Mediatek auxadc driver for mt2701.
In-Reply-To: <1466753830-4542-3-git-send-email-zhiyong.tao@mediatek.com>
References: <1466753830-4542-1-git-send-email-zhiyong.tao@mediatek.com>
 <1466753830-4542-3-git-send-email-zhiyong.tao@mediatek.com>
Message-ID: <alpine.DEB.2.02.1606241036400.10280@pmeerw.net>


> Add Mediatek auxadc driver in iio.
> It will register a device in iio and support iio.
> So thermal can read auxadc channel to sample data by iio device.
> It is tested successfully on mt2701 platform.
> Mt8173 platform is not tested. But the expectation is compatible.

some reference to iio would be nice in the subject line

comments below
 
> Signed-off-by: Zhiyong Tao <zhiyong.tao at mediatek.com>
> ---
>  drivers/iio/adc/Kconfig         |   11 ++
>  drivers/iio/adc/Makefile        |    1 +
>  drivers/iio/adc/mt65xx_auxadc.c |  269
>  +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 281 insertions(+)
>  create mode 100644 drivers/iio/adc/mt65xx_auxadc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 25378c5..7684849 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -315,6 +315,17 @@ config MEN_Z188_ADC
>  	  This driver can also be built as a module. If so, the module will be
>  	  called men_z188_adc.
>  
> +config MEDIATEK_MT65XX_AUXADC
> +	tristate "MediaTek AUXADC driver"

maybe some 'depends on' here so the driver is just visible for relevant 
configs? see below, MXR_LRADC, for an example

> +	help
> +	  Say yes here to enable support for MediaTek mt65xx AUXADC.
> +
> +	  The driver supports immediate mode operation to read from one of sixteen
> +	  channels (external or internal).
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called mt65xx_auxadc.
> +
>  config MXS_LRADC
>          tristate "Freescale i.MX23/i.MX28 LRADC"
>          depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 38638d4..bc62209 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -31,6 +31,7 @@ obj-$(CONFIG_MAX1363) += max1363.o
>  obj-$(CONFIG_MCP320X) += mcp320x.o
>  obj-$(CONFIG_MCP3422) += mcp3422.o
>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
> +obj-$(CONFIG_MEDIATEK_MT65XX_AUXADC) += mt65xx_auxadc.o
>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>  obj-$(CONFIG_NAU7802) += nau7802.o
>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
> diff --git a/drivers/iio/adc/mt65xx_auxadc.c
>  b/drivers/iio/adc/mt65xx_auxadc.c
> new file mode 100644
> index 0000000..b3c059c
> --- /dev/null
> +++ b/drivers/iio/adc/mt65xx_auxadc.c
> @@ -0,0 +1,269 @@
> +/*
> + * Copyright (c) 2016 MediaTek Inc.
> + * Author: Zhiyong Tao <zhiyong.tao at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/iopoll.h>
> +#include <linux/io.h>
> +#include <linux/iio/iio.h>
> +
> +/* Registers  define*/

delete one space before define, add a space after define; maybe 'Register 
definitions' or 'Register defines' would read smoother

> +#define MT65XX_AUXADC_CON0                    0x00
> +#define MT65XX_AUXADC_CON1                    0x04
> +#define MT65XX_AUXADC_CON2                    0x10
> +#define MT65XX_AUXADC_STA                     BIT(0)
> +
> +#define MT65XX_AUXADC_DAT0                    0x14
> +#define MT65XX_AUXADC_RDY0                    BIT(12)
> +
> +#define MT65XX_AUXADC_MISC                    0x94
> +#define MT65XX_AUXADC_PDN_EN                  BIT(14)
> +
> +#define MT65XX_AUXADC_DAT_MASK                0xfff
> +#define MT65XX_AUXADC_SLEEP_US                1000
> +#define MT65XX_AUXADC_TIMEOUT_US              10000
> +#define MT65XX_AUXADC_POWER_READY_MS          1
> +#define MT65XX_AUXADC_SAMPLE_READY_US         25
> +
> +struct mt65xx_auxadc_device {
> +	void __iomem *reg_base;
> +	struct clk *adc_clk;
> +	struct mutex lock; /*generic mutex for auxadc driver*/

comment needed?

> +	unsigned int power_ready_ms;
> +	unsigned int sample_ready_us;
> +};
> +
> +#define MT65xx_AUXADC_CHANNEL(idx) {				       \

XX uppercase

> +		.type = IIO_VOLTAGE,				       \
> +		.indexed = 1,					       \
> +		.channel = (idx),				       \
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	       \
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
> +					    BIT(IIO_CHAN_INFO_OFFSET), \
> +}
> +
> +static const struct iio_chan_spec mt65xx_auxadc_iio_channels[] = {
> +	MT65xx_AUXADC_CHANNEL(0),
> +	MT65xx_AUXADC_CHANNEL(1),
> +	MT65xx_AUXADC_CHANNEL(2),
> +	MT65xx_AUXADC_CHANNEL(3),
> +	MT65xx_AUXADC_CHANNEL(4),
> +	MT65xx_AUXADC_CHANNEL(5),
> +	MT65xx_AUXADC_CHANNEL(6),
> +	MT65xx_AUXADC_CHANNEL(7),
> +	MT65xx_AUXADC_CHANNEL(8),
> +	MT65xx_AUXADC_CHANNEL(9),
> +	MT65xx_AUXADC_CHANNEL(10),
> +	MT65xx_AUXADC_CHANNEL(11),
> +	MT65xx_AUXADC_CHANNEL(12),
> +	MT65xx_AUXADC_CHANNEL(13),
> +	MT65xx_AUXADC_CHANNEL(14),
> +	MT65xx_AUXADC_CHANNEL(15),
> +};
> +
> +static int mt65xx_auxadc_read(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan)
> +{
> +	u32 rawdata, val;
> +	void __iomem *reg_channel;
> +	struct mt65xx_auxadc_device *adc_dev = iio_priv(indio_dev);
> +
> +	reg_channel = adc_dev->reg_base + MT65XX_AUXADC_DAT0 +
> +		      chan->channel * 0x04;
> +
> +	mutex_lock(&adc_dev->lock);
> +
> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_CON1);
> +	val &= ~(1 << chan->channel);
> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_CON1);
> +
> +	/* read channel and make sure old ready bit ==0 */
> +	readl_poll_timeout(reg_channel, val,
> +			   ((val & MT65XX_AUXADC_RDY0) == 0),
> +			   MT65XX_AUXADC_SLEEP_US,
> +			   MT65XX_AUXADC_TIMEOUT_US);

this returns a code 0 or -ETIMEDOUT that probably should be checked

> +
> +	/* set bit  to trigger sample */

double space after bit

> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_CON1);
> +	val |= 1 << chan->channel;
> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_CON1);
> +
> +	/* we must delay here for hardware sample channel data */
> +	udelay(adc_dev->sample_ready_us);
> +
> +	/* check MTK_AUXADC_CON2 if auxadc is idle */
> +	readl_poll_timeout(adc_dev->reg_base + MT65XX_AUXADC_CON2, val,
> +			   ((val & MT65XX_AUXADC_STA) == 0),
> +			   MT65XX_AUXADC_SLEEP_US,
> +			   MT65XX_AUXADC_TIMEOUT_US);
> +
> +	/* read channel and make sure  ready bit ==1 */
> +	readl_poll_timeout(reg_channel, val,
> +			   ((val & MT65XX_AUXADC_RDY0) == 1),
> +			   MT65XX_AUXADC_SLEEP_US,
> +			   MT65XX_AUXADC_TIMEOUT_US);
> +
> +	/* read data */
> +	rawdata = readl(reg_channel) & MT65XX_AUXADC_DAT_MASK;
> +
> +	mutex_unlock(&adc_dev->lock);
> +
> +	return rawdata;
> +}
> +
> +static int mt65xx_auxadc_read_raw(struct iio_dev *indio_dev,
> +				  struct iio_chan_spec const *chan,
> +				  int *val,
> +				  int *val2,
> +				  long info)
> +{
> +	int ret;
> +
> +	switch (info) {
> +	case IIO_CHAN_INFO_RAW:
> +		*val = mt65xx_auxadc_read(indio_dev, chan);
> +		ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 1;

no point in returning 1 and 0 for SCALE and OFFSET, these are the 
default/assumed values if not given

> +		ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_OFFSET:
> +		*val = 0;
> +		ret = IIO_VAL_INT;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct iio_info mt65xx_auxadc_info = {
> +	.driver_module = THIS_MODULE,
> +	.read_raw = &mt65xx_auxadc_read_raw,
> +};
> +
> +static int mt65xx_auxadc_probe(struct platform_device *pdev)
> +{
> +	struct mt65xx_auxadc_device *adc_dev;
> +	unsigned long adc_clk_rate;
> +	struct resource *res;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +	u32 val;
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	adc_dev = iio_priv(indio_dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->info = &mt65xx_auxadc_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = mt65xx_auxadc_iio_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(mt65xx_auxadc_iio_channels);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(adc_dev->reg_base)) {
> +		dev_err(&pdev->dev, "failed to get auxadc base address.\n");
> +		return PTR_ERR(adc_dev->reg_base);
> +	}
> +
> +	adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
> +	if (IS_ERR(adc_dev->adc_clk)) {
> +		dev_err(&pdev->dev, "failed to get axuadc clock\n");

spelling: auxadc

> +		return PTR_ERR(adc_dev->adc_clk);
> +	}
> +
> +	ret = clk_prepare_enable(adc_dev->adc_clk);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to enable auxadc clock\n");
> +		return ret;
> +	}
> +
> +	adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
> +	if (!adc_clk_rate) {
> +		dev_err(&pdev->dev, "null clock rate!\n");
> +		clk_disable_unprepare(adc_dev->adc_clk);
> +		return -EINVAL;
> +	}
> +
> +	adc_dev->power_ready_ms = MT65XX_AUXADC_POWER_READY_MS;
> +	adc_dev->sample_ready_us = MT65XX_AUXADC_SAMPLE_READY_US;
> +
> +	mutex_init(&adc_dev->lock);
> +
> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_MISC);
> +	val |= MT65XX_AUXADC_PDN_EN;
> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_MISC);
> +	mdelay(adc_dev->power_ready_ms);
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to register iio device!\n");

power down on failure?
clk_disable_unprepare()?

> +		return ret;
> +	}
> +
> +	platform_set_drvdata(pdev, indio_dev);

I'd rather put this before iio_device_register() to avoid a potential race 
window

> +
> +	return 0;
> +}
> +
> +static int mt65xx_auxadc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +	struct mt65xx_auxadc_device *adc_dev = iio_priv(indio_dev);
> +	u32 val;
> +
> +	iio_device_unregister(indio_dev);
> +
> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_MISC);
> +	val &= ~MT65XX_AUXADC_PDN_EN;
> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_MISC);
> +
> +	clk_disable_unprepare(adc_dev->adc_clk);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id mt65xx_auxadc_of_match[] = {
> +	{ .compatible = "mediatek,mt2701-auxadc", },
> +	{ .compatible = "mediatek,mt8173-auxadc", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, mt65xx_auxadc_of_match);
> +
> +static struct platform_driver mt65xx_auxadc_driver = {
> +	.driver = {
> +		.name   = "mt65xx-auxadc",
> +		.of_match_table = mt65xx_auxadc_of_match,
> +	},
> +	.probe	= mt65xx_auxadc_probe,
> +	.remove	= mt65xx_auxadc_remove,
> +};
> +module_platform_driver(mt65xx_auxadc_driver);
> +
> +MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao at mediatek.com>");
> +MODULE_DESCRIPTION("MTK AUXADC Device Driver");
> +MODULE_LICENSE("GPL v2");
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)


From hs.liao at mediatek.com  Fri Jun 24 04:39:31 2016
From: hs.liao at mediatek.com (Horng-Shyang Liao)
Date: Fri, 24 Jun 2016 19:39:31 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466495171.8045.27.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466419311.27959.12.camel@mtksdaap41>
 <1466421720.1918.11.camel@mtksdaap41> <1466474580.6214.29.camel@mtksdaap41>
 <1466495171.8045.27.camel@mtksdaap41>
Message-ID: <1466768371.817.9.camel@mtksdaap41>

On Tue, 2016-06-21 at 15:46 +0800, Horng-Shyang Liao wrote:
> On Tue, 2016-06-21 at 10:03 +0800, CK Hu wrote:
> > On Mon, 2016-06-20 at 19:22 +0800, Horng-Shyang Liao wrote:
> > > On Mon, 2016-06-20 at 18:41 +0800, CK Hu wrote:
> > > > [Snip...]
> > > > 
> > > > > +
> > > > > +static int cmdq_eng_get_thread(u64 flag)
> > > > > +{
> > > > > +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> > > > > +		return CMDQ_THR_DISP_MAIN_IDX;
> > > > > +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> > > > > +		return CMDQ_THR_DISP_SUB_IDX;
> > > > > +	else
> > > > > +		return CMDQ_THR_DISP_MISC_IDX;
> > > > > +}
> > > > 
> > > > I think cmdq should not have knowledge of client. These statement show
> > > > that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
> > > > the 'session' to replace engine flag. For example, main display create
> > > > one cmdq_session and external display create another cmdq_session. For
> > > > client driver, every tasks created by main display is bound to main
> > > > display session. For cmdq driver, it should dynamically bind a session
> > > > to a HW thread, and then dispatch tasks of this session to this HW
> > > > thread. After HW thread run out of tasks of this session, detach this
> > > > session with this thread.
> > > > For the problem of remove wfe cmd, I think a session can have a property
> > > > of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
> > > > For other client, it's false.
> > > 
> > > Hi CK,
> > > 
> > > I think your suggestion is similar to CMDQ 'scenarios',
> > > which was removed from CMDQ v2.
> > > 
> > > Daniel suggests to use engine flags instead of scenarios.
> > > Quote from https://patchwork.kernel.org/patch/8068311/ .
> > > 'Instead of encoding policy (these arbitrary "scenarios"), perhaps the
> > >  cmdq driver should just provide these flag bits, and the display
> > >  subsystem can use them to build the "flag" parameter itself.
> > > 
> > >  After all, the exact configuration of mmsys components is somewhat
> > >  flexible.'
> > > 
> > > Therefore, it would be better to discuss with Daniel before we change
> > > it.
> > > 
> > > 
> > > Hi Daniel,
> > > 
> > > Do you think we should use scenarios or sessions instead of flags?
> > > 
> > > Thanks,
> > > HS
> > > 
> > 
> > Hi, HS:
> > 
> > 'session' is not similar to 'scenarios'.
> > 
> > In 'scenarios' mechanism, client bind a task with scenarios and send to
> > cmdq. Cmdq transfer scenarios to engine flag, and use engine flag to
> > dispatch task to HW thread.
> > 
> > 
> > In 'engine flag' mechanism, proposed by Daniel, client bind a task
> > directly with engine flag. Cmdq directly use engine flag to dispatch
> > task to HW thread without any translation.
> > 
> > But neither 'scenarios' mechanism nor 'engine flag' mechanism get rid of
> > engine flag, which make cmdq have knowledge of client.
> > 
> > In 'session' mechanism, there is no engine flag any more. Client bind
> > time-sequential tasks to the same session and tasks in different session
> > can execute parallelly. One thing cmdq need to know is to dispatch tasks
> > with the same session to the same HW thread, so cmdq does not have any
> > knowledge of client.
> > 
> > Daniel focus on reduce translating scenarios to engine flag. I think
> > 'session' mechanism does not conflict with his opinion because we does
> > not translate 'session' to engine flag. Therefore, I think 'session' is
> > the best of these three mechanism.
> > 
> > Regards,
> > CK
> 
> Hi CK,
> 
> 'Session' looks like a group of options for CMDQ.
> CMDQ driver can just follow the options to run its flow,
> and doesn't need to know its client(s).
> 
> We don't have many options now, but it has good flexibility to extend
> for future requirements.
> 
> I will add it in next version.
> 
> Thanks,
> HS

Hi CK,

I think session is very similar to mailbox channel.
We can treat session's parameters as channel's arguments in device tree.
Linking session to GCE thread is also just like linking channel to GCE
thread.
Because I will use mailbox framework in CMDQ v9, we can use mailbox
channel instead of session.
What do you think?

Thanks,
HS

> > > > Here is the sample code to create cmdq_rec with session.
> > > > 
> > > > merge_wfe_cmd = true;
> > > > cmdq_session_create(merge_wfe_cmd, &primary_display_session);
> > > > cmdq_rec_create(dev, primary_display_session, &rec);
> > > > 
> > > > 
> > > > Therefore, the below definition can be removed.
> > > > 
> > > > > +
> > > > > +enum cmdq_eng {
> > > > > +	CMDQ_ENG_DISP_AAL,
> > > > > +	CMDQ_ENG_DISP_COLOR0,
> > > > > +	CMDQ_ENG_DISP_COLOR1,
> > > > > +	CMDQ_ENG_DISP_DPI0,
> > > > > +	CMDQ_ENG_DISP_DSI0,
> > > > > +	CMDQ_ENG_DISP_DSI1,
> > > > > +	CMDQ_ENG_DISP_GAMMA,
> > > > > +	CMDQ_ENG_DISP_OD,
> > > > > +	CMDQ_ENG_DISP_OVL0,
> > > > > +	CMDQ_ENG_DISP_OVL1,
> > > > > +	CMDQ_ENG_DISP_PWM0,
> > > > > +	CMDQ_ENG_DISP_PWM1,
> > > > > +	CMDQ_ENG_DISP_RDMA0,
> > > > > +	CMDQ_ENG_DISP_RDMA1,
> > > > > +	CMDQ_ENG_DISP_RDMA2,
> > > > > +	CMDQ_ENG_DISP_UFOE,
> > > > > +	CMDQ_ENG_DISP_WDMA0,
> > > > > +	CMDQ_ENG_DISP_WDMA1,
> > > > > +	CMDQ_ENG_MAX,
> > > > > +};
> > > > > +
> > > > 
> > > > 
> > > > Regards,
> > > > CK




From carlo at caione.org  Fri Jun 24 13:12:52 2016
From: carlo at caione.org (Carlo Caione)
Date: Fri, 24 Jun 2016 22:12:52 +0200
Subject: [PATCH V2 63/63] clocksources: Switch back to the clksrc table
In-Reply-To: <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
References: <1466112442-31105-1-git-send-email-daniel.lezcano@linaro.org>
 <1466112442-31105-64-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <20160624201252.GA2382@localhost>

On 16/06/16 23:27, Daniel Lezcano wrote:
> All the clocksource drivers's init function are now converted to return
> an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
> clksrc-of table.
> 
> Let's convert back the names:
>  - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
>  - clksrc-of-ret              => clksrc-of
> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
> ---
>  drivers/clocksource/meson6_timer.c        |  2 +-

Acked-by: Carlo Caione <carlo at caione.org>


From gregkh at linuxfoundation.org  Sat Jun 25 07:43:48 2016
From: gregkh at linuxfoundation.org (Greg Kroah-Hartman)
Date: Sat, 25 Jun 2016 07:43:48 -0700
Subject: [PATCH v3 0/2] nvmem: remove regmap dependency
In-Reply-To: <541115749.18852.dd1915ce-f885-44a4-a743-86ca28f8b9fd.open-xchange@email.1und1.de>
References: <1462214174-469-1-git-send-email-srinivas.kandagatla@linaro.org>
 <7eec3215-b455-6567-28a2-e2a860e1d3ec@i2se.com>
 <20160601175542.GA19241@kroah.com>
 <541115749.18852.dd1915ce-f885-44a4-a743-86ca28f8b9fd.open-xchange@email.1und1.de>
Message-ID: <20160625144348.GA19352@kroah.com>

On Thu, Jun 23, 2016 at 08:07:13PM +0200, Stefan Wahren wrote:
> Hi Greg,
> 
> > Greg Kroah-Hartman <gregkh at linuxfoundation.org> hat am 1. Juni 2016 um 19:55
> > geschrieben:
> > 
> > 
> > On Wed, Jun 01, 2016 at 10:27:49AM +0200, Stefan Wahren wrote:
> > > Hi Greg,
> > > 
> > > Am 02.05.2016 um 20:36 schrieb Srinivas Kandagatla:
> > > > Hi Greg, 
> > > >
> > > > This is v3 patchset for the leftover 2 patches for nvmem regmap
> > > > removal series [1]. These patches are based on char-misc tree.
> > > >
> > > > nvmem uses regmap_raw_read/write apis to read/write data from providers,
> > > > With recent patch 922a9f936e40 ("regmap: mmio: Convert to regmap_bus
> > > > and fix accessor usage") nvmem providers based on regmap-mmio stopped
> > > > working, as nvmem core was using raw accessors.
> > > > This issue can be fixed temporarly by moving to other regmap apis,
> > > > but we might hit same issue in future, and regmap looks like an
> > > > overdo for nvmem. Moving to interfaces based on read/write callbacks
> > > > from providers would be more robust.
> > > >
> > > > This patchset converts the nvmem core and nvmem provider drivers to
> > > > use the new callbacks. Tested this patchset on qfprom and at24 drivers.
> > > > Other driver are only compile tested, any testing on them would be great.
> > > >
> > > > Most of the patches have been applied to char-misc tree, these are the
> > > > two patches which had some outstanding comments on mxs nvmemprovider,
> > > > which are now fixed.
> > > >
> > > > Changes since v2:
> > > >  - Fixed the mxs size and dt data pointer spotted by Stefan and Fabio
> > > >
> > > > Changes since v1:
> > > >  - rebased mtk-efuse on top of char-misc
> > > >  - addressed concerns raised by Stefan Wahren.
> > > >
> > > > [1]
> > > > https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg1130026.html
> > > >
> > > > Thanks,
> > > > srini
> > > >
> > > > Srinivas Kandagatla (2):
> > > >   nvmem: mtk-efuse: remove nvmem regmap dependency
> > > >   nvmem: mxs-ocotp: remove nvmem regmap dependency
> > > >
> > > >  drivers/nvmem/Kconfig     |  1 -
> > > >  drivers/nvmem/mtk-efuse.c | 47 ++++++++++++++++++---------
> > > >  drivers/nvmem/mxs-ocotp.c | 83
> > > > +++++++++++++----------------------------------
> > > >  3 files changed, 54 insertions(+), 77 deletions(-)
> > > >
> > > 
> > > any objections about this series or is it still in queue?
> > 
> > still in my queue...
> > 
> 
> gently ping ...

Sorry for the delay, now queued up.

greg k-h


From jic23 at kernel.org  Sun Jun 26 07:31:05 2016
From: jic23 at kernel.org (Jonathan Cameron)
Date: Sun, 26 Jun 2016 15:31:05 +0100
Subject: [PATCH 2/3] auxadc: Add Mediatek auxadc driver for mt2701.
In-Reply-To: <alpine.DEB.2.02.1606241036400.10280@pmeerw.net>
References: <1466753830-4542-1-git-send-email-zhiyong.tao@mediatek.com>
 <1466753830-4542-3-git-send-email-zhiyong.tao@mediatek.com>
 <alpine.DEB.2.02.1606241036400.10280@pmeerw.net>
Message-ID: <38e8d819-e4f7-1b30-f286-e9b37680fea1@kernel.org>

On 24/06/16 09:51, Peter Meerwald-Stadler wrote:
> 
>> Add Mediatek auxadc driver in iio.
>> It will register a device in iio and support iio.
>> So thermal can read auxadc channel to sample data by iio device.
>> It is tested successfully on mt2701 platform.
>> Mt8173 platform is not tested. But the expectation is compatible.
> 
> some reference to iio would be nice in the subject line
> 
> comments below
>  
>> Signed-off-by: Zhiyong Tao <zhiyong.tao at mediatek.com>
A few additions from me to add to Peter's already thorough review.
I'm being more fuss about naming than anything else.  Looks pretty
good otherwise.

Jonathan
>> ---
>>  drivers/iio/adc/Kconfig         |   11 ++
>>  drivers/iio/adc/Makefile        |    1 +
>>  drivers/iio/adc/mt65xx_auxadc.c |  269
The naming is 'interesting'.  I'd just name it after the first major part
to be supported.  Hardware manufacturers (possibly including mediatek) play
with series numbers all the time so wild cards in part names often get
more confusing than just having an actual part number and listing the others
in the Kconfig help as well.
>>  +++++++++++++++++++++++++++++++++++++++
>>  3 files changed, 281 insertions(+)
>>  create mode 100644 drivers/iio/adc/mt65xx_auxadc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 25378c5..7684849 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -315,6 +315,17 @@ config MEN_Z188_ADC
>>  	  This driver can also be built as a module. If so, the module will be
>>  	  called men_z188_adc.
>>  
>> +config MEDIATEK_MT65XX_AUXADC
>> +	tristate "MediaTek AUXADC driver"
> 
> maybe some 'depends on' here so the driver is just visible for relevant 
> configs? see below, MXR_LRADC, for an example
Make sure to include CONFIG_TEST as well though to get build coverage from
the autobuilders.
> 
>> +	help
>> +	  Say yes here to enable support for MediaTek mt65xx AUXADC.
>> +
>> +	  The driver supports immediate mode operation to read from one of sixteen
>> +	  channels (external or internal).
>> +
>> +	  This driver can also be built as a module. If so, the module will be
>> +	  called mt65xx_auxadc.
>> +
>>  config MXS_LRADC
>>          tristate "Freescale i.MX23/i.MX28 LRADC"
>>          depends on (ARCH_MXS || COMPILE_TEST) && HAS_IOMEM
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 38638d4..bc62209 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -31,6 +31,7 @@ obj-$(CONFIG_MAX1363) += max1363.o
>>  obj-$(CONFIG_MCP320X) += mcp320x.o
>>  obj-$(CONFIG_MCP3422) += mcp3422.o
>>  obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
>> +obj-$(CONFIG_MEDIATEK_MT65XX_AUXADC) += mt65xx_auxadc.o
>>  obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
>>  obj-$(CONFIG_NAU7802) += nau7802.o
>>  obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
>> diff --git a/drivers/iio/adc/mt65xx_auxadc.c
>>  b/drivers/iio/adc/mt65xx_auxadc.c
>> new file mode 100644
>> index 0000000..b3c059c
>> --- /dev/null
>> +++ b/drivers/iio/adc/mt65xx_auxadc.c
>> @@ -0,0 +1,269 @@
>> +/*
>> + * Copyright (c) 2016 MediaTek Inc.
>> + * Author: Zhiyong Tao <zhiyong.tao at mediatek.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/err.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/io.h>
>> +#include <linux/iio/iio.h>
>> +
>> +/* Registers  define*/
> 
> delete one space before define, add a space after define; maybe 'Register 
> definitions' or 'Register defines' would read smoother
> 
>> +#define MT65XX_AUXADC_CON0                    0x00
>> +#define MT65XX_AUXADC_CON1                    0x04
>> +#define MT65XX_AUXADC_CON2                    0x10
>> +#define MT65XX_AUXADC_STA                     BIT(0)
>> +
>> +#define MT65XX_AUXADC_DAT0                    0x14
>> +#define MT65XX_AUXADC_RDY0                    BIT(12)
>> +
>> +#define MT65XX_AUXADC_MISC                    0x94
>> +#define MT65XX_AUXADC_PDN_EN                  BIT(14)
>> +
>> +#define MT65XX_AUXADC_DAT_MASK                0xfff
>> +#define MT65XX_AUXADC_SLEEP_US                1000
>> +#define MT65XX_AUXADC_TIMEOUT_US              10000
>> +#define MT65XX_AUXADC_POWER_READY_MS          1
>> +#define MT65XX_AUXADC_SAMPLE_READY_US         25
>> +
>> +struct mt65xx_auxadc_device {
>> +	void __iomem *reg_base;
>> +	struct clk *adc_clk;
>> +	struct mutex lock; /*generic mutex for auxadc driver*/
> 
> comment needed?
> 
>> +	unsigned int power_ready_ms;
>> +	unsigned int sample_ready_us;
>> +};
>> +
>> +#define MT65xx_AUXADC_CHANNEL(idx) {				       \
> 
> XX uppercase
> 
>> +		.type = IIO_VOLTAGE,				       \
>> +		.indexed = 1,					       \
>> +		.channel = (idx),				       \
>> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	       \
>> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
>> +					    BIT(IIO_CHAN_INFO_OFFSET), \
>> +}
>> +
>> +static const struct iio_chan_spec mt65xx_auxadc_iio_channels[] = {
>> +	MT65xx_AUXADC_CHANNEL(0),
>> +	MT65xx_AUXADC_CHANNEL(1),
>> +	MT65xx_AUXADC_CHANNEL(2),
>> +	MT65xx_AUXADC_CHANNEL(3),
>> +	MT65xx_AUXADC_CHANNEL(4),
>> +	MT65xx_AUXADC_CHANNEL(5),
>> +	MT65xx_AUXADC_CHANNEL(6),
>> +	MT65xx_AUXADC_CHANNEL(7),
>> +	MT65xx_AUXADC_CHANNEL(8),
>> +	MT65xx_AUXADC_CHANNEL(9),
>> +	MT65xx_AUXADC_CHANNEL(10),
>> +	MT65xx_AUXADC_CHANNEL(11),
>> +	MT65xx_AUXADC_CHANNEL(12),
>> +	MT65xx_AUXADC_CHANNEL(13),
>> +	MT65xx_AUXADC_CHANNEL(14),
>> +	MT65xx_AUXADC_CHANNEL(15),
>> +};
>> +
>> +static int mt65xx_auxadc_read(struct iio_dev *indio_dev,
>> +			      struct iio_chan_spec const *chan)
>> +{
>> +	u32 rawdata, val;
>> +	void __iomem *reg_channel;
>> +	struct mt65xx_auxadc_device *adc_dev = iio_priv(indio_dev);
>> +
>> +	reg_channel = adc_dev->reg_base + MT65XX_AUXADC_DAT0 +
>> +		      chan->channel * 0x04;
>> +
>> +	mutex_lock(&adc_dev->lock);
>> +
>> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_CON1);
>> +	val &= ~(1 << chan->channel);
>> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_CON1);
>> +
>> +	/* read channel and make sure old ready bit ==0 */
>> +	readl_poll_timeout(reg_channel, val,
>> +			   ((val & MT65XX_AUXADC_RDY0) == 0),
>> +			   MT65XX_AUXADC_SLEEP_US,
>> +			   MT65XX_AUXADC_TIMEOUT_US);
> 
> this returns a code 0 or -ETIMEDOUT that probably should be checked
> 
>> +
>> +	/* set bit  to trigger sample */
> 
> double space after bit
> 
>> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_CON1);
>> +	val |= 1 << chan->channel;
>> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_CON1);
>> +
>> +	/* we must delay here for hardware sample channel data */
>> +	udelay(adc_dev->sample_ready_us);
>> +
>> +	/* check MTK_AUXADC_CON2 if auxadc is idle */
>> +	readl_poll_timeout(adc_dev->reg_base + MT65XX_AUXADC_CON2, val,
>> +			   ((val & MT65XX_AUXADC_STA) == 0),
>> +			   MT65XX_AUXADC_SLEEP_US,
>> +			   MT65XX_AUXADC_TIMEOUT_US);
>> +
>> +	/* read channel and make sure  ready bit ==1 */
space before 1 (nitpick but would conform to coding style if it were code ;)

>> +	readl_poll_timeout(reg_channel, val,
>> +			   ((val & MT65XX_AUXADC_RDY0) == 1),
>> +			   MT65XX_AUXADC_SLEEP_US,
>> +			   MT65XX_AUXADC_TIMEOUT_US);
>> +
>> +	/* read data */
>> +	rawdata = readl(reg_channel) & MT65XX_AUXADC_DAT_MASK;
>> +
>> +	mutex_unlock(&adc_dev->lock);
>> +
>> +	return rawdata;
>> +}
>> +
>> +static int mt65xx_auxadc_read_raw(struct iio_dev *indio_dev,
>> +				  struct iio_chan_spec const *chan,
>> +				  int *val,
>> +				  int *val2,
>> +				  long info)
>> +{
>> +	int ret;
>> +
>> +	switch (info) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		*val = mt65xx_auxadc_read(indio_dev, chan);
>> +		ret = IIO_VAL_INT;
>> +		break;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = 1;
> 
> no point in returning 1 and 0 for SCALE and OFFSET, these are the 
> default/assumed values if not given
If the device is really kicking out values in the base units for
it's type (millivolts) then have the channel type as _PROCESSED not
raw to indicate to userspace that it can be used directly without any
offsets or scales being applied.


> 
>> +		ret = IIO_VAL_INT;
>> +		break;
>> +	case IIO_CHAN_INFO_OFFSET:
>> +		*val = 0;
>> +		ret = IIO_VAL_INT;
>> +		break;
>> +	default:
>> +		ret = -EINVAL;
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_info mt65xx_auxadc_info = {
>> +	.driver_module = THIS_MODULE,
>> +	.read_raw = &mt65xx_auxadc_read_raw,
>> +};
>> +
>> +static int mt65xx_auxadc_probe(struct platform_device *pdev)
>> +{
>> +	struct mt65xx_auxadc_device *adc_dev;
>> +	unsigned long adc_clk_rate;
>> +	struct resource *res;
>> +	struct iio_dev *indio_dev;
>> +	int ret;
>> +	u32 val;
>> +
>> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	adc_dev = iio_priv(indio_dev);
>> +	indio_dev->dev.parent = &pdev->dev;
>> +	indio_dev->name = dev_name(&pdev->dev);
>> +	indio_dev->info = &mt65xx_auxadc_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +	indio_dev->channels = mt65xx_auxadc_iio_channels;
>> +	indio_dev->num_channels = ARRAY_SIZE(mt65xx_auxadc_iio_channels);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
>> +	if (IS_ERR(adc_dev->reg_base)) {
>> +		dev_err(&pdev->dev, "failed to get auxadc base address.\n");
>> +		return PTR_ERR(adc_dev->reg_base);
>> +	}
>> +
>> +	adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
>> +	if (IS_ERR(adc_dev->adc_clk)) {
>> +		dev_err(&pdev->dev, "failed to get axuadc clock\n");
> 
> spelling: auxadc
> 
>> +		return PTR_ERR(adc_dev->adc_clk);
>> +	}
>> +
>> +	ret = clk_prepare_enable(adc_dev->adc_clk);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to enable auxadc clock\n");
>> +		return ret;
>> +	}
>> +
>> +	adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
>> +	if (!adc_clk_rate) {
>> +		dev_err(&pdev->dev, "null clock rate!\n");
>> +		clk_disable_unprepare(adc_dev->adc_clk);
>> +		return -EINVAL;
>> +	}
>> +
>> +	adc_dev->power_ready_ms = MT65XX_AUXADC_POWER_READY_MS;
>> +	adc_dev->sample_ready_us = MT65XX_AUXADC_SAMPLE_READY_US;
>> +
>> +	mutex_init(&adc_dev->lock);
>> +
>> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_MISC);
>> +	val |= MT65XX_AUXADC_PDN_EN;
>> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_MISC);
>> +	mdelay(adc_dev->power_ready_ms);
>> +
>> +	ret = iio_device_register(indio_dev);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "failed to register iio device!\n");
> 
> power down on failure?
> clk_disable_unprepare()?
Absolutely.  Anything you have in remove (other than very first statement)
should be mirrored in the error paths of probe.
> 
>> +		return ret;
>> +	}
>> +
>> +	platform_set_drvdata(pdev, indio_dev);
> 
> I'd rather put this before iio_device_register() to avoid a potential race 
> window
Whilst good practice in general, I'm not sure there is a race here.
It's only used in remove, which inherently can't race with probe.
> 
>> +
>> +	return 0;
>> +}
>> +
>> +static int mt65xx_auxadc_remove(struct platform_device *pdev)
>> +{
>> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>> +	struct mt65xx_auxadc_device *adc_dev = iio_priv(indio_dev);
>> +	u32 val;
>> +
>> +	iio_device_unregister(indio_dev);
>> +
>> +	val = readl(adc_dev->reg_base + MT65XX_AUXADC_MISC);
>> +	val &= ~MT65XX_AUXADC_PDN_EN;
>> +	writel(val, adc_dev->reg_base + MT65XX_AUXADC_MISC);
>> +
>> +	clk_disable_unprepare(adc_dev->adc_clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct of_device_id mt65xx_auxadc_of_match[] = {
>> +	{ .compatible = "mediatek,mt2701-auxadc", },
>> +	{ .compatible = "mediatek,mt8173-auxadc", },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mt65xx_auxadc_of_match);
>> +
>> +static struct platform_driver mt65xx_auxadc_driver = {
>> +	.driver = {
>> +		.name   = "mt65xx-auxadc",
>> +		.of_match_table = mt65xx_auxadc_of_match,
>> +	},
>> +	.probe	= mt65xx_auxadc_probe,
>> +	.remove	= mt65xx_auxadc_remove,
>> +};
>> +module_platform_driver(mt65xx_auxadc_driver);
>> +
>> +MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao at mediatek.com>");
>> +MODULE_DESCRIPTION("MTK AUXADC Device Driver");
>> +MODULE_LICENSE("GPL v2");
>>
> 



From ck.hu at mediatek.com  Sun Jun 26 19:00:07 2016
From: ck.hu at mediatek.com (CK Hu)
Date: Mon, 27 Jun 2016 10:00:07 +0800
Subject: [PATCH v8 2/3] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1466768371.817.9.camel@mtksdaap41>
References: <1464578397-29743-1-git-send-email-hs.liao@mediatek.com>
 <1464578397-29743-3-git-send-email-hs.liao@mediatek.com>
 <1466419311.27959.12.camel@mtksdaap41>
 <1466421720.1918.11.camel@mtksdaap41> <1466474580.6214.29.camel@mtksdaap41>
 <1466495171.8045.27.camel@mtksdaap41> <1466768371.817.9.camel@mtksdaap41>
Message-ID: <1466992807.9692.2.camel@mtksdaap41>

On Fri, 2016-06-24 at 19:39 +0800, Horng-Shyang Liao wrote:
> On Tue, 2016-06-21 at 15:46 +0800, Horng-Shyang Liao wrote:
> > On Tue, 2016-06-21 at 10:03 +0800, CK Hu wrote:
> > > On Mon, 2016-06-20 at 19:22 +0800, Horng-Shyang Liao wrote:
> > > > On Mon, 2016-06-20 at 18:41 +0800, CK Hu wrote:
> > > > > [Snip...]
> > > > > 
> > > > > > +
> > > > > > +static int cmdq_eng_get_thread(u64 flag)
> > > > > > +{
> > > > > > +	if (flag & BIT_ULL(CMDQ_ENG_DISP_DSI0))
> > > > > > +		return CMDQ_THR_DISP_MAIN_IDX;
> > > > > > +	else if (flag & BIT_ULL(CMDQ_ENG_DISP_DPI0))
> > > > > > +		return CMDQ_THR_DISP_SUB_IDX;
> > > > > > +	else
> > > > > > +		return CMDQ_THR_DISP_MISC_IDX;
> > > > > > +}
> > > > > 
> > > > > I think cmdq should not have knowledge of client. These statement show
> > > > > that cmdq know that DSI0-tasks is different with DPI0-tasks. I propose
> > > > > the 'session' to replace engine flag. For example, main display create
> > > > > one cmdq_session and external display create another cmdq_session. For
> > > > > client driver, every tasks created by main display is bound to main
> > > > > display session. For cmdq driver, it should dynamically bind a session
> > > > > to a HW thread, and then dispatch tasks of this session to this HW
> > > > > thread. After HW thread run out of tasks of this session, detach this
> > > > > session with this thread.
> > > > > For the problem of remove wfe cmd, I think a session can have a property
> > > > > of merge_wfe_cmd. For display session, session->merge_wfe_cmd = true.
> > > > > For other client, it's false.
> > > > 
> > > > Hi CK,
> > > > 
> > > > I think your suggestion is similar to CMDQ 'scenarios',
> > > > which was removed from CMDQ v2.
> > > > 
> > > > Daniel suggests to use engine flags instead of scenarios.
> > > > Quote from https://patchwork.kernel.org/patch/8068311/ .
> > > > 'Instead of encoding policy (these arbitrary "scenarios"), perhaps the
> > > >  cmdq driver should just provide these flag bits, and the display
> > > >  subsystem can use them to build the "flag" parameter itself.
> > > > 
> > > >  After all, the exact configuration of mmsys components is somewhat
> > > >  flexible.'
> > > > 
> > > > Therefore, it would be better to discuss with Daniel before we change
> > > > it.
> > > > 
> > > > 
> > > > Hi Daniel,
> > > > 
> > > > Do you think we should use scenarios or sessions instead of flags?
> > > > 
> > > > Thanks,
> > > > HS
> > > > 
> > > 
> > > Hi, HS:
> > > 
> > > 'session' is not similar to 'scenarios'.
> > > 
> > > In 'scenarios' mechanism, client bind a task with scenarios and send to
> > > cmdq. Cmdq transfer scenarios to engine flag, and use engine flag to
> > > dispatch task to HW thread.
> > > 
> > > 
> > > In 'engine flag' mechanism, proposed by Daniel, client bind a task
> > > directly with engine flag. Cmdq directly use engine flag to dispatch
> > > task to HW thread without any translation.
> > > 
> > > But neither 'scenarios' mechanism nor 'engine flag' mechanism get rid of
> > > engine flag, which make cmdq have knowledge of client.
> > > 
> > > In 'session' mechanism, there is no engine flag any more. Client bind
> > > time-sequential tasks to the same session and tasks in different session
> > > can execute parallelly. One thing cmdq need to know is to dispatch tasks
> > > with the same session to the same HW thread, so cmdq does not have any
> > > knowledge of client.
> > > 
> > > Daniel focus on reduce translating scenarios to engine flag. I think
> > > 'session' mechanism does not conflict with his opinion because we does
> > > not translate 'session' to engine flag. Therefore, I think 'session' is
> > > the best of these three mechanism.
> > > 
> > > Regards,
> > > CK
> > 
> > Hi CK,
> > 
> > 'Session' looks like a group of options for CMDQ.
> > CMDQ driver can just follow the options to run its flow,
> > and doesn't need to know its client(s).
> > 
> > We don't have many options now, but it has good flexibility to extend
> > for future requirements.
> > 
> > I will add it in next version.
> > 
> > Thanks,
> > HS
> 
> Hi CK,
> 
> I think session is very similar to mailbox channel.
> We can treat session's parameters as channel's arguments in device tree.
> Linking session to GCE thread is also just like linking channel to GCE
> thread.
> Because I will use mailbox framework in CMDQ v9, we can use mailbox
> channel instead of session.
> What do you think?
> 
> Thanks,
> HS
> 

Hi, HS:

I'm not familiar with mailbox, but this sounds good to me.

Regards,
CK

> > > > > Here is the sample code to create cmdq_rec with session.
> > > > > 
> > > > > merge_wfe_cmd = true;
> > > > > cmdq_session_create(merge_wfe_cmd, &primary_display_session);
> > > > > cmdq_rec_create(dev, primary_display_session, &rec);
> > > > > 
> > > > > 
> > > > > Therefore, the below definition can be removed.
> > > > > 
> > > > > > +
> > > > > > +enum cmdq_eng {
> > > > > > +	CMDQ_ENG_DISP_AAL,
> > > > > > +	CMDQ_ENG_DISP_COLOR0,
> > > > > > +	CMDQ_ENG_DISP_COLOR1,
> > > > > > +	CMDQ_ENG_DISP_DPI0,
> > > > > > +	CMDQ_ENG_DISP_DSI0,
> > > > > > +	CMDQ_ENG_DISP_DSI1,
> > > > > > +	CMDQ_ENG_DISP_GAMMA,
> > > > > > +	CMDQ_ENG_DISP_OD,
> > > > > > +	CMDQ_ENG_DISP_OVL0,
> > > > > > +	CMDQ_ENG_DISP_OVL1,
> > > > > > +	CMDQ_ENG_DISP_PWM0,
> > > > > > +	CMDQ_ENG_DISP_PWM1,
> > > > > > +	CMDQ_ENG_DISP_RDMA0,
> > > > > > +	CMDQ_ENG_DISP_RDMA1,
> > > > > > +	CMDQ_ENG_DISP_RDMA2,
> > > > > > +	CMDQ_ENG_DISP_UFOE,
> > > > > > +	CMDQ_ENG_DISP_WDMA0,
> > > > > > +	CMDQ_ENG_DISP_WDMA1,
> > > > > > +	CMDQ_ENG_MAX,
> > > > > > +};
> > > > > > +
> > > > > 
> > > > > 
> > > > > Regards,
> > > > > CK
> 
> 




From arnd at arndb.de  Mon Jun 27 02:22:37 2016
From: arnd at arndb.de (Arnd Bergmann)
Date: Mon, 27 Jun 2016 11:22:37 +0200
Subject: [PATCH] drm/mediatek: add ARM_SMCCC dependency
Message-ID: <20160627092416.1463857-1-arnd@arndb.de>

ARM SMCCC is only set for ARMv7 and ARMv8 CPUs, but we currently
allow the driver to be build for older architecture levels as
well, which results in a link failure:

drivers/gpu/built-in.o: In function `mtk_hdmi_hw_make_reg_writable':
:(.text+0x1e737c): undefined reference to `arm_smccc_smc'

This adds a Kconfig dependency. The patch applies on my two
previous fixes that are not yet applied, so please apply all
three to get randconfig builds to work correctly.

Signed-off-by: Arnd Bergmann <arnd at arndb.de>
Fixes: 8f83f26891e1 ("drm/mediatek: Add HDMI support")
---
 drivers/gpu/drm/mediatek/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 96ebf8bb6024..294de4549922 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -3,6 +3,7 @@ config DRM_MEDIATEK
 	depends on DRM
 	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
 	depends on COMMON_CLK
+	depends on HAVE_ARM_SMCCC
 	depends on OF
 	select DRM_GEM_CMA_HELPER
 	select DRM_KMS_HELPER
-- 
2.9.0



From matthias.bgg at gmail.com  Mon Jun 27 02:30:54 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 27 Jun 2016 11:30:54 +0200
Subject: [PATCH] drm/mediatek: add ARM_SMCCC dependency
In-Reply-To: <20160627092416.1463857-1-arnd@arndb.de>
References: <20160627092416.1463857-1-arnd@arndb.de>
Message-ID: <8177d497-69bb-db97-5dec-98417dad012a@gmail.com>



On 06/27/2016 11:22 AM, Arnd Bergmann wrote:
> ARM SMCCC is only set for ARMv7 and ARMv8 CPUs, but we currently
> allow the driver to be build for older architecture levels as
> well, which results in a link failure:
>
> drivers/gpu/built-in.o: In function `mtk_hdmi_hw_make_reg_writable':
> :(.text+0x1e737c): undefined reference to `arm_smccc_smc'
>
> This adds a Kconfig dependency. The patch applies on my two
> previous fixes that are not yet applied, so please apply all
> three to get randconfig builds to work correctly.
>
> Signed-off-by: Arnd Bergmann <arnd at arndb.de>
> Fixes: 8f83f26891e1 ("drm/mediatek: Add HDMI support")
> ---
>  drivers/gpu/drm/mediatek/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
>

Reviewed-by: Matthias Brugger <matthias.bgg at gmail.com>

> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 96ebf8bb6024..294de4549922 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -3,6 +3,7 @@ config DRM_MEDIATEK
>  	depends on DRM
>  	depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST)
>  	depends on COMMON_CLK
> +	depends on HAVE_ARM_SMCCC
>  	depends on OF
>  	select DRM_GEM_CMA_HELPER
>  	select DRM_KMS_HELPER
>


From matthias.bgg at gmail.com  Mon Jun 27 03:20:10 2016
From: matthias.bgg at gmail.com (Matthias Brugger)
Date: Mon, 27 Jun 2016 12:20:10 +0200
Subject: [GIT PULL] drm/mediatek: MT8173 gamma & dither support
In-Reply-To: <1466753238-17216-1-git-send-email-bibby.hsieh@mediatek.com>
References: <1466753238-17216-1-git-send-email-bibby.hsieh@mediatek.com>
Message-ID: <558c9efc-d197-a6c3-cf7e-f9fcac60ca64@gmail.com>



On 06/24/2016 09:27 AM, Bibby Hsieh wrote:
> Hi Dave,
>
> Please consider merging this tag, which contains the v2 MT8173 gamma & dither function patches I sent on 2016-06-17, rebased onto v4.7-rc1. There have been no further comments.
>
> Thanks
> Bibby
>
> The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
>
>   Linux 4.7-rc1 (2016-05-29 16:29:24 GMT)
>
> are available in the git repository at:
>
>   git at github.com:BibbyHsieh/linux4.7-rc1.git
>
> for you to fetch changes up to dd0eb773bc125f5e6bca735d19c08500dc3730f9:
>
>   drm/mediatek: Add gamma correction
>
> -------------------------------------------------------------------------

As far as I can see, your branch has 3 patches on top from Eddie. It 
seems to me as if you didn't send your patches to the mailinglist before?
Anyway this branch does not fulfill the rules to get merged into the 
linux kernel.

Why do you send a pull request? The normal process is to send the 
patches via email and if the maintainer wants you can send a pull 
request once the patches are ready to be merged.

Regards,
Matthias

> This is MT8173 gamma & dither support PATCH v2, based on 4.7-rc1.
>
> Changes since v1:
>  -According to the suggestion from Daniel,
>   we used the new atomic color management.
>  -Applied gamma function at GAMMA engine (path 2).
>  -Made dithering function support hardware mirroring well.
>  -Removed the bpc variable from mtk_drm_crtc struct.
> -------------------------------------------------------------------------
>
> Bibby Hsieh (2):
>   drm/mediatek: Add gamma correction
>   drm/mediatek: set mt8173 dithering function
>
>  drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
>  drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   26 ++++-
>  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |  147 ++++++++++++++++++++++++++-
>  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   16 ++-
>  6 files changed, 184 insertions(+), 12 deletions(-)
>


From bibby.hsieh at mediatek.com  Mon Jun 27 04:29:30 2016
From: bibby.hsieh at mediatek.com (Bibby Hsieh)
Date: Mon, 27 Jun 2016 19:29:30 +0800
Subject: [GIT PULL] drm/mediatek: MT8173 gamma & dither support
In-Reply-To: <558c9efc-d197-a6c3-cf7e-f9fcac60ca64@gmail.com>
References: <1466753238-17216-1-git-send-email-bibby.hsieh@mediatek.com>
 <558c9efc-d197-a6c3-cf7e-f9fcac60ca64@gmail.com>
Message-ID: <1467026970.23883.16.camel@mtksdaap41>

On Mon, 2016-06-27 at 12:20 +0200, Matthias Brugger wrote:
> 
> On 06/24/2016 09:27 AM, Bibby Hsieh wrote:
> > Hi Dave,
> >
> > Please consider merging this tag, which contains the v2 MT8173 gamma & dither function patches I sent on 2016-06-17, rebased onto v4.7-rc1. There have been no further comments.
> >
> > Thanks
> > Bibby
> >
> > The following changes since commit 1a695a905c18548062509178b98bc91e67510864:
> >
> >   Linux 4.7-rc1 (2016-05-29 16:29:24 GMT)
> >
> > are available in the git repository at:
> >
> >   git at github.com:BibbyHsieh/linux4.7-rc1.git
> >
> > for you to fetch changes up to dd0eb773bc125f5e6bca735d19c08500dc3730f9:
> >
> >   drm/mediatek: Add gamma correction
> >
> > -------------------------------------------------------------------------
> 
> As far as I can see, your branch has 3 patches on top from Eddie. It 
> seems to me as if you didn't send your patches to the mailinglist before?
> Anyway this branch does not fulfill the rules to get merged into the 
> linux kernel.
> 
> Why do you send a pull request? The normal process is to send the 
> patches via email and if the maintainer wants you can send a pull 
> request once the patches are ready to be merged.
> 
> Regards,
> Matthias
> 

I sent v1 and v2 on 2016-06-14 and 06-17 respectively [0-8].
After I made some modifications according to Daniel Vetter's comments,
there had been no further comments and I sent the pull request as the
other sub-sys.

I'm sorry for my mistake, I will re-arrange the tree for upstream.
Next time, I will check with maintainer by email first, and sent the
pull request.

Thanks.
Bibby 
 
[0]https://patchwork.kernel.org/patch/9174941/
[1]https://patchwork.kernel.org/patch/9174855/
[2]https://patchwork.kernel.org/patch/9182361/
[3]https://patchwork.kernel.org/patch/9182367/
[4]https://lists.freedesktop.org/archives/dri-devel/2016-June/111178.html
[5]https://lists.freedesktop.org/archives/dri-devel/2016-June/111177.html
[6]https://lists.freedesktop.org/archives/dri-devel/2016-June/111176.html
[7]https://lists.freedesktop.org/archives/dri-devel/2016-June/110781.html
[8]https://lists.freedesktop.org/archives/dri-devel/2016-June/110782.html
[9]https://lists.freedesktop.org/archives/dri-devel/2016-June/110784.html

> > This is MT8173 gamma & dither support PATCH v2, based on 4.7-rc1.
> >
> > Changes since v1:
> >  -According to the suggestion from Daniel,
> >   we used the new atomic color management.
> >  -Applied gamma function at GAMMA engine (path 2).
> >  -Made dithering function support hardware mirroring well.
> >  -Removed the bpc variable from mtk_drm_crtc struct.
> > -------------------------------------------------------------------------
> >
> > Bibby Hsieh (2):
> >   drm/mediatek: Add gamma correction
> >   drm/mediatek: set mt8173 dithering function
> >
> >  drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |    3 +-
> >  drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |    3 +-
> >  drivers/gpu/drm/mediatek/mtk_drm_crtc.c     |   26 ++++-
> >  drivers/gpu/drm/mediatek/mtk_drm_crtc.h     |    1 +
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |  147 ++++++++++++++++++++++++++-
> >  drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |   16 ++-
> >  6 files changed, 184 insertions(+), 12 deletions(-)
> >




From mars.cheng at mediatek.com  Mon Jun 27 22:15:54 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 28 Jun 2016 13:15:54 +0800
Subject: [PATCH v2 0/2] Add mt6755 basic chip support
Message-ID: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>

This patch adds basic support for Mediatek's new 8-core chip, mt6755.
Based on 4.7-rc1

Changes in v2:
1. use evb as project suffix
2. add "disable" property for uart dts nodes
3. only alias uart0 as serial0, since we don't enable uart1

Mars Cheng (2):
  Document: DT: Add bindings for mediatek MT6755 SoC Platform
  arm64: dts: mediatek: add mt6755 support

 Documentation/devicetree/bindings/arm/mediatek.txt |    4 +
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 arch/arm64/boot/dts/mediatek/Makefile              |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-evb.dts        |   38 +++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi           |  145 ++++++++++++++++++++
 6 files changed, 190 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-evb.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi


From mars.cheng at mediatek.com  Mon Jun 27 22:15:55 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 28 Jun 2016 13:15:55 +0800
Subject: [PATCH v2 1/2] Document: DT: Add bindings for mediatek MT6755 SoC
 Platform
In-Reply-To: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
References: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1467090956-31757-2-git-send-email-mars.cheng@mediatek.com>

This adds DT binding documentation for Mediatek MT6755.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
---
 Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 3 files changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/mediatek.txt b/Documentation/devicetree/bindings/arm/mediatek.txt
index d9c2a37..c860b24 100644
--- a/Documentation/devicetree/bindings/arm/mediatek.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek.txt
@@ -10,6 +10,7 @@ compatible: Must contain one of
    "mediatek,mt6580"
    "mediatek,mt6589"
    "mediatek,mt6592"
+   "mediatek,mt6755"
    "mediatek,mt6795"
    "mediatek,mt7623"
    "mediatek,mt8127"
@@ -31,6 +32,9 @@ Supported boards:
 - Evaluation board for MT6592:
     Required root node properties:
       - compatible = "mediatek,mt6592-evb", "mediatek,mt6592";
+- Evaluation phone for MT6755(Helio P10):
+    Required root node properties:
+      - compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
 - Evaluation board for MT6795(Helio X10):
     Required root node properties:
       - compatible = "mediatek,mt6795-evb", "mediatek,mt6795";
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
index 8cf564d..9d1d72c 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
@@ -9,6 +9,7 @@ Required properties:
 	"mediatek,mt8135-sysirq"
 	"mediatek,mt8127-sysirq"
 	"mediatek,mt6795-sysirq"
+	"mediatek,mt6755-sysirq"
 	"mediatek,mt6592-sysirq"
 	"mediatek,mt6589-sysirq"
 	"mediatek,mt6582-sysirq"
diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt
index e99e10a..0015c72 100644
--- a/Documentation/devicetree/bindings/serial/mtk-uart.txt
+++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt
@@ -6,6 +6,7 @@ Required properties:
   * "mediatek,mt6580-uart" for MT6580 compatible UARTS
   * "mediatek,mt6582-uart" for MT6582 compatible UARTS
   * "mediatek,mt6589-uart" for MT6589 compatible UARTS
+  * "mediatek,mt6755-uart" for MT6755 compatible UARTS
   * "mediatek,mt6795-uart" for MT6795 compatible UARTS
   * "mediatek,mt7623-uart" for MT7623 compatible UARTS
   * "mediatek,mt8127-uart" for MT8127 compatible UARTS
-- 
1.7.9.5



From mars.cheng at mediatek.com  Mon Jun 27 22:15:56 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Tue, 28 Jun 2016 13:15:56 +0800
Subject: [PATCH v2 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
References: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1467090956-31757-3-git-send-email-mars.cheng@mediatek.com>

This adds basic chip support for MT6755 SoC.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/Makefile       |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-evb.dts |   38 +++++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi    |  145 +++++++++++++++++++++++++++
 3 files changed, 184 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-evb.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi

diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
index e0a4bff..9fbfd32 100644
--- a/arch/arm64/boot/dts/mediatek/Makefile
+++ b/arch/arm64/boot/dts/mediatek/Makefile
@@ -1,3 +1,4 @@
+dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-evb.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
 
diff --git a/arch/arm64/boot/dts/mediatek/mt6755-evb.dts b/arch/arm64/boot/dts/mediatek/mt6755-evb.dts
new file mode 100644
index 0000000..c568d49
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755-evb.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+#include "mt6755.dtsi"
+
+/ {
+	model = "MediaTek MT6755 EVB";
+	compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	memory at 40000000 {
+		device_type = "memory";
+		reg = <0 0x40000000 0 0x1e800000>;
+	};
+
+	chosen {
+		stdout-path = "serial0:921600n8";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
new file mode 100644
index 0000000..01ba776
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+	compatible = "mediatek,mt6755";
+	interrupt-parent = <&sysirq>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	psci {
+		compatible = "arm,psci-0.2";
+		method = "smc";
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x000>;
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x001>;
+		};
+
+		cpu2: cpu at 2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x002>;
+		};
+
+		cpu3: cpu at 3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x003>;
+		};
+
+		cpu4: cpu at 100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x100>;
+		};
+
+		cpu5: cpu at 101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x101>;
+		};
+
+		cpu6: cpu at 102 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x102>;
+		};
+
+		cpu7: cpu at 103 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x103>;
+		};
+	};
+
+	uart_clk: dummy26m {
+		compatible = "fixed-clock";
+		clock-frequency = <26000000>;
+		#clock-cells = <0>;
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupt-parent = <&gic>;
+		interrupts = <GIC_PPI 13
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
+	};
+
+	sysirq: intpol-controller at 10200620 {
+		compatible = "mediatek,mt6755-sysirq",
+			     "mediatek,mt6577-sysirq";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		reg = <0 0x10200620 0 0x20>;
+	};
+
+	gic: interrupt-controller at 10231000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		interrupt-controller;
+		reg = <0 0x10231000 0 0x1000>,
+		      <0 0x10232000 0 0x2000>,
+		      <0 0x10234000 0 0x2000>,
+		      <0 0x10236000 0 0x2000>;
+	};
+
+	uart0: serial at 11002000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11002000 0 0x400>;
+		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+		status = "disabled";
+	};
+
+	uart1: serial at 11003000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11003000 0 0x400>;
+		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+		status = "disabled";
+	};
+};
-- 
1.7.9.5



From daniel.lezcano at linaro.org  Tue Jun 28 03:30:33 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Tue, 28 Jun 2016 12:30:33 +0200
Subject: [PATCH 14/92] clocksource/drivers/mtk_timer: Convert init function to
 return error
In-Reply-To: <1467109911-11060-1-git-send-email-daniel.lezcano@linaro.org>
References: <577251A4.7030508@linaro.org>
 <1467109911-11060-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467109911-11060-14-git-send-email-daniel.lezcano@linaro.org>

The init functions do not return any error. They behave as the following:

 - panic, thus leading to a kernel crash while another timer may work and
   make the system boot up correctly

 or

 - print an error and let the caller unaware if the state of the system

Change that by converting the init functions to return an error conforming
to the CLOCKSOURCE_OF_RET prototype.

Proper error handling (rollback, errno value) will be changed later case
by case, thus this change just return back an error or success in the init
function.

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>
Acked-by: Matthias Brugger <matthias.bgg at gmail.com>
---
 drivers/clocksource/mtk_timer.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 7e583f8..432a2c0 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -181,7 +181,7 @@ static void mtk_timer_enable_irq(struct mtk_clock_event_device *evt, u8 timer)
 			evt->gpt_base + GPT_IRQ_EN_REG);
 }
 
-static void __init mtk_timer_init(struct device_node *node)
+static int __init mtk_timer_init(struct device_node *node)
 {
 	struct mtk_clock_event_device *evt;
 	struct resource res;
@@ -190,7 +190,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	evt = kzalloc(sizeof(*evt), GFP_KERNEL);
 	if (!evt)
-		return;
+		return -ENOMEM;
 
 	evt->dev.name = "mtk_tick";
 	evt->dev.rating = 300;
@@ -248,7 +248,7 @@ static void __init mtk_timer_init(struct device_node *node)
 
 	mtk_timer_enable_irq(evt, GPT_CLK_EVT);
 
-	return;
+	return 0;
 
 err_clk_disable:
 	clk_disable_unprepare(clk);
@@ -262,5 +262,7 @@ err_mem:
 	release_mem_region(res.start, resource_size(&res));
 err_kzalloc:
 	kfree(evt);
+
+	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
-- 
1.9.1



From daniel.lezcano at linaro.org  Tue Jun 28 03:31:31 2016
From: daniel.lezcano at linaro.org (Daniel Lezcano)
Date: Tue, 28 Jun 2016 12:31:31 +0200
Subject: [PATCH 72/92] clocksources: Switch back to the clksrc table
In-Reply-To: <1467109911-11060-1-git-send-email-daniel.lezcano@linaro.org>
References: <577251A4.7030508@linaro.org>
 <1467109911-11060-1-git-send-email-daniel.lezcano@linaro.org>
Message-ID: <1467109911-11060-72-git-send-email-daniel.lezcano@linaro.org>

All the clocksource drivers's init function are now converted to return
an error code. CLOCKSOURCE_OF_DECLARE is no longer used as well as the
clksrc-of table.

Let's convert back the names:
 - CLOCKSOURCE_OF_DECLARE_RET => CLOCKSOURCE_OF_DECLARE
 - clksrc-of-ret              => clksrc-of

Signed-off-by: Daniel Lezcano <daniel.lezcano at linaro.org>

For exynos_mct and samsung_pwm_timer:
Acked-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>

For arch/arc:
Acked-by: Vineet Gupta <vgupta at synopsys.com>

For mediatek driver:
Acked-by: Matthias Brugger <matthias.bgg at gmail.com>

For the Rockchip-part
Acked-by: Heiko Stuebner <heiko at sntech.de>

For STi :
Acked-by: Patrice Chotard <patrice.chotard at st.com>

For the mps2-timer.c and versatile.c changes:
Acked-by: Liviu Dudau <Liviu.Dudau at arm.com>

For the OXNAS part :
Acked-by: Neil Armstrong <narmstrong at baylibre.com>

For LPC32xx driver:
Acked-by: Sylvain Lemieux <slemieux.tyco at gmail.com>

For Broadcom Kona timer change:
Acked-by: Ray Jui <ray.jui at broadcom.com>

For Sun4i and Sun5i:
Acked-by: Chen-Yu Tsai <wens at csie.org>

For Meson6:
Acked-by: Carlo Caione <carlo at caione.org>

For Keystone:
Acked-by: Santosh Shilimkar <ssantosh at kernel.org>

For NPS:
Acked-by: Noam Camus <noamca at mellanox.com>

For bcm2835:
Acked-by: Eric Anholt <eric at anholt.net>
---
 arch/arc/kernel/time.c                    |  6 +++---
 arch/arm/kernel/smp_twd.c                 |  6 +++---
 arch/microblaze/kernel/timer.c            |  2 +-
 arch/mips/ralink/cevt-rt3352.c            |  2 +-
 arch/nios2/kernel/time.c                  |  2 +-
 drivers/clocksource/arm_arch_timer.c      |  6 +++---
 drivers/clocksource/arm_global_timer.c    |  2 +-
 drivers/clocksource/armv7m_systick.c      |  2 +-
 drivers/clocksource/asm9260_timer.c       |  2 +-
 drivers/clocksource/bcm2835_timer.c       |  2 +-
 drivers/clocksource/bcm_kona_timer.c      |  4 ++--
 drivers/clocksource/cadence_ttc_timer.c   |  2 +-
 drivers/clocksource/clksrc-dbx500-prcmu.c |  2 +-
 drivers/clocksource/clksrc-probe.c        | 14 --------------
 drivers/clocksource/clksrc_st_lpc.c       |  2 +-
 drivers/clocksource/clps711x-timer.c      |  2 +-
 drivers/clocksource/dw_apb_timer_of.c     |  8 ++++----
 drivers/clocksource/exynos_mct.c          |  4 ++--
 drivers/clocksource/fsl_ftm_timer.c       |  2 +-
 drivers/clocksource/h8300_timer16.c       |  2 +-
 drivers/clocksource/h8300_timer8.c        |  2 +-
 drivers/clocksource/h8300_tpu.c           |  2 +-
 drivers/clocksource/meson6_timer.c        |  2 +-
 drivers/clocksource/mips-gic-timer.c      |  2 +-
 drivers/clocksource/moxart_timer.c        |  2 +-
 drivers/clocksource/mps2-timer.c          |  2 +-
 drivers/clocksource/mtk_timer.c           |  2 +-
 drivers/clocksource/mxs_timer.c           |  2 +-
 drivers/clocksource/nomadik-mtu.c         |  2 +-
 drivers/clocksource/pxa_timer.c           |  2 +-
 drivers/clocksource/qcom-timer.c          |  4 ++--
 drivers/clocksource/rockchip_timer.c      |  8 ++++----
 drivers/clocksource/samsung_pwm_timer.c   |  8 ++++----
 drivers/clocksource/sun4i_timer.c         |  2 +-
 drivers/clocksource/tango_xtal.c          |  2 +-
 drivers/clocksource/tegra20_timer.c       |  4 ++--
 drivers/clocksource/time-armada-370-xp.c  |  6 +++---
 drivers/clocksource/time-efm32.c          |  4 ++--
 drivers/clocksource/time-lpc32xx.c        |  2 +-
 drivers/clocksource/time-orion.c          |  2 +-
 drivers/clocksource/time-pistachio.c      |  2 +-
 drivers/clocksource/timer-atlas7.c        |  2 +-
 drivers/clocksource/timer-atmel-pit.c     |  2 +-
 drivers/clocksource/timer-atmel-st.c      |  2 +-
 drivers/clocksource/timer-digicolor.c     |  2 +-
 drivers/clocksource/timer-imx-gpt.c       | 24 ++++++++++++------------
 drivers/clocksource/timer-integrator-ap.c |  2 +-
 drivers/clocksource/timer-keystone.c      |  2 +-
 drivers/clocksource/timer-nps.c           |  4 ++--
 drivers/clocksource/timer-oxnas-rps.c     |  4 ++--
 drivers/clocksource/timer-prima2.c        |  2 +-
 drivers/clocksource/timer-sp804.c         |  4 ++--
 drivers/clocksource/timer-stm32.c         |  2 +-
 drivers/clocksource/timer-sun5i.c         |  4 ++--
 drivers/clocksource/timer-ti-32k.c        |  2 +-
 drivers/clocksource/timer-u300.c          |  2 +-
 drivers/clocksource/versatile.c           |  4 ++--
 drivers/clocksource/vf_pit_timer.c        |  2 +-
 drivers/clocksource/vt8500_timer.c        |  2 +-
 drivers/clocksource/zevio-timer.c         |  2 +-
 include/asm-generic/vmlinux.lds.h         |  2 --
 include/linux/clocksource.h               |  5 +----
 62 files changed, 98 insertions(+), 117 deletions(-)

diff --git a/arch/arc/kernel/time.c b/arch/arc/kernel/time.c
index 09de669..98f22d2 100644
--- a/arch/arc/kernel/time.c
+++ b/arch/arc/kernel/time.c
@@ -130,7 +130,7 @@ static int __init arc_cs_setup_gfrc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_gfrc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
+CLOCKSOURCE_OF_DECLARE(arc_gfrc, "snps,archs-timer-gfrc", arc_cs_setup_gfrc);
 
 #endif
 
@@ -192,7 +192,7 @@ static int __init arc_cs_setup_rtc(struct device_node *node)
 
 	return clocksource_register_hz(&arc_counter_rtc, arc_timer_freq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
+CLOCKSOURCE_OF_DECLARE(arc_rtc, "snps,archs-timer-rtc", arc_cs_setup_rtc);
 
 #endif
 
@@ -379,7 +379,7 @@ static int __init arc_of_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(arc_clkevt, "snps,arc-timer", arc_of_timer_init);
 
 /*
  * Called from start_kernel() - boot CPU only
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index 2b24be4..b6ec65e 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -412,7 +412,7 @@ out:
 	WARN(err, "twd_local_timer_of_register failed (%d)\n", err);
 	return err;
 }
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
-CLOCKSOURCE_OF_DECLARE_RET(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a9, "arm,cortex-a9-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_a5, "arm,cortex-a5-twd-timer", twd_local_timer_of_register);
+CLOCKSOURCE_OF_DECLARE(arm_twd_11mp, "arm,arm11mp-twd-timer", twd_local_timer_of_register);
 #endif
diff --git a/arch/microblaze/kernel/timer.c b/arch/microblaze/kernel/timer.c
index 7f35e7b..5bbf38b 100644
--- a/arch/microblaze/kernel/timer.c
+++ b/arch/microblaze/kernel/timer.c
@@ -332,5 +332,5 @@ static int __init xilinx_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(xilinx_timer, "xlnx,xps-timer-1.00.a",
+CLOCKSOURCE_OF_DECLARE(xilinx_timer, "xlnx,xps-timer-1.00.a",
 		       xilinx_timer_init);
diff --git a/arch/mips/ralink/cevt-rt3352.c b/arch/mips/ralink/cevt-rt3352.c
index f2d3c79..f24eee0 100644
--- a/arch/mips/ralink/cevt-rt3352.c
+++ b/arch/mips/ralink/cevt-rt3352.c
@@ -150,4 +150,4 @@ static int __init ralink_systick_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(systick, "ralink,cevt-systick", ralink_systick_init);
+CLOCKSOURCE_OF_DECLARE(systick, "ralink,cevt-systick", ralink_systick_init);
diff --git a/arch/nios2/kernel/time.c b/arch/nios2/kernel/time.c
index b75e40e..d9563dd 100644
--- a/arch/nios2/kernel/time.c
+++ b/arch/nios2/kernel/time.c
@@ -352,4 +352,4 @@ void __init time_init(void)
 	clocksource_probe();
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
+CLOCKSOURCE_OF_DECLARE(nios2_timer, ALTR_TIMER_COMPATIBLE, nios2_time_init);
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d0cda68..9e33309 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -784,8 +784,8 @@ static int __init arch_timer_of_init(struct device_node *np)
 
 	return arch_timer_init();
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
-CLOCKSOURCE_OF_DECLARE_RET(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
 
 static int __init arch_timer_mem_init(struct device_node *np)
 {
@@ -868,7 +868,7 @@ out:
 	of_node_put(best_frame);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(armv7_arch_timer_mem, "arm,armv7-timer-mem",
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
 		       arch_timer_mem_init);
 
 #ifdef CONFIG_ACPI
diff --git a/drivers/clocksource/arm_global_timer.c b/drivers/clocksource/arm_global_timer.c
index 40104fc..2a9ceb6 100644
--- a/drivers/clocksource/arm_global_timer.c
+++ b/drivers/clocksource/arm_global_timer.c
@@ -358,5 +358,5 @@ out_unmap:
 }
 
 /* Only tested on r2p2 and r3p0  */
-CLOCKSOURCE_OF_DECLARE_RET(arm_gt, "arm,cortex-a9-global-timer",
+CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
 			global_timer_of_register);
diff --git a/drivers/clocksource/armv7m_systick.c b/drivers/clocksource/armv7m_systick.c
index 2b55410..e93af1f 100644
--- a/drivers/clocksource/armv7m_systick.c
+++ b/drivers/clocksource/armv7m_systick.c
@@ -81,5 +81,5 @@ out_unmap:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(arm_systick, "arm,armv7m-systick",
+CLOCKSOURCE_OF_DECLARE(arm_systick, "arm,armv7m-systick",
 			system_timer_of_register);
diff --git a/drivers/clocksource/asm9260_timer.c b/drivers/clocksource/asm9260_timer.c
index d113c02..1ba871b 100644
--- a/drivers/clocksource/asm9260_timer.c
+++ b/drivers/clocksource/asm9260_timer.c
@@ -238,5 +238,5 @@ static int __init asm9260_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(asm9260_timer, "alphascale,asm9260-timer",
+CLOCKSOURCE_OF_DECLARE(asm9260_timer, "alphascale,asm9260-timer",
 		asm9260_timer_init);
diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c
index 2dcf896..e71acf2 100644
--- a/drivers/clocksource/bcm2835_timer.c
+++ b/drivers/clocksource/bcm2835_timer.c
@@ -142,5 +142,5 @@ static int __init bcm2835_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(bcm2835, "brcm,bcm2835-system-timer",
+CLOCKSOURCE_OF_DECLARE(bcm2835, "brcm,bcm2835-system-timer",
 			bcm2835_timer_init);
diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c
index c251aa6..86f87be3d 100644
--- a/drivers/clocksource/bcm_kona_timer.c
+++ b/drivers/clocksource/bcm_kona_timer.c
@@ -195,9 +195,9 @@ static int __init kona_timer_init(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(brcm_kona, "brcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(brcm_kona, "brcm,kona-timer", kona_timer_init);
 /*
  * bcm,kona-timer is deprecated by brcm,kona-timer
  * being kept here for driver compatibility
  */
-CLOCKSOURCE_OF_DECLARE_RET(bcm_kona, "bcm,kona-timer", kona_timer_init);
+CLOCKSOURCE_OF_DECLARE(bcm_kona, "bcm,kona-timer", kona_timer_init);
diff --git a/drivers/clocksource/cadence_ttc_timer.c b/drivers/clocksource/cadence_ttc_timer.c
index e2e7631..388a77b 100644
--- a/drivers/clocksource/cadence_ttc_timer.c
+++ b/drivers/clocksource/cadence_ttc_timer.c
@@ -539,4 +539,4 @@ static int __init ttc_timer_init(struct device_node *timer)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ttc, "cdns,ttc", ttc_timer_init);
+CLOCKSOURCE_OF_DECLARE(ttc, "cdns,ttc", ttc_timer_init);
diff --git a/drivers/clocksource/clksrc-dbx500-prcmu.c b/drivers/clocksource/clksrc-dbx500-prcmu.c
index 5a59d29..77a365f 100644
--- a/drivers/clocksource/clksrc-dbx500-prcmu.c
+++ b/drivers/clocksource/clksrc-dbx500-prcmu.c
@@ -86,5 +86,5 @@ static int __init clksrc_dbx500_prcmu_init(struct device_node *node)
 #endif
 	return clocksource_register_hz(&clocksource_dbx500_prcmu, RATE_32K);
 }
-CLOCKSOURCE_OF_DECLARE_RET(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
+CLOCKSOURCE_OF_DECLARE(dbx500_prcmu, "stericsson,db8500-prcmu-timer-4",
 		       clksrc_dbx500_prcmu_init);
diff --git a/drivers/clocksource/clksrc-probe.c b/drivers/clocksource/clksrc-probe.c
index 5fa6a55..bc62be9 100644
--- a/drivers/clocksource/clksrc-probe.c
+++ b/drivers/clocksource/clksrc-probe.c
@@ -20,19 +20,14 @@
 #include <linux/clocksource.h>
 
 extern struct of_device_id __clksrc_of_table[];
-extern struct of_device_id __clksrc_ret_of_table[];
 
 static const struct of_device_id __clksrc_of_table_sentinel
 	__used __section(__clksrc_of_table_end);
 
-static const struct of_device_id __clksrc_ret_of_table_sentinel
-	__used __section(__clksrc_ret_of_table_end);
-
 void __init clocksource_probe(void)
 {
 	struct device_node *np;
 	const struct of_device_id *match;
-	of_init_fn_1 init_func;
 	of_init_fn_1_ret init_func_ret;
 	unsigned clocksources = 0;
 	int ret;
@@ -41,15 +36,6 @@ void __init clocksource_probe(void)
 		if (!of_device_is_available(np))
 			continue;
 
-		init_func = match->data;
-		init_func(np);
-		clocksources++;
-	}
-
-	for_each_matching_node_and_match(np, __clksrc_ret_of_table, &match) {
-		if (!of_device_is_available(np))
-			continue;
-
 		init_func_ret = match->data;
 
 		ret = init_func_ret(np);
diff --git a/drivers/clocksource/clksrc_st_lpc.c b/drivers/clocksource/clksrc_st_lpc.c
index c9022a9..03cc492 100644
--- a/drivers/clocksource/clksrc_st_lpc.c
+++ b/drivers/clocksource/clksrc_st_lpc.c
@@ -132,4 +132,4 @@ static int __init st_clksrc_of_register(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ddata, "st,stih407-lpc", st_clksrc_of_register);
+CLOCKSOURCE_OF_DECLARE(ddata, "st,stih407-lpc", st_clksrc_of_register);
diff --git a/drivers/clocksource/clps711x-timer.c b/drivers/clocksource/clps711x-timer.c
index 3b66198..84aed78 100644
--- a/drivers/clocksource/clps711x-timer.c
+++ b/drivers/clocksource/clps711x-timer.c
@@ -119,5 +119,5 @@ static int __init clps711x_timer_init(struct device_node *np)
 		return -EINVAL;
 	}
 }
-CLOCKSOURCE_OF_DECLARE_RET(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
+CLOCKSOURCE_OF_DECLARE(clps711x, "cirrus,clps711x-timer", clps711x_timer_init);
 #endif
diff --git a/drivers/clocksource/dw_apb_timer_of.c b/drivers/clocksource/dw_apb_timer_of.c
index 4985a2c..aee6c0d 100644
--- a/drivers/clocksource/dw_apb_timer_of.c
+++ b/drivers/clocksource/dw_apb_timer_of.c
@@ -167,7 +167,7 @@ static int __init dw_apb_timer_init(struct device_node *timer)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(pc3x2_timer, "picochip,pc3x2-timer", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_osc, "snps,dw-apb-timer-osc", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer_sp, "snps,dw-apb-timer-sp", dw_apb_timer_init);
+CLOCKSOURCE_OF_DECLARE(apb_timer, "snps,dw-apb-timer", dw_apb_timer_init);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index f6caed0..0d18dd4b 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -627,5 +627,5 @@ static int __init mct_init_ppi(struct device_node *np)
 {
 	return mct_init_dt(np, MCT_INT_PPI);
 }
-CLOCKSOURCE_OF_DECLARE_RET(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
-CLOCKSOURCE_OF_DECLARE_RET(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
+CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
+CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
diff --git a/drivers/clocksource/fsl_ftm_timer.c b/drivers/clocksource/fsl_ftm_timer.c
index 9ad4ca3..738515b 100644
--- a/drivers/clocksource/fsl_ftm_timer.c
+++ b/drivers/clocksource/fsl_ftm_timer.c
@@ -369,4 +369,4 @@ err:
 	kfree(priv);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(flextimer, "fsl,ftm-timer", ftm_timer_init);
+CLOCKSOURCE_OF_DECLARE(flextimer, "fsl,ftm-timer", ftm_timer_init);
diff --git a/drivers/clocksource/h8300_timer16.c b/drivers/clocksource/h8300_timer16.c
index 9d99fc8..07d9d5b 100644
--- a/drivers/clocksource/h8300_timer16.c
+++ b/drivers/clocksource/h8300_timer16.c
@@ -187,5 +187,5 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_16bit, "renesas,16bit-timer",
+CLOCKSOURCE_OF_DECLARE(h8300_16bit, "renesas,16bit-timer",
 			   h8300_16timer_init);
diff --git a/drivers/clocksource/h8300_timer8.c b/drivers/clocksource/h8300_timer8.c
index 0292a19..546bb18 100644
--- a/drivers/clocksource/h8300_timer8.c
+++ b/drivers/clocksource/h8300_timer8.c
@@ -215,4 +215,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
+CLOCKSOURCE_OF_DECLARE(h8300_8bit, "renesas,8bit-timer", h8300_8timer_init);
diff --git a/drivers/clocksource/h8300_tpu.c b/drivers/clocksource/h8300_tpu.c
index 4faf718..7bdf199 100644
--- a/drivers/clocksource/h8300_tpu.c
+++ b/drivers/clocksource/h8300_tpu.c
@@ -154,4 +154,4 @@ free_clk:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(h8300_tpu, "renesas,tpu", h8300_tpu_init);
+CLOCKSOURCE_OF_DECLARE(h8300_tpu, "renesas,tpu", h8300_tpu_init);
diff --git a/drivers/clocksource/meson6_timer.c b/drivers/clocksource/meson6_timer.c
index 3a6e78f..52af591 100644
--- a/drivers/clocksource/meson6_timer.c
+++ b/drivers/clocksource/meson6_timer.c
@@ -174,5 +174,5 @@ static int __init meson6_timer_init(struct device_node *node)
 					1, 0xfffe);
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(meson6, "amlogic,meson6-timer",
+CLOCKSOURCE_OF_DECLARE(meson6, "amlogic,meson6-timer",
 		       meson6_timer_init);
diff --git a/drivers/clocksource/mips-gic-timer.c b/drivers/clocksource/mips-gic-timer.c
index b164b87..1572c7a 100644
--- a/drivers/clocksource/mips-gic-timer.c
+++ b/drivers/clocksource/mips-gic-timer.c
@@ -222,5 +222,5 @@ static void __init gic_clocksource_of_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mips_gic_timer, "mti,gic-timer",
+CLOCKSOURCE_OF_DECLARE(mips_gic_timer, "mti,gic-timer",
 		       gic_clocksource_of_init);
diff --git a/drivers/clocksource/moxart_timer.c b/drivers/clocksource/moxart_timer.c
index b9c30cd..8414544 100644
--- a/drivers/clocksource/moxart_timer.c
+++ b/drivers/clocksource/moxart_timer.c
@@ -178,4 +178,4 @@ static int __init moxart_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(moxart, "moxa,moxart-timer", moxart_timer_init);
+CLOCKSOURCE_OF_DECLARE(moxart, "moxa,moxart-timer", moxart_timer_init);
diff --git a/drivers/clocksource/mps2-timer.c b/drivers/clocksource/mps2-timer.c
index c303fa9..3e4431e 100644
--- a/drivers/clocksource/mps2-timer.c
+++ b/drivers/clocksource/mps2-timer.c
@@ -274,4 +274,4 @@ static int __init mps2_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(mps2_timer, "arm,mps2-timer", mps2_timer_init);
+CLOCKSOURCE_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);
diff --git a/drivers/clocksource/mtk_timer.c b/drivers/clocksource/mtk_timer.c
index 432a2c0..9065949 100644
--- a/drivers/clocksource/mtk_timer.c
+++ b/drivers/clocksource/mtk_timer.c
@@ -265,4 +265,4 @@ err_kzalloc:
 
 	return -EINVAL;
 }
-CLOCKSOURCE_OF_DECLARE_RET(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
+CLOCKSOURCE_OF_DECLARE(mtk_mt6577, "mediatek,mt6577-timer", mtk_timer_init);
diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c
index 17b9d19..630a8d3 100644
--- a/drivers/clocksource/mxs_timer.c
+++ b/drivers/clocksource/mxs_timer.c
@@ -295,4 +295,4 @@ static int __init mxs_timer_init(struct device_node *np)
 
 	return setup_irq(irq, &mxs_timer_irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(mxs, "fsl,timrot", mxs_timer_init);
+CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init);
diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c
index d2be5b3..3c124d1 100644
--- a/drivers/clocksource/nomadik-mtu.c
+++ b/drivers/clocksource/nomadik-mtu.c
@@ -284,5 +284,5 @@ static int __init nmdk_timer_of_init(struct device_node *node)
 
 	return nmdk_timer_init(base, irq, pclk, clk);
 }
-CLOCKSOURCE_OF_DECLARE_RET(nomadik_mtu, "st,nomadik-mtu",
+CLOCKSOURCE_OF_DECLARE(nomadik_mtu, "st,nomadik-mtu",
 		       nmdk_timer_of_init);
diff --git a/drivers/clocksource/pxa_timer.c b/drivers/clocksource/pxa_timer.c
index 59af75c..937e10b 100644
--- a/drivers/clocksource/pxa_timer.c
+++ b/drivers/clocksource/pxa_timer.c
@@ -213,7 +213,7 @@ static int __init pxa_timer_dt_init(struct device_node *np)
 
 	return pxa_timer_common_init(irq, clk_get_rate(clk));
 }
-CLOCKSOURCE_OF_DECLARE_RET(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
+CLOCKSOURCE_OF_DECLARE(pxa_timer, "marvell,pxa-timer", pxa_timer_dt_init);
 
 /*
  * Legacy timer init for non device-tree boards.
diff --git a/drivers/clocksource/qcom-timer.c b/drivers/clocksource/qcom-timer.c
index 79f73bd..6625763 100644
--- a/drivers/clocksource/qcom-timer.c
+++ b/drivers/clocksource/qcom-timer.c
@@ -273,5 +273,5 @@ static int __init msm_dt_timer_init(struct device_node *np)
 
 	return msm_timer_init(freq, 32, irq, !!percpu_offset);
 }
-CLOCKSOURCE_OF_DECLARE_RET(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
+CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
diff --git a/drivers/clocksource/rockchip_timer.c b/drivers/clocksource/rockchip_timer.c
index 85aee69..23e267a 100644
--- a/drivers/clocksource/rockchip_timer.c
+++ b/drivers/clocksource/rockchip_timer.c
@@ -205,7 +205,7 @@ static int __init rk3399_timer_init(struct device_node *np)
 	return rk_timer_init(np, TIMER_CONTROL_REG3399);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(rk3288_timer, "rockchip,rk3288-timer",
-			   rk3288_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(rk3399_timer, "rockchip,rk3399-timer",
-			   rk3399_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3288_timer, "rockchip,rk3288-timer",
+		       rk3288_timer_init);
+CLOCKSOURCE_OF_DECLARE(rk3399_timer, "rockchip,rk3399-timer",
+		       rk3399_timer_init);
diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c
index 27a9797..54565bd 100644
--- a/drivers/clocksource/samsung_pwm_timer.c
+++ b/drivers/clocksource/samsung_pwm_timer.c
@@ -466,7 +466,7 @@ static int __init s3c2410_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c24xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s3c64xx_variant = {
 	.bits		= 32,
@@ -479,7 +479,7 @@ static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s3c64xx_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p64x0_variant = {
 	.bits		= 32,
@@ -492,7 +492,7 @@ static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p64x0_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);
 
 static const struct samsung_pwm_variant s5p_variant = {
 	.bits		= 32,
@@ -505,5 +505,5 @@ static int __init s5p_pwm_clocksource_init(struct device_node *np)
 {
 	return samsung_pwm_alloc(np, &s5p_variant);
 }
-CLOCKSOURCE_OF_DECLARE_RET(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);
 #endif
diff --git a/drivers/clocksource/sun4i_timer.c b/drivers/clocksource/sun4i_timer.c
index 4453730..97669ee 100644
--- a/drivers/clocksource/sun4i_timer.c
+++ b/drivers/clocksource/sun4i_timer.c
@@ -226,5 +226,5 @@ static int __init sun4i_timer_init(struct device_node *node)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun4i, "allwinner,sun4i-a10-timer",
+CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-a10-timer",
 		       sun4i_timer_init);
diff --git a/drivers/clocksource/tango_xtal.c b/drivers/clocksource/tango_xtal.c
index 7dc716c..12fcef8 100644
--- a/drivers/clocksource/tango_xtal.c
+++ b/drivers/clocksource/tango_xtal.c
@@ -53,4 +53,4 @@ static int __init tango_clocksource_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(tango, "sigma,tick-counter", tango_clocksource_init);
+CLOCKSOURCE_OF_DECLARE(tango, "sigma,tick-counter", tango_clocksource_init);
diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c
index 543c37e..f960891 100644
--- a/drivers/clocksource/tegra20_timer.c
+++ b/drivers/clocksource/tegra20_timer.c
@@ -237,7 +237,7 @@ static int __init tegra20_init_timer(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
+CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer);
 
 static int __init tegra20_init_rtc(struct device_node *np)
 {
@@ -261,4 +261,4 @@ static int __init tegra20_init_rtc(struct device_node *np)
 
 	return register_persistent_clock(NULL, tegra_read_persistent_clock64);
 }
-CLOCKSOURCE_OF_DECLARE_RET(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
+CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc);
diff --git a/drivers/clocksource/time-armada-370-xp.c b/drivers/clocksource/time-armada-370-xp.c
index bc4ab48..a4e5923 100644
--- a/drivers/clocksource/time-armada-370-xp.c
+++ b/drivers/clocksource/time-armada-370-xp.c
@@ -371,7 +371,7 @@ static int __init armada_xp_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_xp, "marvell,armada-xp-timer",
+CLOCKSOURCE_OF_DECLARE(armada_xp, "marvell,armada-xp-timer",
 		       armada_xp_timer_init);
 
 static int __init armada_375_timer_init(struct device_node *np)
@@ -409,7 +409,7 @@ static int __init armada_375_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_375, "marvell,armada-375-timer",
+CLOCKSOURCE_OF_DECLARE(armada_375, "marvell,armada-375-timer",
 		       armada_375_timer_init);
 
 static int __init armada_370_timer_init(struct device_node *np)
@@ -432,5 +432,5 @@ static int __init armada_370_timer_init(struct device_node *np)
 
 	return armada_370_xp_timer_common_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(armada_370, "marvell,armada-370-timer",
+CLOCKSOURCE_OF_DECLARE(armada_370, "marvell,armada-370-timer",
 		       armada_370_timer_init);
diff --git a/drivers/clocksource/time-efm32.c b/drivers/clocksource/time-efm32.c
index 6e2f79f..5ac344b 100644
--- a/drivers/clocksource/time-efm32.c
+++ b/drivers/clocksource/time-efm32.c
@@ -283,5 +283,5 @@ static int __init efm32_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(efm32compat, "efm32,timer", efm32_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(efm32, "energymicro,efm32-timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32compat, "efm32,timer", efm32_timer_init);
+CLOCKSOURCE_OF_DECLARE(efm32, "energymicro,efm32-timer", efm32_timer_init);
diff --git a/drivers/clocksource/time-lpc32xx.c b/drivers/clocksource/time-lpc32xx.c
index cb5b866..9649cfd 100644
--- a/drivers/clocksource/time-lpc32xx.c
+++ b/drivers/clocksource/time-lpc32xx.c
@@ -311,4 +311,4 @@ static int __init lpc32xx_timer_init(struct device_node *np)
 
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
+CLOCKSOURCE_OF_DECLARE(lpc32xx_timer, "nxp,lpc3220-timer", lpc32xx_timer_init);
diff --git a/drivers/clocksource/time-orion.c b/drivers/clocksource/time-orion.c
index 5fdeb5d..a28f496 100644
--- a/drivers/clocksource/time-orion.c
+++ b/drivers/clocksource/time-orion.c
@@ -167,4 +167,4 @@ static int __init orion_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(orion_timer, "marvell,orion-timer", orion_timer_init);
+CLOCKSOURCE_OF_DECLARE(orion_timer, "marvell,orion-timer", orion_timer_init);
diff --git a/drivers/clocksource/time-pistachio.c b/drivers/clocksource/time-pistachio.c
index adaaec5..a7d9a08 100644
--- a/drivers/clocksource/time-pistachio.c
+++ b/drivers/clocksource/time-pistachio.c
@@ -214,5 +214,5 @@ static int __init pistachio_clksrc_of_init(struct device_node *node)
 	sched_clock_register(pistachio_read_sched_clock, 32, rate);
 	return clocksource_register_hz(&pcs_gpt.cs, rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(pistachio_gptimer, "img,pistachio-gptimer",
+CLOCKSOURCE_OF_DECLARE(pistachio_gptimer, "img,pistachio-gptimer",
 		       pistachio_clksrc_of_init);
diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c
index 7b1a007..90f8fbc 100644
--- a/drivers/clocksource/timer-atlas7.c
+++ b/drivers/clocksource/timer-atlas7.c
@@ -304,4 +304,4 @@ static int __init sirfsoc_of_timer_init(struct device_node *np)
 
 	return sirfsoc_atlas7_timer_init(np);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
+CLOCKSOURCE_OF_DECLARE(sirfsoc_atlas7_timer, "sirf,atlas7-tick", sirfsoc_of_timer_init);
diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index ffaca7c..1ffac0c 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -270,5 +270,5 @@ static int __init at91sam926x_pit_dt_init(struct device_node *node)
 
 	return at91sam926x_pit_common_init(data);
 }
-CLOCKSOURCE_OF_DECLARE_RET(at91sam926x_pit, "atmel,at91sam9260-pit",
+CLOCKSOURCE_OF_DECLARE(at91sam926x_pit, "atmel,at91sam9260-pit",
 		       at91sam926x_pit_dt_init);
diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c
index e9331d3..e90ab5b 100644
--- a/drivers/clocksource/timer-atmel-st.c
+++ b/drivers/clocksource/timer-atmel-st.c
@@ -260,5 +260,5 @@ static int __init atmel_st_timer_init(struct device_node *node)
 	/* register clocksource */
 	return clocksource_register_hz(&clk32k, sclk_rate);
 }
-CLOCKSOURCE_OF_DECLARE_RET(atmel_st_timer, "atmel,at91rm9200-st",
+CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st",
 		       atmel_st_timer_init);
diff --git a/drivers/clocksource/timer-digicolor.c b/drivers/clocksource/timer-digicolor.c
index b929061..10318cc 100644
--- a/drivers/clocksource/timer-digicolor.c
+++ b/drivers/clocksource/timer-digicolor.c
@@ -202,5 +202,5 @@ static int __init digicolor_timer_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(conexant_digicolor, "cnxt,cx92755-timer",
+CLOCKSOURCE_OF_DECLARE(conexant_digicolor, "cnxt,cx92755-timer",
 		       digicolor_timer_init);
diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c
index d5640a74..f595460 100644
--- a/drivers/clocksource/timer-imx-gpt.c
+++ b/drivers/clocksource/timer-imx-gpt.c
@@ -545,15 +545,15 @@ static int __init imx6dl_timer_init_dt(struct device_node *np)
 	return mxc_timer_init_dt(np, GPT_TYPE_IMX6DL);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
-CLOCKSOURCE_OF_DECLARE_RET(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx1_timer, "fsl,imx1-gpt", imx1_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx21_timer, "fsl,imx21-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx27_timer, "fsl,imx27-gpt", imx21_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx31_timer, "fsl,imx31-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx25_timer, "fsl,imx25-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx50_timer, "fsl,imx50-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx51_timer, "fsl,imx51-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx53_timer, "fsl,imx53-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6q_timer, "fsl,imx6q-gpt", imx31_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6dl_timer, "fsl,imx6dl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sl_timer, "fsl,imx6sl-gpt", imx6dl_timer_init_dt);
+CLOCKSOURCE_OF_DECLARE(imx6sx_timer, "fsl,imx6sx-gpt", imx6dl_timer_init_dt);
diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c
index 675face..df6e672 100644
--- a/drivers/clocksource/timer-integrator-ap.c
+++ b/drivers/clocksource/timer-integrator-ap.c
@@ -232,5 +232,5 @@ static int __init integrator_ap_timer_init_of(struct device_node *node)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(integrator_ap_timer, "arm,integrator-timer",
+CLOCKSOURCE_OF_DECLARE(integrator_ap_timer, "arm,integrator-timer",
 		       integrator_ap_timer_init_of);
diff --git a/drivers/clocksource/timer-keystone.c b/drivers/clocksource/timer-keystone.c
index 4199823..ab68a47 100644
--- a/drivers/clocksource/timer-keystone.c
+++ b/drivers/clocksource/timer-keystone.c
@@ -226,5 +226,5 @@ err:
 	return error;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(keystone_timer, "ti,keystone-timer",
+CLOCKSOURCE_OF_DECLARE(keystone_timer, "ti,keystone-timer",
 			   keystone_timer_init);
diff --git a/drivers/clocksource/timer-nps.c b/drivers/clocksource/timer-nps.c
index b5c7b2b..70c149a 100644
--- a/drivers/clocksource/timer-nps.c
+++ b/drivers/clocksource/timer-nps.c
@@ -96,5 +96,5 @@ static int __init nps_timer_init(struct device_node *node)
 	return nps_setup_clocksource(node, clk);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ezchip_nps400_clksrc, "ezchip,nps400-timer",
-			   nps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ezchip_nps400_clksrc, "ezchip,nps400-timer",
+		       nps_timer_init);
diff --git a/drivers/clocksource/timer-oxnas-rps.c b/drivers/clocksource/timer-oxnas-rps.c
index 0d99f40..bd887e2 100644
--- a/drivers/clocksource/timer-oxnas-rps.c
+++ b/drivers/clocksource/timer-oxnas-rps.c
@@ -293,5 +293,5 @@ err_alloc:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(ox810se_rps,
-			   "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
+CLOCKSOURCE_OF_DECLARE(ox810se_rps,
+		       "oxsemi,ox810se-rps-timer", oxnas_rps_timer_init);
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 7b1084d..dae8a66 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -246,5 +246,5 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sirfsoc_prima2_timer,
+CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
 	"sirf,prima2-tick", sirfsoc_prima2_timer_init);
diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c
index 3dc47ef..d078633 100644
--- a/drivers/clocksource/timer-sp804.c
+++ b/drivers/clocksource/timer-sp804.c
@@ -287,7 +287,7 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(sp804, "arm,sp804", sp804_of_init);
+CLOCKSOURCE_OF_DECLARE(sp804, "arm,sp804", sp804_of_init);
 
 static int __init integrator_cp_of_init(struct device_node *np)
 {
@@ -335,4 +335,4 @@ err:
 	iounmap(base);
 	return ret;
 }
-CLOCKSOURCE_OF_DECLARE_RET(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
+CLOCKSOURCE_OF_DECLARE(intcp, "arm,integrator-cp-timer", integrator_cp_of_init);
diff --git a/drivers/clocksource/timer-stm32.c b/drivers/clocksource/timer-stm32.c
index d5bf352..1b2574c 100644
--- a/drivers/clocksource/timer-stm32.c
+++ b/drivers/clocksource/timer-stm32.c
@@ -187,4 +187,4 @@ err_clk_get:
 	return ret;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(stm32, "st,stm32-timer", stm32_clockevent_init);
+CLOCKSOURCE_OF_DECLARE(stm32, "st,stm32-timer", stm32_clockevent_init);
diff --git a/drivers/clocksource/timer-sun5i.c b/drivers/clocksource/timer-sun5i.c
index f0a3ffb..c184eb8 100644
--- a/drivers/clocksource/timer-sun5i.c
+++ b/drivers/clocksource/timer-sun5i.c
@@ -346,7 +346,7 @@ static int __init sun5i_timer_init(struct device_node *node)
 
 	return sun5i_setup_clockevent(node, timer_base, clk, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(sun5i_a13, "allwinner,sun5i-a13-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
 			   sun5i_timer_init);
-CLOCKSOURCE_OF_DECLARE_RET(sun7i_a20, "allwinner,sun7i-a20-hstimer",
+CLOCKSOURCE_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
 			   sun5i_timer_init);
diff --git a/drivers/clocksource/timer-ti-32k.c b/drivers/clocksource/timer-ti-32k.c
index e4ad3c6e..92b7e39 100644
--- a/drivers/clocksource/timer-ti-32k.c
+++ b/drivers/clocksource/timer-ti-32k.c
@@ -124,5 +124,5 @@ static int __init ti_32k_timer_init(struct device_node *np)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(ti_32k_timer, "ti,omap-counter32k",
+CLOCKSOURCE_OF_DECLARE(ti_32k_timer, "ti,omap-counter32k",
 		ti_32k_timer_init);
diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c
index a6a0dec..704e40c 100644
--- a/drivers/clocksource/timer-u300.c
+++ b/drivers/clocksource/timer-u300.c
@@ -458,5 +458,5 @@ static int __init u300_timer_init_of(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(u300_timer, "stericsson,u300-apptimer",
+CLOCKSOURCE_OF_DECLARE(u300_timer, "stericsson,u300-apptimer",
 		       u300_timer_init_of);
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
index 8daeffa..220b490 100644
--- a/drivers/clocksource/versatile.c
+++ b/drivers/clocksource/versatile.c
@@ -38,7 +38,7 @@ static int __init versatile_sched_clock_init(struct device_node *node)
 
 	return 0;
 }
-CLOCKSOURCE_OF_DECLARE_RET(vexpress, "arm,vexpress-sysreg",
+CLOCKSOURCE_OF_DECLARE(vexpress, "arm,vexpress-sysreg",
 		       versatile_sched_clock_init);
-CLOCKSOURCE_OF_DECLARE_RET(versatile, "arm,versatile-sysreg",
+CLOCKSOURCE_OF_DECLARE(versatile, "arm,versatile-sysreg",
 		       versatile_sched_clock_init);
diff --git a/drivers/clocksource/vf_pit_timer.c b/drivers/clocksource/vf_pit_timer.c
index ca4dff4..55d8d84 100644
--- a/drivers/clocksource/vf_pit_timer.c
+++ b/drivers/clocksource/vf_pit_timer.c
@@ -201,4 +201,4 @@ static int __init pit_timer_init(struct device_node *np)
 
 	return pit_clockevent_init(clk_rate, irq);
 }
-CLOCKSOURCE_OF_DECLARE_RET(vf610, "fsl,vf610-pit", pit_timer_init);
+CLOCKSOURCE_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);
diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c
index 1bc8707..b150694 100644
--- a/drivers/clocksource/vt8500_timer.c
+++ b/drivers/clocksource/vt8500_timer.c
@@ -165,4 +165,4 @@ static int __init vt8500_timer_init(struct device_node *np)
 	return 0;
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(vt8500, "via,vt8500-timer", vt8500_timer_init);
+CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init);
diff --git a/drivers/clocksource/zevio-timer.c b/drivers/clocksource/zevio-timer.c
index cb4cf056..9a53f5e 100644
--- a/drivers/clocksource/zevio-timer.c
+++ b/drivers/clocksource/zevio-timer.c
@@ -215,4 +215,4 @@ static int __init zevio_timer_init(struct device_node *node)
 	return zevio_timer_add(node);
 }
 
-CLOCKSOURCE_OF_DECLARE_RET(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
+CLOCKSOURCE_OF_DECLARE(zevio_timer, "lsi,zevio-timer", zevio_timer_init);
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 8c6c626..6a67ab9 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -173,7 +173,6 @@
 	*(__##name##_of_table_end)
 
 #define CLKSRC_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc)
-#define CLKSRC_RET_OF_TABLES()	OF_TABLE(CONFIG_CLKSRC_OF, clksrc_ret)
 #define IRQCHIP_OF_MATCH_TABLE() OF_TABLE(CONFIG_IRQCHIP, irqchip)
 #define CLK_OF_TABLES()		OF_TABLE(CONFIG_COMMON_CLK, clk)
 #define IOMMU_OF_TABLES()	OF_TABLE(CONFIG_OF_IOMMU, iommu)
@@ -532,7 +531,6 @@
 	CLK_OF_TABLES()							\
 	RESERVEDMEM_OF_TABLES()						\
 	CLKSRC_OF_TABLES()						\
-	CLKSRC_RET_OF_TABLES()						\
 	IOMMU_OF_TABLES()						\
 	CPU_METHOD_OF_TABLES()						\
 	CPUIDLE_METHOD_OF_TABLES()					\
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index 15c3839..0839818 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -244,10 +244,7 @@ extern int clocksource_mmio_init(void __iomem *, const char *,
 extern int clocksource_i8253_init(void);
 
 #define CLOCKSOURCE_OF_DECLARE(name, compat, fn) \
-	OF_DECLARE_1(clksrc, name, compat, fn)
-
-#define CLOCKSOURCE_OF_DECLARE_RET(name, compat, fn) \
-	OF_DECLARE_1_RET(clksrc_ret, name, compat, fn)
+	OF_DECLARE_1_RET(clksrc, name, compat, fn)
 
 #ifdef CONFIG_CLKSRC_PROBE
 extern void clocksource_probe(void);
-- 
1.9.1



From tj at kernel.org  Tue Jun 28 09:56:16 2016
From: tj at kernel.org (Tejun Heo)
Date: Tue, 28 Jun 2016 12:56:16 -0400
Subject: [PATCH] PM / suspend: show workqueues busy name in suspend flow
In-Reply-To: <1467020645.25092.24.camel@mtksdaap41>
References: <1466586509-32400-1-git-send-email-roger.lu@mediatek.com>
 <20160622152126.GV3262@mtj.duckdns.org>
 <1467020645.25092.24.camel@mtksdaap41>
Message-ID: <20160628165616.GD5185@htj.duckdns.org>

Hello, Roger.

On Mon, Jun 27, 2016 at 05:44:05PM +0800, Roger Lu wrote:
> show_workqueue_state() is a better choice to me. However, only freezable
> workqueue is able to affect suspend flow. So, is there other mailing
> list discussing about showing freezable workqueue state only?? Maybe we
> can use that API in this case. Thanks very much.
> 
> freezable workqueue means workqueue is created with flag WQ_FREEZABLE.

It's for debugging anyway and workqueue dumps usually are pretty
short.  I don't think it's fine to use the same function.  We can add
flags in the printouts but I'm not even sure that'd be necessary.

Thanks.

-- 
tejun


From robh at kernel.org  Tue Jun 28 14:01:29 2016
From: robh at kernel.org (Rob Herring)
Date: Tue, 28 Jun 2016 16:01:29 -0500
Subject: [PATCH v2 1/2] Document: DT: Add bindings for mediatek MT6755
 SoC Platform
In-Reply-To: <1467090956-31757-2-git-send-email-mars.cheng@mediatek.com>
References: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
 <1467090956-31757-2-git-send-email-mars.cheng@mediatek.com>
Message-ID: <20160628210129.GA7076@rob-hp-laptop>

On Tue, Jun 28, 2016 at 01:15:55PM +0800, Mars Cheng wrote:
> This adds DT binding documentation for Mediatek MT6755.
> 
> Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
> ---
>  Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
>  .../interrupt-controller/mediatek,sysirq.txt       |    1 +
>  .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
>  3 files changed, 6 insertions(+)

Please add acks when posting new versions or explain why you didn't.

Rob


From mars.cheng at mediatek.com  Tue Jun 28 18:39:38 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Wed, 29 Jun 2016 09:39:38 +0800
Subject: [PATCH v2 1/2] Document: DT: Add bindings for mediatek MT6755
 SoC Platform
In-Reply-To: <20160628210129.GA7076@rob-hp-laptop>
References: <1467090956-31757-1-git-send-email-mars.cheng@mediatek.com>
 <1467090956-31757-2-git-send-email-mars.cheng@mediatek.com>
 <20160628210129.GA7076@rob-hp-laptop>
Message-ID: <1467164378.7805.2.camel@mtkswgap22>

On Tue, 2016-06-28 at 16:01 -0500, Rob Herring wrote:
> On Tue, Jun 28, 2016 at 01:15:55PM +0800, Mars Cheng wrote:
> > This adds DT binding documentation for Mediatek MT6755.
> > 
> > Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
> > ---
> >  Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
> >  .../interrupt-controller/mediatek,sysirq.txt       |    1 +
> >  .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
> >  3 files changed, 6 insertions(+)
> 
> Please add acks when posting new versions or explain why you didn't.
> 
> Rob
Hi Rob

So sorry about this. I just miss it. Will add acks and send another
patch.

Thanks for your kind reminder.



From mars.cheng at mediatek.com  Tue Jun 28 19:09:32 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Wed, 29 Jun 2016 10:09:32 +0800
Subject: [PATCH v3 1/2] Document: DT: Add bindings for mediatek MT6755 SoC
 Platform
In-Reply-To: <1467166173-9029-1-git-send-email-mars.cheng@mediatek.com>
References: <1467166173-9029-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1467166173-9029-2-git-send-email-mars.cheng@mediatek.com>

This adds DT binding documentation for Mediatek MT6755.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
Acked-by: Rob Herring <robh at kernel.org>
---
 Documentation/devicetree/bindings/arm/mediatek.txt |    4 ++++
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 3 files changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/mediatek.txt b/Documentation/devicetree/bindings/arm/mediatek.txt
index d9c2a37..c860b24 100644
--- a/Documentation/devicetree/bindings/arm/mediatek.txt
+++ b/Documentation/devicetree/bindings/arm/mediatek.txt
@@ -10,6 +10,7 @@ compatible: Must contain one of
    "mediatek,mt6580"
    "mediatek,mt6589"
    "mediatek,mt6592"
+   "mediatek,mt6755"
    "mediatek,mt6795"
    "mediatek,mt7623"
    "mediatek,mt8127"
@@ -31,6 +32,9 @@ Supported boards:
 - Evaluation board for MT6592:
     Required root node properties:
       - compatible = "mediatek,mt6592-evb", "mediatek,mt6592";
+- Evaluation phone for MT6755(Helio P10):
+    Required root node properties:
+      - compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
 - Evaluation board for MT6795(Helio X10):
     Required root node properties:
       - compatible = "mediatek,mt6795-evb", "mediatek,mt6795";
diff --git a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
index 8cf564d..9d1d72c 100644
--- a/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
+++ b/Documentation/devicetree/bindings/interrupt-controller/mediatek,sysirq.txt
@@ -9,6 +9,7 @@ Required properties:
 	"mediatek,mt8135-sysirq"
 	"mediatek,mt8127-sysirq"
 	"mediatek,mt6795-sysirq"
+	"mediatek,mt6755-sysirq"
 	"mediatek,mt6592-sysirq"
 	"mediatek,mt6589-sysirq"
 	"mediatek,mt6582-sysirq"
diff --git a/Documentation/devicetree/bindings/serial/mtk-uart.txt b/Documentation/devicetree/bindings/serial/mtk-uart.txt
index e99e10a..0015c72 100644
--- a/Documentation/devicetree/bindings/serial/mtk-uart.txt
+++ b/Documentation/devicetree/bindings/serial/mtk-uart.txt
@@ -6,6 +6,7 @@ Required properties:
   * "mediatek,mt6580-uart" for MT6580 compatible UARTS
   * "mediatek,mt6582-uart" for MT6582 compatible UARTS
   * "mediatek,mt6589-uart" for MT6589 compatible UARTS
+  * "mediatek,mt6755-uart" for MT6755 compatible UARTS
   * "mediatek,mt6795-uart" for MT6795 compatible UARTS
   * "mediatek,mt7623-uart" for MT7623 compatible UARTS
   * "mediatek,mt8127-uart" for MT8127 compatible UARTS
-- 
1.7.9.5



From mars.cheng at mediatek.com  Tue Jun 28 19:09:31 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Wed, 29 Jun 2016 10:09:31 +0800
Subject: [PATCH v3 0/2] Add mt6755 basic chip support
Message-ID: <1467166173-9029-1-git-send-email-mars.cheng@mediatek.com>

This patch adds basic support for Mediatek's new 8-core chip, mt6755.
Based on 4.7-rc1

Changes in v3:
1. use evb as project suffix
2. add "disable" property for uart dts nodes
3. only alias uart0 as serial0, since we don't enable uart1
4. confirm all acks are added

Mars Cheng (2):
  Document: DT: Add bindings for mediatek MT6755 SoC Platform
  arm64: dts: mediatek: add mt6755 support

Mars Cheng (2):
  Document: DT: Add bindings for mediatek MT6755 SoC Platform
  arm64: dts: mediatek: add mt6755 support

 Documentation/devicetree/bindings/arm/mediatek.txt |    4 +
 .../interrupt-controller/mediatek,sysirq.txt       |    1 +
 .../devicetree/bindings/serial/mtk-uart.txt        |    1 +
 arch/arm64/boot/dts/mediatek/Makefile              |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-evb.dts        |   38 +++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi           |  145 ++++++++++++++++++++
 6 files changed, 190 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-evb.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi

--
1.7.9.5


From mars.cheng at mediatek.com  Tue Jun 28 19:09:33 2016
From: mars.cheng at mediatek.com (Mars Cheng)
Date: Wed, 29 Jun 2016 10:09:33 +0800
Subject: [PATCH v3 2/2] arm64: dts: mediatek: add mt6755 support
In-Reply-To: <1467166173-9029-1-git-send-email-mars.cheng@mediatek.com>
References: <1467166173-9029-1-git-send-email-mars.cheng@mediatek.com>
Message-ID: <1467166173-9029-3-git-send-email-mars.cheng@mediatek.com>

This adds basic chip support for MT6755 SoC.

Signed-off-by: Mars Cheng <mars.cheng at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/Makefile       |    1 +
 arch/arm64/boot/dts/mediatek/mt6755-evb.dts |   38 +++++++
 arch/arm64/boot/dts/mediatek/mt6755.dtsi    |  145 +++++++++++++++++++++++++++
 3 files changed, 184 insertions(+)
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755-evb.dts
 create mode 100644 arch/arm64/boot/dts/mediatek/mt6755.dtsi

diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile
index e0a4bff..9fbfd32 100644
--- a/arch/arm64/boot/dts/mediatek/Makefile
+++ b/arch/arm64/boot/dts/mediatek/Makefile
@@ -1,3 +1,4 @@
+dtb-$(CONFIG_ARCH_MEDIATEK) += mt6755-evb.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb
 dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb
 
diff --git a/arch/arm64/boot/dts/mediatek/mt6755-evb.dts b/arch/arm64/boot/dts/mediatek/mt6755-evb.dts
new file mode 100644
index 0000000..c568d49
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755-evb.dts
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/dts-v1/;
+#include "mt6755.dtsi"
+
+/ {
+	model = "MediaTek MT6755 EVB";
+	compatible = "mediatek,mt6755-evb", "mediatek,mt6755";
+
+	aliases {
+		serial0 = &uart0;
+	};
+
+	memory at 40000000 {
+		device_type = "memory";
+		reg = <0 0x40000000 0 0x1e800000>;
+	};
+
+	chosen {
+		stdout-path = "serial0:921600n8";
+	};
+};
+
+&uart0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
new file mode 100644
index 0000000..01ba776
--- /dev/null
+++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Mars.C <mars.cheng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+
+/ {
+	compatible = "mediatek,mt6755";
+	interrupt-parent = <&sysirq>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	psci {
+		compatible = "arm,psci-0.2";
+		method = "smc";
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x000>;
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x001>;
+		};
+
+		cpu2: cpu at 2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x002>;
+		};
+
+		cpu3: cpu at 3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x003>;
+		};
+
+		cpu4: cpu at 100 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x100>;
+		};
+
+		cpu5: cpu at 101 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x101>;
+		};
+
+		cpu6: cpu at 102 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x102>;
+		};
+
+		cpu7: cpu at 103 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a53";
+			enable-method = "psci";
+			reg = <0x103>;
+		};
+	};
+
+	uart_clk: dummy26m {
+		compatible = "fixed-clock";
+		clock-frequency = <26000000>;
+		#clock-cells = <0>;
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupt-parent = <&gic>;
+		interrupts = <GIC_PPI 13
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10
+			     (GIC_CPU_MASK_SIMPLE(8) | IRQ_TYPE_LEVEL_LOW)>;
+	};
+
+	sysirq: intpol-controller at 10200620 {
+		compatible = "mediatek,mt6755-sysirq",
+			     "mediatek,mt6577-sysirq";
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		reg = <0 0x10200620 0 0x20>;
+	};
+
+	gic: interrupt-controller at 10231000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-parent = <&gic>;
+		interrupt-controller;
+		reg = <0 0x10231000 0 0x1000>,
+		      <0 0x10232000 0 0x2000>,
+		      <0 0x10234000 0 0x2000>,
+		      <0 0x10236000 0 0x2000>;
+	};
+
+	uart0: serial at 11002000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11002000 0 0x400>;
+		interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+		status = "disabled";
+	};
+
+	uart1: serial at 11003000 {
+		compatible = "mediatek,mt6755-uart",
+			     "mediatek,mt6577-uart";
+		reg = <0 0x11003000 0 0x400>;
+		interrupts = <GIC_SPI 92 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&uart_clk>;
+		status = "disabled";
+	};
+};
-- 
1.7.9.5



From roger.lu at mediatek.com  Tue Jun 28 20:54:11 2016
From: roger.lu at mediatek.com (Roger Lu)
Date: Wed, 29 Jun 2016 11:54:11 +0800
Subject: [PATCH] PM / suspend: show workqueues busy name in suspend flow
In-Reply-To: <20160628165616.GD5185@htj.duckdns.org>
References: <1466586509-32400-1-git-send-email-roger.lu@mediatek.com>
 <20160622152126.GV3262@mtj.duckdns.org>
 <1467020645.25092.24.camel@mtksdaap41>
 <20160628165616.GD5185@htj.duckdns.org>
Message-ID: <1467172451.4141.20.camel@mtksdaap41>

Dear Tejun,

On Tue, 2016-06-28 at 12:56 -0400, Tejun Heo wrote:
> Hello, Roger.
> 
> On Mon, Jun 27, 2016 at 05:44:05PM +0800, Roger Lu wrote:
> > show_workqueue_state() is a better choice to me. However, only freezable
> > workqueue is able to affect suspend flow. So, is there other mailing
> > list discussing about showing freezable workqueue state only?? Maybe we
> > can use that API in this case. Thanks very much.
> > 
> > freezable workqueue means workqueue is created with flag WQ_FREEZABLE.
> 
> It's for debugging anyway and workqueue dumps usually are pretty
> short.  I don't think it's fine to use the same function.  We can add
> flags in the printouts but I'm not even sure that'd be necessary.
> 
> Thanks.
> 

Please allow me to elaborate my previous concern about printing
freezable workqueue info only in this case.

The benefit of it is that debugger can quickly understand which
freezable workqueues block suspend flow and assign this issue to
corresponding owner instead of extracting freezable workqueue info from
show_workqueue_state() first and, then, assigning the issue.

Adding a flag to printout the info we need is great. Perhaps we can do
that. Thanks for the advice.

Sincerely,
Roger Lu.









From djkurtz at chromium.org  Tue Jun 28 21:31:23 2016
From: djkurtz at chromium.org (Daniel Kurtz)
Date: Wed, 29 Jun 2016 12:31:23 +0800
Subject: [PATCH 2/2 v16] drm/bridge: Add I2C based driver for ps8640 bridge
In-Reply-To: <CACvgo53002XCPLufbJHQLC1uL1nNS7_0gh=napedetmvT6FoSw@mail.gmail.com>
References: <1464861445-11086-1-git-send-email-jitao.shi@mediatek.com>
 <1464861445-11086-2-git-send-email-jitao.shi@mediatek.com>
 <CACvgo53002XCPLufbJHQLC1uL1nNS7_0gh=napedetmvT6FoSw@mail.gmail.com>
Message-ID: <CAGS+omDsDS5tx=+Efq8LWNd3-asU4r2h=-AE6vdKgfG-p6vdAA@mail.gmail.com>

Hi Emil,

One answer inline below.  The rest I leave to Jitao...

[snip...]

On Fri, Jun 17, 2016 at 3:14 AM, Emil Velikov <emil.l.velikov at gmail.com> wrote:

>> +static ssize_t ps8640_update_fw_store(struct device *dev,
>> +                                     struct device_attribute *attr,
>> +                                     const char *buf, size_t count)
>> +{
>> +       struct i2c_client *client = to_i2c_client(dev);
>> +       struct ps8640 *ps_bridge = i2c_get_clientdata(client);
>> +       const struct firmware *fw;
>> +       int error;
>> +
>> +       error = request_firmware(&fw, PS_FW_NAME, dev);
> Can the device operate without a firmware ? If not, why is the
> firmware loaded so later/after user interaction (via sysfs) ? I don't
> recall any other driver in DRM to use such an approach.

The PS8640 has internal flash, so it should always already have a
working firmware.
This sysfs interface is useful for user space initiated field firmware updates.

Regards,
-Daniel

> Regards,
> Emil
> _______________________________________________
> dri-devel mailing list
> dri-devel at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel


From john at phrozen.org  Wed Jun 29 04:38:07 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 29 Jun 2016 13:38:07 +0200
Subject: [PATCH V2 0/4] net-next: mediatek: IRQ cleanups, fixes and grouping
Message-ID: <1467200291-60106-1-git-send-email-john@phrozen.org>

This series contains 2 small code cleanups that are leftovers from the
MIPS support. There is also a small fix that adds proper locking to the
code accessing the IRQ registers. Without this fix we saw deadlocks caused
by the last patch of the series, which adds IRQ grouping. The grouping
feature allows us to use different IRQs for TX and RX. By doing so we can
use affinity to let the SoC handle the IRQs on different cores.

This series depends on a previous series currently sitting in net.git
starting with
	commit 562c5a70400c ("net: mediatek: only wake the queue if it is stopped")
up to
	commit 82c6544dddc6 ("net: mediatek: remove superfluous queue wake up call")


John Crispin (4):
  net-next: mediatek: remove superfluous register reads
  net-next: mediatek: don't use intermediate variables to store IRQ
    masks
  net-next: mediatek: add IRQ locking
  net-next: mediatek: add support for IRQ grouping

 drivers/net/ethernet/mediatek/mtk_eth_soc.c |  174 +++++++++++++++++----------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |   16 ++-
 2 files changed, 122 insertions(+), 68 deletions(-)

-- 
1.7.10.4



From john at phrozen.org  Wed Jun 29 04:38:09 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 29 Jun 2016 13:38:09 +0200
Subject: [PATCH V2 2/4] net-next: mediatek: don't use intermediate variables
 to store IRQ masks
In-Reply-To: <1467200291-60106-1-git-send-email-john@phrozen.org>
References: <1467200291-60106-1-git-send-email-john@phrozen.org>
Message-ID: <1467200291-60106-3-git-send-email-john@phrozen.org>

The code currently uses variables to store and never modify the bit masks
of interrupts. This is legacy code from an early version of the driver
that supported MIPS based SoCs where the IRQ bits depended on the actual
SoC. As the bits are the same for all ARM based SoCs using this driver we
can remove the intermediate variables.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |   22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 5a3a4e9..d6c3a17 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -794,7 +794,7 @@ drop:
 }
 
 static int mtk_poll_rx(struct napi_struct *napi, int budget,
-		       struct mtk_eth *eth, u32 rx_intr)
+		       struct mtk_eth *eth)
 {
 	struct mtk_rx_ring *ring = &eth->rx_ring;
 	int idx = ring->calc_idx;
@@ -882,7 +882,7 @@ release_desc:
 	}
 
 	if (done < budget)
-		mtk_w32(eth, rx_intr, MTK_QMTK_INT_STATUS);
+		mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
 
 	return done;
 }
@@ -967,28 +967,26 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 static int mtk_poll(struct napi_struct *napi, int budget)
 {
 	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-	u32 status, status2, mask, tx_intr, rx_intr, status_intr;
+	u32 status, status2, mask;
 	int tx_done, rx_done;
 	bool tx_again = false;
 
 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
 	status2 = mtk_r32(eth, MTK_INT_STATUS2);
-	tx_intr = MTK_TX_DONE_INT;
-	rx_intr = MTK_RX_DONE_INT;
-	status_intr = (MTK_GDM1_AF | MTK_GDM2_AF);
 	tx_done = 0;
 	rx_done = 0;
 	tx_again = 0;
 
-	if (status & tx_intr)
+	if (status & MTK_TX_DONE_INT)
 		tx_done = mtk_poll_tx(eth, budget, &tx_again);
 
-	if (status & rx_intr)
-		rx_done = mtk_poll_rx(napi, budget, eth, rx_intr);
+	if (status & MTK_RX_DONE_INT)
+		rx_done = mtk_poll_rx(napi, budget, eth);
 
-	if (unlikely(status2 & status_intr)) {
+	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
 		mtk_stats_update(eth);
-		mtk_w32(eth, status_intr, MTK_INT_STATUS2);
+		mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
+			MTK_INT_STATUS2);
 	}
 
 	if (unlikely(netif_msg_intr(eth))) {
@@ -1006,7 +1004,7 @@ static int mtk_poll(struct napi_struct *napi, int budget)
 		return budget;
 
 	napi_complete(napi);
-	mtk_irq_enable(eth, tx_intr | rx_intr);
+	mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
 
 	return rx_done;
 }
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 29 04:38:10 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 29 Jun 2016 13:38:10 +0200
Subject: [PATCH V2 3/4] net-next: mediatek: add IRQ locking
In-Reply-To: <1467200291-60106-1-git-send-email-john@phrozen.org>
References: <1467200291-60106-1-git-send-email-john@phrozen.org>
Message-ID: <1467200291-60106-4-git-send-email-john@phrozen.org>

The code that enables and disables IRQs is missing proper locking. After
adding the IRQ grouping patch and routing the RX and TX IRQs to different
cores we experienced IRQ stalls. Fix this by adding proper locking.
We use a dedicated lock to reduce the latency if the IRQ code.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    7 +++++++
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |    1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index d6c3a17..698caba 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -328,18 +328,24 @@ static void mtk_mdio_cleanup(struct mtk_eth *eth)
 
 static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
 {
+	unsigned long flags;
 	u32 val;
 
+	spin_lock_irqsave(&eth->irq_lock, flags);
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
+	spin_unlock_irqrestore(&eth->irq_lock, flags);
 }
 
 static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
 {
+	unsigned long flags;
 	u32 val;
 
+	spin_lock_irqsave(&eth->irq_lock, flags);
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
+	spin_unlock_irqrestore(&eth->irq_lock, flags);
 }
 
 static int mtk_set_mac_address(struct net_device *dev, void *p)
@@ -1760,6 +1766,7 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->base);
 
 	spin_lock_init(&eth->page_lock);
+	spin_lock_init(&eth->irq_lock);
 
 	eth->ethsys = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
 						      "mediatek,ethsys");
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index a5eb7c6..3159d2a 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -373,6 +373,7 @@ struct mtk_eth {
 	void __iomem			*base;
 	struct reset_control		*rstc;
 	spinlock_t			page_lock;
+	spinlock_t			irq_lock;
 	struct net_device		dummy_dev;
 	struct net_device		*netdev[MTK_MAX_DEVS];
 	struct mtk_mac			*mac[MTK_MAX_DEVS];
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 29 04:38:08 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 29 Jun 2016 13:38:08 +0200
Subject: [PATCH V2 1/4] net-next: mediatek: remove superfluous register reads
In-Reply-To: <1467200291-60106-1-git-send-email-john@phrozen.org>
References: <1467200291-60106-1-git-send-email-john@phrozen.org>
Message-ID: <1467200291-60106-2-git-send-email-john@phrozen.org>

The driver was originally written for MIPS based SoC. These required the
IRQ mask register to be read after writing it to ensure that the content
was actually applied. As this version only works on ARM based SoCs, we can
safely remove the 2 reads as they are no longer required.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |    4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index d1cdc2d..5a3a4e9 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -332,8 +332,6 @@ static inline void mtk_irq_disable(struct mtk_eth *eth, u32 mask)
 
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val & ~mask, MTK_QDMA_INT_MASK);
-	/* flush write */
-	mtk_r32(eth, MTK_QDMA_INT_MASK);
 }
 
 static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
@@ -342,8 +340,6 @@ static inline void mtk_irq_enable(struct mtk_eth *eth, u32 mask)
 
 	val = mtk_r32(eth, MTK_QDMA_INT_MASK);
 	mtk_w32(eth, val | mask, MTK_QDMA_INT_MASK);
-	/* flush write */
-	mtk_r32(eth, MTK_QDMA_INT_MASK);
 }
 
 static int mtk_set_mac_address(struct net_device *dev, void *p)
-- 
1.7.10.4



From john at phrozen.org  Wed Jun 29 04:38:11 2016
From: john at phrozen.org (John Crispin)
Date: Wed, 29 Jun 2016 13:38:11 +0200
Subject: [PATCH V2 4/4] net-next: mediatek: add support for IRQ grouping
In-Reply-To: <1467200291-60106-1-git-send-email-john@phrozen.org>
References: <1467200291-60106-1-git-send-email-john@phrozen.org>
Message-ID: <1467200291-60106-5-git-send-email-john@phrozen.org>

The ethernet core has 3 IRQs. Using the IRQ grouping registers we are able
to separate TX and RX IRQs, which allows us to service them on separate
cores. This patch splits the IRQ handler into 2 separate functions, one for
TX and another for RX. The TX housekeeping is split out into its own NAPI
handler.

Signed-off-by: John Crispin <john at phrozen.org>
---
 drivers/net/ethernet/mediatek/mtk_eth_soc.c |  155 +++++++++++++++++----------
 drivers/net/ethernet/mediatek/mtk_eth_soc.h |   15 ++-
 2 files changed, 111 insertions(+), 59 deletions(-)

diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 698caba..7056a37 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -893,17 +893,17 @@ release_desc:
 	return done;
 }
 
-static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
+static int mtk_poll_tx(struct mtk_eth *eth, int budget)
 {
 	struct mtk_tx_ring *ring = &eth->tx_ring;
 	struct mtk_tx_dma *desc;
 	struct sk_buff *skb;
 	struct mtk_tx_buf *tx_buf;
-	int total = 0, done[MTK_MAX_DEVS];
+	unsigned int done[MTK_MAX_DEVS];
 	unsigned int bytes[MTK_MAX_DEVS];
 	u32 cpu, dma;
 	static int condition;
-	int i;
+	int total = 0, i;
 
 	memset(done, 0, sizeof(done));
 	memset(bytes, 0, sizeof(bytes));
@@ -954,15 +954,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 		total += done[i];
 	}
 
-	/* read hw index again make sure no new tx packet */
-	if (cpu != dma || cpu != mtk_r32(eth, MTK_QTX_DRX_PTR))
-		*tx_again = true;
-	else
-		mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
-
-	if (!total)
-		return 0;
-
 	if (mtk_queue_stopped(eth) &&
 	    (atomic_read(&ring->free_count) > ring->thresh))
 		mtk_wake_queue(eth);
@@ -970,47 +961,75 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget, bool *tx_again)
 	return total;
 }
 
-static int mtk_poll(struct napi_struct *napi, int budget)
+static void mtk_handle_status_irq(struct mtk_eth *eth)
 {
-	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
-	u32 status, status2, mask;
-	int tx_done, rx_done;
-	bool tx_again = false;
-
-	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	status2 = mtk_r32(eth, MTK_INT_STATUS2);
-	tx_done = 0;
-	rx_done = 0;
-	tx_again = 0;
-
-	if (status & MTK_TX_DONE_INT)
-		tx_done = mtk_poll_tx(eth, budget, &tx_again);
-
-	if (status & MTK_RX_DONE_INT)
-		rx_done = mtk_poll_rx(napi, budget, eth);
+	u32 status2 = mtk_r32(eth, MTK_INT_STATUS2);
 
 	if (unlikely(status2 & (MTK_GDM1_AF | MTK_GDM2_AF))) {
 		mtk_stats_update(eth);
 		mtk_w32(eth, (MTK_GDM1_AF | MTK_GDM2_AF),
 			MTK_INT_STATUS2);
 	}
+}
+
+static int mtk_napi_tx(struct napi_struct *napi, int budget)
+{
+	struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi);
+	u32 status, mask;
+	int tx_done = 0;
+
+	mtk_handle_status_irq(eth);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_QMTK_INT_STATUS);
+	tx_done = mtk_poll_tx(eth, budget);
+
+	if (unlikely(netif_msg_intr(eth))) {
+		status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+		mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
+		dev_info(eth->dev,
+			 "done tx %d, intr 0x%08x/0x%x\n",
+			 tx_done, status, mask);
+	}
+
+	if (tx_done == budget)
+		return budget;
+
+	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
+	if (status & MTK_TX_DONE_INT)
+		return budget;
+
+	napi_complete(napi);
+	mtk_irq_enable(eth, MTK_TX_DONE_INT);
+
+	return tx_done;
+}
+
+static int mtk_napi_rx(struct napi_struct *napi, int budget)
+{
+	struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi);
+	u32 status, mask;
+	int rx_done = 0;
+
+	mtk_handle_status_irq(eth);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_QMTK_INT_STATUS);
+	rx_done = mtk_poll_rx(napi, budget, eth);
 
 	if (unlikely(netif_msg_intr(eth))) {
+		status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
 		mask = mtk_r32(eth, MTK_QDMA_INT_MASK);
-		netdev_info(eth->netdev[0],
-			    "done tx %d, rx %d, intr 0x%08x/0x%x\n",
-			    tx_done, rx_done, status, mask);
+		dev_info(eth->dev,
+			 "done rx %d, intr 0x%08x/0x%x\n",
+			 rx_done, status, mask);
 	}
 
-	if (tx_again || rx_done == budget)
+	if (rx_done == budget)
 		return budget;
 
 	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	if (status & (tx_intr | rx_intr))
+	if (status & MTK_RX_DONE_INT)
 		return budget;
 
 	napi_complete(napi);
-	mtk_irq_enable(eth, MTK_RX_DONE_INT | MTK_RX_DONE_INT);
+	mtk_irq_enable(eth, MTK_RX_DONE_INT);
 
 	return rx_done;
 }
@@ -1246,22 +1265,26 @@ static void mtk_tx_timeout(struct net_device *dev)
 	schedule_work(&eth->pending_work);
 }
 
-static irqreturn_t mtk_handle_irq(int irq, void *_eth)
+static irqreturn_t mtk_handle_irq_rx(int irq, void *_eth)
 {
 	struct mtk_eth *eth = _eth;
-	u32 status;
 
-	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
-	if (unlikely(!status))
-		return IRQ_NONE;
+	if (likely(napi_schedule_prep(&eth->rx_napi))) {
+		__napi_schedule(&eth->rx_napi);
+		mtk_irq_disable(eth, MTK_RX_DONE_INT);
+	}
 
-	if (likely(status & (MTK_RX_DONE_INT | MTK_TX_DONE_INT))) {
-		if (likely(napi_schedule_prep(&eth->rx_napi)))
-			__napi_schedule(&eth->rx_napi);
-	} else {
-		mtk_w32(eth, status, MTK_QMTK_INT_STATUS);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_handle_irq_tx(int irq, void *_eth)
+{
+	struct mtk_eth *eth = _eth;
+
+	if (likely(napi_schedule_prep(&eth->tx_napi))) {
+		__napi_schedule(&eth->tx_napi);
+		mtk_irq_disable(eth, MTK_TX_DONE_INT);
 	}
-	mtk_irq_disable(eth, (MTK_RX_DONE_INT | MTK_TX_DONE_INT));
 
 	return IRQ_HANDLED;
 }
@@ -1274,7 +1297,7 @@ static void mtk_poll_controller(struct net_device *dev)
 	u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
 
 	mtk_irq_disable(eth, int_mask);
-	mtk_handle_irq(dev->irq, dev);
+	mtk_handle_irq(dev->irq[0], dev);
 	mtk_irq_enable(eth, int_mask);
 }
 #endif
@@ -1310,6 +1333,7 @@ static int mtk_open(struct net_device *dev)
 		if (err)
 			return err;
 
+		napi_enable(&eth->tx_napi);
 		napi_enable(&eth->rx_napi);
 		mtk_irq_enable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
 	}
@@ -1358,6 +1382,7 @@ static int mtk_stop(struct net_device *dev)
 		return 0;
 
 	mtk_irq_disable(eth, MTK_TX_DONE_INT | MTK_RX_DONE_INT);
+	napi_disable(&eth->tx_napi);
 	napi_disable(&eth->rx_napi);
 
 	mtk_stop_dma(eth, MTK_QDMA_GLO_CFG);
@@ -1395,7 +1420,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 	/* Enable RX VLan Offloading */
 	mtk_w32(eth, 1, MTK_CDMP_EG_CTRL);
 
-	err = devm_request_irq(eth->dev, eth->irq, mtk_handle_irq, 0,
+	err = devm_request_irq(eth->dev, eth->irq[1], mtk_handle_irq_tx, 0,
+			       dev_name(eth->dev), eth);
+	if (err)
+		return err;
+	err = devm_request_irq(eth->dev, eth->irq[2], mtk_handle_irq_rx, 0,
 			       dev_name(eth->dev), eth);
 	if (err)
 		return err;
@@ -1411,7 +1440,11 @@ static int __init mtk_hw_init(struct mtk_eth *eth)
 	mtk_w32(eth, 0, MTK_RST_GL);
 
 	/* FE int grouping */
-	mtk_w32(eth, 0, MTK_FE_INT_GRP);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_PDMA_INT_GRP1);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_PDMA_INT_GRP2);
+	mtk_w32(eth, MTK_TX_DONE_INT, MTK_QDMA_INT_GRP1);
+	mtk_w32(eth, MTK_RX_DONE_INT, MTK_QDMA_INT_GRP2);
+	mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP);
 
 	for (i = 0; i < 2; i++) {
 		u32 val = mtk_r32(eth, MTK_GDMA_FWD_CFG(i));
@@ -1459,7 +1492,9 @@ static void mtk_uninit(struct net_device *dev)
 	phy_disconnect(mac->phy_dev);
 	mtk_mdio_cleanup(eth);
 	mtk_irq_disable(eth, ~0);
-	free_irq(dev->irq, dev);
+	free_irq(eth->irq[0], dev);
+	free_irq(eth->irq[1], dev);
+	free_irq(eth->irq[2], dev);
 }
 
 static int mtk_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
@@ -1733,10 +1768,10 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
 		dev_err(eth->dev, "error bringing up device\n");
 		goto free_netdev;
 	}
-	eth->netdev[id]->irq = eth->irq;
+	eth->netdev[id]->irq = eth->irq[0];
 	netif_info(eth, probe, eth->netdev[id],
 		   "mediatek frame engine at 0x%08lx, irq %d\n",
-		   eth->netdev[id]->base_addr, eth->netdev[id]->irq);
+		   eth->netdev[id]->base_addr, eth->irq[0]);
 
 	return 0;
 
@@ -1753,6 +1788,7 @@ static int mtk_probe(struct platform_device *pdev)
 	struct mtk_soc_data *soc;
 	struct mtk_eth *eth;
 	int err;
+	int i;
 
 	match = of_match_device(of_mtk_match, &pdev->dev);
 	soc = (struct mtk_soc_data *)match->data;
@@ -1788,10 +1824,12 @@ static int mtk_probe(struct platform_device *pdev)
 		return PTR_ERR(eth->rstc);
 	}
 
-	eth->irq = platform_get_irq(pdev, 0);
-	if (eth->irq < 0) {
-		dev_err(&pdev->dev, "no IRQ resource found\n");
-		return -ENXIO;
+	for (i = 0; i < 3; i++) {
+		eth->irq[i] = platform_get_irq(pdev, i);
+		if (eth->irq[i] < 0) {
+			dev_err(&pdev->dev, "no IRQ%d resource found\n", i);
+			return -ENXIO;
+		}
 	}
 
 	eth->clk_ethif = devm_clk_get(&pdev->dev, "ethif");
@@ -1832,7 +1870,9 @@ static int mtk_probe(struct platform_device *pdev)
 	 * for NAPI to work
 	 */
 	init_dummy_netdev(&eth->dummy_dev);
-	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_poll,
+	netif_napi_add(&eth->dummy_dev, &eth->tx_napi, mtk_napi_tx,
+		       MTK_NAPI_WEIGHT);
+	netif_napi_add(&eth->dummy_dev, &eth->rx_napi, mtk_napi_rx,
 		       MTK_NAPI_WEIGHT);
 
 	platform_set_drvdata(pdev, eth);
@@ -1853,6 +1893,7 @@ static int mtk_remove(struct platform_device *pdev)
 	clk_disable_unprepare(eth->clk_gp1);
 	clk_disable_unprepare(eth->clk_gp2);
 
+	netif_napi_del(&eth->tx_napi);
 	netif_napi_del(&eth->rx_napi);
 	mtk_cleanup(eth);
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 3159d2a..f82e3ac 100644
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -68,6 +68,10 @@
 /* Unicast Filter MAC Address Register - High */
 #define MTK_GDMA_MAC_ADRH(x)	(0x50C + (x * 0x1000))
 
+/* PDMA Interrupt grouping registers */
+#define MTK_PDMA_INT_GRP1	0xa50
+#define MTK_PDMA_INT_GRP2	0xa54
+
 /* QDMA TX Queue Configuration Registers */
 #define MTK_QTX_CFG(x)		(0x1800 + (x * 0x10))
 #define QDMA_RES_THRES		4
@@ -125,6 +129,11 @@
 #define MTK_TX_DONE_INT		(MTK_TX_DONE_INT0 | MTK_TX_DONE_INT1 | \
 				 MTK_TX_DONE_INT2 | MTK_TX_DONE_INT3)
 
+/* QDMA Interrupt grouping registers */
+#define MTK_QDMA_INT_GRP1	0x1a20
+#define MTK_QDMA_INT_GRP2	0x1a24
+#define MTK_RLS_DONE_INT	BIT(0)
+
 /* QDMA Interrupt Status Register */
 #define MTK_QDMA_INT_MASK	0x1A1C
 
@@ -356,7 +365,8 @@ struct mtk_rx_ring {
  * @dma_refcnt:		track how many netdevs are using the DMA engine
  * @tx_ring:		Pointer to the memore holding info about the TX ring
  * @rx_ring:		Pointer to the memore holding info about the RX ring
- * @rx_napi:		The NAPI struct
+ * @tx_napi:		The TX NAPI struct
+ * @rx_napi:		The RX NAPI struct
  * @scratch_ring:	Newer SoCs need memory for a second HW managed TX ring
  * @phy_scratch_ring:	physical address of scratch_ring
  * @scratch_head:	The scratch memory that scratch_ring points to.
@@ -377,7 +387,7 @@ struct mtk_eth {
 	struct net_device		dummy_dev;
 	struct net_device		*netdev[MTK_MAX_DEVS];
 	struct mtk_mac			*mac[MTK_MAX_DEVS];
-	int				irq;
+	int				irq[3];
 	u32				msg_enable;
 	unsigned long			sysclk;
 	struct regmap			*ethsys;
@@ -385,6 +395,7 @@ struct mtk_eth {
 	atomic_t			dma_refcnt;
 	struct mtk_tx_ring		tx_ring;
 	struct mtk_rx_ring		rx_ring;
+	struct napi_struct		tx_napi;
 	struct napi_struct		rx_napi;
 	struct mtk_tx_dma		*scratch_ring;
 	dma_addr_t			phy_scratch_ring;
-- 
1.7.10.4



From tj at kernel.org  Wed Jun 29 06:22:01 2016
From: tj at kernel.org (Tejun Heo)
Date: Wed, 29 Jun 2016 09:22:01 -0400
Subject: [PATCH] PM / suspend: show workqueues busy name in suspend flow
In-Reply-To: <1467172451.4141.20.camel@mtksdaap41>
References: <1466586509-32400-1-git-send-email-roger.lu@mediatek.com>
 <20160622152126.GV3262@mtj.duckdns.org>
 <1467020645.25092.24.camel@mtksdaap41>
 <20160628165616.GD5185@htj.duckdns.org>
 <1467172451.4141.20.camel@mtksdaap41>
Message-ID: <20160629132201.GC24054@htj.duckdns.org>

Hello, Roger.

On Wed, Jun 29, 2016 at 11:54:11AM +0800, Roger Lu wrote:
> Please allow me to elaborate my previous concern about printing
> freezable workqueue info only in this case.
> 
> The benefit of it is that debugger can quickly understand which
> freezable workqueues block suspend flow and assign this issue to
> corresponding owner instead of extracting freezable workqueue info from
> show_workqueue_state() first and, then, assigning the issue.

I don't think it matters.  At that point, workqueues are generally
pretty idle anyway and it shouldn't be difficult to tell which work
items are the offending ones.  Besides, freezable and unfreezable
workqueues share the same backend pools, so it isn't easily separable
either.

Thanks.

-- 
tejun


From chaotian.jing at mediatek.com  Wed Jun 29 19:01:00 2016
From: chaotian.jing at mediatek.com (Chaotian Jing)
Date: Thu, 30 Jun 2016 10:01:00 +0800
Subject: [PATCH 3/4] mmc: mediatek: fix CMD21/CMD19 timeout issue
In-Reply-To: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
References: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <1467252061-3791-4-git-send-email-chaotian.jing@mediatek.com>

we did not deal with the read data of CMD21/CMD19 if there is
response CRC error of CMD21/CMD19, in this case, eMMC/SD may
still in send-data state. therefore, all of next commands cannot
get response as device is not in transfer state.

for resolving this issue, still need deal with the data receive
to make device back to transfer state.

Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>
---
 drivers/mmc/host/mtk-sd.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 4b175a6..91277b9 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -801,7 +801,13 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
 	}
 
 	if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
-		msdc_reset_hw(host);
+		if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
+		    cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)
+			/*
+			 * should not clear fifo/interrupt as the tune data
+			 * may have alreay come.
+			 */
+			msdc_reset_hw(host);
 		if (events & MSDC_INT_RSPCRCERR) {
 			cmd->error = -EILSEQ;
 			host->error |= REQ_CMD_EIO;
@@ -885,7 +891,11 @@ static void msdc_start_command(struct msdc_host *host,
 static void msdc_cmd_next(struct msdc_host *host,
 		struct mmc_request *mrq, struct mmc_command *cmd)
 {
-	if (cmd->error || (mrq->sbc && mrq->sbc->error))
+	if ((cmd->error &&
+	    !(cmd->error == -EILSEQ &&
+	      (cmd->opcode == MMC_SEND_TUNING_BLOCK ||
+	       cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))) ||
+	    (mrq->sbc && mrq->sbc->error))
 		msdc_request_done(host, mrq);
 	else if (cmd == mrq->sbc)
 		msdc_start_command(host, mrq, mrq->cmd);
-- 
1.8.1.1.dirty



From chaotian.jing at mediatek.com  Wed Jun 29 19:01:01 2016
From: chaotian.jing at mediatek.com (Chaotian Jing)
Date: Thu, 30 Jun 2016 10:01:01 +0800
Subject: [PATCH 4/4] mmc: mediatek: perfer to use rise edge latching
In-Reply-To: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
References: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <1467252061-3791-5-git-send-email-chaotian.jing@mediatek.com>

in our host design, rise edge latching is more stable than fall edge
latching. so that if rise edge has enough margin, no need scan fall edge.

Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>
---
 drivers/mmc/host/mtk-sd.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 91277b9..84e9afc 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1328,7 +1328,7 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
 {
 	struct msdc_host *host = mmc_priv(mmc);
 	u32 rise_delay = 0, fall_delay = 0;
-	struct msdc_delay_phase final_rise_delay, final_fall_delay;
+	struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
 	u8 final_delay, final_maxlen;
 	int cmd_err;
 	int i;
@@ -1341,6 +1341,11 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
 		if (!cmd_err)
 			rise_delay |= (1 << i);
 	}
+	final_rise_delay = get_best_delay(host, rise_delay);
+	/* if rising edge has enough margin, then do not scan falling edge */
+	if (final_rise_delay.maxlen >= 10 ||
+	    (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+		goto skip_fall;
 
 	sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
 	for (i = 0; i < PAD_DELAY_MAX; i++) {
@@ -1350,10 +1355,9 @@ static int msdc_tune_response(struct mmc_host *mmc, u32 opcode)
 		if (!cmd_err)
 			fall_delay |= (1 << i);
 	}
-
-	final_rise_delay = get_best_delay(host, rise_delay);
 	final_fall_delay = get_best_delay(host, fall_delay);
 
+skip_fall:
 	final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
 	if (final_maxlen == final_rise_delay.maxlen) {
 		sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_RSPL);
@@ -1374,7 +1378,7 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
 {
 	struct msdc_host *host = mmc_priv(mmc);
 	u32 rise_delay = 0, fall_delay = 0;
-	struct msdc_delay_phase final_rise_delay, final_fall_delay;
+	struct msdc_delay_phase final_rise_delay, final_fall_delay = { 0,};
 	u8 final_delay, final_maxlen;
 	int i, ret;
 
@@ -1387,6 +1391,11 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
 		if (!ret)
 			rise_delay |= (1 << i);
 	}
+	final_rise_delay = get_best_delay(host, rise_delay);
+	/* if rising edge has enough margin, then do not scan falling edge */
+	if (final_rise_delay.maxlen >= 10 ||
+	    (final_rise_delay.start == 0 && final_rise_delay.maxlen >= 4))
+		goto skip_fall;
 
 	sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
 	sdr_set_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
@@ -1397,14 +1406,10 @@ static int msdc_tune_data(struct mmc_host *mmc, u32 opcode)
 		if (!ret)
 			fall_delay |= (1 << i);
 	}
-
-	final_rise_delay = get_best_delay(host, rise_delay);
 	final_fall_delay = get_best_delay(host, fall_delay);
 
+skip_fall:
 	final_maxlen = max(final_rise_delay.maxlen, final_fall_delay.maxlen);
-	/* Rising edge is more stable, prefer to use it */
-	if (final_rise_delay.maxlen >= 10)
-		final_maxlen = final_rise_delay.maxlen;
 	if (final_maxlen == final_rise_delay.maxlen) {
 		sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_DSPL);
 		sdr_clr_bits(host->base + MSDC_IOCON, MSDC_IOCON_W_DSPL);
-- 
1.8.1.1.dirty



From chaotian.jing at mediatek.com  Wed Jun 29 19:00:59 2016
From: chaotian.jing at mediatek.com (Chaotian Jing)
Date: Thu, 30 Jun 2016 10:00:59 +0800
Subject: [PATCH 2/4] mmc: mediatek: fix CRC error when calling
 mmc_select_hs400()
In-Reply-To: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
References: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <1467252061-3791-3-git-send-email-chaotian.jing@mediatek.com>

the tune result of hs200 mode at 200Mhz is not suitable for 50Mhz,
mmc_select_hs400() will set clock frequency to 50Mhz, use defalut
tune setting for 50Mhz to avoid CRC error.

Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>
---
 drivers/mmc/host/mtk-sd.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 4d7ce65..4b175a6 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -287,6 +287,11 @@ struct msdc_save_para {
 	u32 emmc50_cfg0;
 };
 
+struct msdc_tune_para {
+	u32 iocon;
+	u32 pad_tune;
+};
+
 struct msdc_delay_phase {
 	u8 maxlen;
 	u8 start;
@@ -328,6 +333,8 @@ struct msdc_host {
 	u32 hs400_ds_delay;
 	bool hs400_mode;	/* current eMMC will run at hs400 mode */
 	struct msdc_save_para save_para; /* used when gate HCLK */
+	struct msdc_tune_para def_tune_para; /* default tune setting */
+	struct msdc_tune_para saved_tune_para; /* tune result of CMD21/CMD19 */
 };
 
 static void sdr_set_bits(void __iomem *reg, u32 bs)
@@ -583,6 +590,18 @@ static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz)
 	msdc_set_timeout(host, host->timeout_ns, host->timeout_clks);
 	sdr_set_bits(host->base + MSDC_INTEN, flags);
 
+	/*
+	 * mmc_select_hs400() will drop to 50Mhz and High speed mode,
+	 * tune result of hs200/200Mhz is not suitable for 50Mhz
+	 */
+	if (host->sclk <= 52000000) {
+		writel(host->def_tune_para.iocon, host->base + MSDC_IOCON);
+		writel(host->def_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
+	} else {
+		writel(host->saved_tune_para.iocon, host->base + MSDC_IOCON);
+		writel(host->saved_tune_para.pad_tune, host->base + MSDC_PAD_TUNE);
+	}
+
 	dev_dbg(host->dev, "sclk: %d, timing: %d\n", host->sclk, timing);
 }
 
@@ -1159,6 +1178,8 @@ static void msdc_init_hw(struct msdc_host *host)
 	/* Configure to default data timeout */
 	sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3);
 
+	host->def_tune_para.iocon = readl(host->base + MSDC_IOCON);
+	host->def_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
 	dev_dbg(host->dev, "init hardware done!");
 }
 
@@ -1409,6 +1430,8 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
 			dev_err(host->dev, "Tune data fail!\n");
 	}
 
+	host->saved_tune_para.iocon = readl(host->base + MSDC_IOCON);
+	host->saved_tune_para.pad_tune = readl(host->base + MSDC_PAD_TUNE);
 	return ret;
 }
 
-- 
1.8.1.1.dirty



From chaotian.jing at mediatek.com  Wed Jun 29 19:00:58 2016
From: chaotian.jing at mediatek.com (Chaotian Jing)
Date: Thu, 30 Jun 2016 10:00:58 +0800
Subject: [PATCH 1/4] mmc: mediatek: do not tune data for HS400 mode
In-Reply-To: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
References: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>
Message-ID: <1467252061-3791-2-git-send-email-chaotian.jing@mediatek.com>

for hs400 mode, should only tune DS delay, should not
tune PAD_TUNE for data path.
if eMMC will run at hs400 mode, do not tune data while
call ops->execute_tuning().

Signed-off-by: Chaotian Jing <chaotian.jing at mediatek.com>
---
 drivers/mmc/host/mtk-sd.c | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 5642f71..4d7ce65 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -326,6 +326,7 @@ struct msdc_host {
 	unsigned char timing;
 	bool vqmmc_enabled;
 	u32 hs400_ds_delay;
+	bool hs400_mode;	/* current eMMC will run at hs400 mode */
 	struct msdc_save_para save_para; /* used when gate HCLK */
 };
 
@@ -1402,9 +1403,11 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
 		dev_err(host->dev, "Tune response fail!\n");
 		return ret;
 	}
-	ret = msdc_tune_data(mmc, opcode);
-	if (ret == -EIO)
-		dev_err(host->dev, "Tune data fail!\n");
+	if (host->hs400_mode == false) {
+		ret = msdc_tune_data(mmc, opcode);
+		if (ret == -EIO)
+			dev_err(host->dev, "Tune data fail!\n");
+	}
 
 	return ret;
 }
@@ -1412,6 +1415,7 @@ static int msdc_execute_tuning(struct mmc_host *mmc, u32 opcode)
 static int msdc_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct msdc_host *host = mmc_priv(mmc);
+	host->hs400_mode = true;
 
 	writel(host->hs400_ds_delay, host->base + PAD_DS_TUNE);
 	return 0;
-- 
1.8.1.1.dirty



From chaotian.jing at mediatek.com  Wed Jun 29 19:00:57 2016
From: chaotian.jing at mediatek.com (Chaotian Jing)
Date: Thu, 30 Jun 2016 10:00:57 +0800
Subject: fix some host driver defect
Message-ID: <1467252061-3791-1-git-send-email-chaotian.jing@mediatek.com>


Chaotian Jing (4):
  mmc: mediatek: do not tune data for HS400 mode
  mmc: mediatek: fix CRC error when calling mmc_select_hs400()
  mmc: mediatek: fix CMD21/CMD19 timeout issue
  mmc: mediatek: perfer to use rise edge latching

 drivers/mmc/host/mtk-sd.c | 70 +++++++++++++++++++++++++++++++++++++----------
 1 file changed, 56 insertions(+), 14 deletions(-)

-- 
1.8.1.1.dirty



From roger.lu at mediatek.com  Wed Jun 29 20:17:46 2016
From: roger.lu at mediatek.com (Roger Lu)
Date: Thu, 30 Jun 2016 11:17:46 +0800
Subject: [PATCH] PM / suspend: show workqueue state in suspend flow
Message-ID: <1467256666-27808-1-git-send-email-roger.lu@mediatek.com>

If freezable workqueue aborts suspend flow, show
workqueue state for debug purpose.

Signed-off-by: Roger Lu <roger.lu at mediatek.com>
---
 kernel/power/process.c | 3 +++
 kernel/workqueue.c     | 7 +++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/kernel/power/process.c b/kernel/power/process.c
index df058be..6eef250 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -89,6 +89,9 @@ static int try_to_freeze_tasks(bool user_only)
 		       elapsed_msecs / 1000, elapsed_msecs % 1000,
 		       todo - wq_busy, wq_busy);
 
+		if (wq_busy)
+			show_workqueue_state();
+
 		if (!wakeup) {
 			read_lock(&tasklist_lock);
 			for_each_process_thread(g, p) {
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index e1c0e99..c945617 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4369,8 +4369,8 @@ static void show_pwq(struct pool_workqueue *pwq)
 /**
  * show_workqueue_state - dump workqueue state
  *
- * Called from a sysrq handler and prints out all busy workqueues and
- * pools.
+ * Called from a sysrq handler or try_to_freeze_tasks() and prints out
+ * all busy workqueues and pools.
  */
 void show_workqueue_state(void)
 {
@@ -4396,6 +4396,9 @@ void show_workqueue_state(void)
 		if (idle)
 			continue;
 
+		if (wq->flags & WQ_FREEZABLE)
+			pr_info("freezable ");
+
 		pr_info("workqueue %s: flags=0x%x\n", wq->name, wq->flags);
 
 		for_each_pwq(pwq, wq) {
-- 
2.6.4



From lkp at intel.com  Wed Jun 29 22:04:30 2016
From: lkp at intel.com (kbuild test robot)
Date: Thu, 30 Jun 2016 13:04:30 +0800
Subject: [PATCH V2 4/4] net-next: mediatek: add support for IRQ grouping
In-Reply-To: <1467200291-60106-5-git-send-email-john@phrozen.org>
Message-ID: <201606301332.bixswLIk%fengguang.wu@intel.com>

Hi,

[auto build test ERROR on net/master]
[also build test ERROR on next-20160629]
[cannot apply to net-next/master v4.7-rc5]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/John-Crispin/net-next-mediatek-IRQ-cleanups-fixes-and-grouping/20160629-194341
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 5.3.1-8) 5.3.1 20160205
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 errors (new ones prefixed by >>):

   drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_poll_controller':
>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1300:2: error: implicit declaration of function 'mtk_handle_irq' [-Werror=implicit-function-declaration]
     mtk_handle_irq(dev->irq[0], dev);
     ^
>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1300:25: error: subscripted value is neither array nor pointer nor vector
     mtk_handle_irq(dev->irq[0], dev);
                            ^
   cc1: some warnings being treated as errors

vim +/mtk_handle_irq +1300 drivers/net/ethernet/mediatek/mtk_eth_soc.c

  1294	{
  1295		struct mtk_mac *mac = netdev_priv(dev);
  1296		struct mtk_eth *eth = mac->hw;
  1297		u32 int_mask = MTK_TX_DONE_INT | MTK_RX_DONE_INT;
  1298	
  1299		mtk_irq_disable(eth, int_mask);
> 1300		mtk_handle_irq(dev->irq[0], dev);
  1301		mtk_irq_enable(eth, int_mask);
  1302	}
  1303	#endif

---
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: 57589 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160630/c6eed448/attachment-0001.obj>

From lkp at intel.com  Wed Jun 29 23:03:52 2016
From: lkp at intel.com (kbuild test robot)
Date: Thu, 30 Jun 2016 14:03:52 +0800
Subject: [PATCH V2 2/4] net-next: mediatek: don't use intermediate
 variables to store IRQ masks
In-Reply-To: <1467200291-60106-3-git-send-email-john@phrozen.org>
Message-ID: <201606301404.EoyOs9Sz%fengguang.wu@intel.com>

Hi,

[auto build test ERROR on net/master]
[also build test ERROR on v4.7-rc5 next-20160629]
[cannot apply to net-next/master]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/John-Crispin/net-next-mediatek-IRQ-cleanups-fixes-and-grouping/20160629-194341
config: arm-allmodconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 5.3.1-8) 5.3.1 20160205
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 

Note: the linux-review/John-Crispin/net-next-mediatek-IRQ-cleanups-fixes-and-grouping/20160629-194341 HEAD e8462c612b33003fba7c1a70328c7fb7872d8507 builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_poll':
>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:16: error: 'tx_intr' undeclared (first use in this function)
     if (status & (tx_intr | rx_intr))
                   ^
   drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:16: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:26: error: 'rx_intr' undeclared (first use in this function)
     if (status & (tx_intr | rx_intr))
                             ^

vim +/tx_intr +1003 drivers/net/ethernet/mediatek/mtk_eth_soc.c

656e7052 John Crispin 2016-03-08   997  	}
656e7052 John Crispin 2016-03-08   998  
656e7052 John Crispin 2016-03-08   999  	if (tx_again || rx_done == budget)
656e7052 John Crispin 2016-03-08  1000  		return budget;
656e7052 John Crispin 2016-03-08  1001  
656e7052 John Crispin 2016-03-08  1002  	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
656e7052 John Crispin 2016-03-08 @1003  	if (status & (tx_intr | rx_intr))
656e7052 John Crispin 2016-03-08  1004  		return budget;
656e7052 John Crispin 2016-03-08  1005  
656e7052 John Crispin 2016-03-08  1006  	napi_complete(napi);

:::::: The code at line 1003 was first introduced by commit
:::::: 656e705243fd0c2864b89634ea16ed444ef64dc6 net-next: mediatek: add support for MT7623 ethernet

:::::: TO: John Crispin <blogic at openwrt.org>
:::::: CC: David S. Miller <davem at davemloft.net>

---
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: 57589 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-mediatek/attachments/20160630/6291bee9/attachment-0001.obj>

From hs.liao at mediatek.com  Thu Jun 30 00:14:39 2016
From: hs.liao at mediatek.com (HS Liao)
Date: Thu, 30 Jun 2016 15:14:39 +0800
Subject: [PATCH v9 0/4] Mediatek MT8173 CMDQ support
Message-ID: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>


Hi,

This is Mediatek MT8173 Command Queue(CMDQ) driver. The CMDQ is used
to help write registers with critical time limitation, such as
updating display configuration during the vblank. It controls Global
Command Engine (GCE) hardware to achieve this requirement.

These patches have a build dependency on top of v4.7-rc1.

Changes since v8:
 - use mailbox framework, and merge options for GCE thread into
   mailbox channel
 - use timer to handle timeout case
 - rename cmdq_handle_error_done to cmdq_thread_irq_handler, handle
   error by cmdq_task_handle_error, and handle timeout by
   cmdq_thread_handle_timeout
 - use bitops
 - fix bug of treating working task as done task
 - move GCE hardware events' mapping from header to driver
 - remove code about previous tasks since we should keep tasks' order
   for display, and it is impossible to have previous tasks
 - centralize code to handle executed tasks in cmdq_task_exec_done
 - use u64* instead of u32* for GCE command pointer
 - drop cmdq_thread_readl/writel
 - depends on ARM64
 - refine some code accroding to comments for v8

Best regards,
HS Liao

HS Liao (4):
  dt-bindings: soc: Add documentation for the MediaTek GCE unit
  CMDQ: Mediatek CMDQ driver
  arm64: dts: mt8173: Add GCE node
  CMDQ: save more energy in idle

 .../devicetree/bindings/soc/mediatek/gce.txt       |  44 +
 arch/arm64/boot/dts/mediatek/mt8173.dtsi           |  10 +
 drivers/soc/mediatek/Kconfig                       |  11 +
 drivers/soc/mediatek/Makefile                      |   1 +
 drivers/soc/mediatek/mtk-cmdq.c                    | 967 +++++++++++++++++++++
 include/soc/mediatek/cmdq.h                        | 192 ++++
 6 files changed, 1225 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/gce.txt
 create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
 create mode 100644 include/soc/mediatek/cmdq.h

-- 
1.9.1



From hs.liao at mediatek.com  Thu Jun 30 00:14:40 2016
From: hs.liao at mediatek.com (HS Liao)
Date: Thu, 30 Jun 2016 15:14:40 +0800
Subject: [PATCH v9 1/4] dt-bindings: soc: Add documentation for the MediaTek
 GCE unit
In-Reply-To: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
References: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
Message-ID: <1467270883-1407-2-git-send-email-hs.liao@mediatek.com>

This adds documentation for the MediaTek Global Command Engine (GCE) unit
found in MT8173 SoCs.

Signed-off-by: HS Liao <hs.liao at mediatek.com>
Acked-by: Rob Herring <robh at kernel.org>
---
 .../devicetree/bindings/soc/mediatek/gce.txt       | 44 ++++++++++++++++++++++
 1 file changed, 44 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/mediatek/gce.txt

diff --git a/Documentation/devicetree/bindings/soc/mediatek/gce.txt b/Documentation/devicetree/bindings/soc/mediatek/gce.txt
new file mode 100644
index 0000000..63c5364
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/mediatek/gce.txt
@@ -0,0 +1,44 @@
+MediaTek GCE
+===============
+
+The Global Command Engine (GCE) is used to help read/write registers with
+critical time limitation, such as updating display configuration during the
+vblank. The GCE can be used to implement the Command Queue (CMDQ) driver.
+
+CMDQ driver uses mailbox framework for communication. Please refer to
+../../mailbox/mailbox.txt for generic information about mailbox device-tree
+bindings.
+
+Required properties:
+- compatible: Must be "mediatek,mt8173-gce"
+- reg: Address range of the GCE unit
+- interrupts: The interrupt signal from the GCE block
+- clock: Clocks according to the common clock binding
+- clock-names: Must be "gce" to stand for GCE clock
+- #mbox-cells: Should be 1
+
+Required properties for a client device:
+- mediatek,gce: Should point to the respective GCE block
+- mboxes: List of phandle, mailbox channel specifiers, and merging wfe flag.
+
+Example:
+
+	gce: gce at 10212000 {
+		compatible = "mediatek,mt8173-gce";
+		reg = <0 0x10212000 0 0x1000>;
+		interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_GCE>;
+		clock-names = "gce";
+
+		#mbox-cells = <1>;
+	};
+
+Example for a client device:
+
+	mmsys: clock-controller at 14000000 {
+		compatible = "mediatek,mt8173-mmsys";
+		mediatek,gce = <&gce>;
+		mboxes = <&gce 0 1 /* main display with merging wfe */
+			  &gce 1 1>; /* sub display with merging wfe */
+		...
+	};
-- 
1.9.1



From hs.liao at mediatek.com  Thu Jun 30 00:14:41 2016
From: hs.liao at mediatek.com (HS Liao)
Date: Thu, 30 Jun 2016 15:14:41 +0800
Subject: [PATCH v9 2/4] CMDQ: Mediatek CMDQ driver
In-Reply-To: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
References: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
Message-ID: <1467270883-1407-3-git-send-email-hs.liao@mediatek.com>

This patch is first version of Mediatek Command Queue(CMDQ) driver. The
CMDQ is used to help write registers with critical time limitation,
such as updating display configuration during the vblank. It controls
Global Command Engine (GCE) hardware to achieve this requirement.
Currently, CMDQ only supports display related hardwares, but we expect
it can be extended to other hardwares for future requirements.

Signed-off-by: HS Liao <hs.liao at mediatek.com>
Signed-off-by: CK Hu <ck.hu at mediatek.com>
---
 drivers/soc/mediatek/Kconfig    |  11 +
 drivers/soc/mediatek/Makefile   |   1 +
 drivers/soc/mediatek/mtk-cmdq.c | 928 ++++++++++++++++++++++++++++++++++++++++
 include/soc/mediatek/cmdq.h     | 192 +++++++++
 4 files changed, 1132 insertions(+)
 create mode 100644 drivers/soc/mediatek/mtk-cmdq.c
 create mode 100644 include/soc/mediatek/cmdq.h

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index 0a4ea80..50869e4 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -1,6 +1,17 @@
 #
 # MediaTek SoC drivers
 #
+config MTK_CMDQ
+	bool "MediaTek CMDQ Support"
+	depends on ARM64 && ( ARCH_MEDIATEK || COMPILE_TEST )
+	select MAILBOX
+	select MTK_INFRACFG
+	help
+	  Say yes here to add support for the MediaTek Command Queue (CMDQ)
+	  driver. The CMDQ is used to help read/write registers with critical
+	  time limitation, such as updating display configuration during the
+	  vblank.
+
 config MTK_INFRACFG
 	bool "MediaTek INFRACFG Support"
 	depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 12998b0..f7397ef 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MTK_CMDQ) += mtk-cmdq.o
 obj-$(CONFIG_MTK_INFRACFG) += mtk-infracfg.o
 obj-$(CONFIG_MTK_PMIC_WRAP) += mtk-pmic-wrap.o
 obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
new file mode 100644
index 0000000..6a6c4c3
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-cmdq.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/completion.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/suspend.h>
+#include <linux/timer.h>
+#include <soc/mediatek/cmdq.h>
+
+#define CMDQ_THR_MAX_COUNT		3 /* main, sub, general(misc) */
+#define CMDQ_INST_SIZE			8 /* instruction is 64-bit */
+#define CMDQ_TIMEOUT_MS			1000
+#define CMDQ_IRQ_MASK			0xffff
+#define CMDQ_NUM_CMD(t)			(t->cmd_buf_size / CMDQ_INST_SIZE)
+
+#define CMDQ_CURR_IRQ_STATUS		0x10
+#define CMDQ_THR_SLOT_CYCLES		0x30
+
+#define CMDQ_THR_BASE			0x100
+#define CMDQ_THR_SIZE			0x80
+#define CMDQ_THR_WARM_RESET		0x00
+#define CMDQ_THR_ENABLE_TASK		0x04
+#define CMDQ_THR_SUSPEND_TASK		0x08
+#define CMDQ_THR_CURR_STATUS		0x0c
+#define CMDQ_THR_IRQ_STATUS		0x10
+#define CMDQ_THR_IRQ_ENABLE		0x14
+#define CMDQ_THR_CURR_ADDR		0x20
+#define CMDQ_THR_END_ADDR		0x24
+
+#define CMDQ_THR_ENABLED		0x1
+#define CMDQ_THR_DISABLED		0x0
+#define CMDQ_THR_SUSPEND		0x1
+#define CMDQ_THR_RESUME			0x0
+#define CMDQ_THR_STATUS_SUSPENDED	BIT(1)
+#define CMDQ_THR_DO_WARM_RESET		BIT(0)
+#define CMDQ_THR_ACTIVE_SLOT_CYCLES	0x3200
+#define CMDQ_THR_IRQ_DONE		0x1
+#define CMDQ_THR_IRQ_ERROR		0x12
+#define CMDQ_THR_IRQ_EN			(CMDQ_THR_IRQ_ERROR | CMDQ_THR_IRQ_DONE)
+
+#define CMDQ_OP_CODE_SHIFT		24
+#define CMDQ_SUBSYS_SHIFT		16
+
+#define CMDQ_ARG_A_WRITE_MASK		0xffff
+#define CMDQ_OP_CODE_MASK		(0xff << CMDQ_OP_CODE_SHIFT)
+
+#define CMDQ_WRITE_ENABLE_MASK		BIT(0)
+#define CMDQ_JUMP_BY_OFFSET		0x10000000
+#define CMDQ_JUMP_BY_PA			0x10000001
+#define CMDQ_JUMP_PASS			CMDQ_INST_SIZE
+#define CMDQ_WFE_UPDATE			BIT(31)
+#define CMDQ_WFE_WAIT			BIT(15)
+#define CMDQ_WFE_WAIT_VALUE		0x1
+#define CMDQ_EOC_IRQ_EN			BIT(0)
+
+/*
+ * CMDQ_CODE_MASK:
+ *   set write mask
+ *   format: op mask
+ * CMDQ_CODE_WRITE:
+ *   write value into target register
+ *   format: op subsys address value
+ * CMDQ_CODE_JUMP:
+ *   jump by offset
+ *   format: op offset
+ * CMDQ_CODE_WFE:
+ *   wait for event and clear
+ *   it is just clear if no wait
+ *   format: [wait]  op event update:1 to_wait:1 wait:1
+ *           [clear] op event update:1 to_wait:0 wait:0
+ * CMDQ_CODE_EOC:
+ *   end of command
+ *   format: op irq_flag
+ */
+enum cmdq_code {
+	CMDQ_CODE_MASK = 0x02,
+	CMDQ_CODE_WRITE = 0x04,
+	CMDQ_CODE_JUMP = 0x10,
+	CMDQ_CODE_WFE = 0x20,
+	CMDQ_CODE_EOC = 0x40,
+};
+
+struct cmdq_task_cb {
+	cmdq_async_flush_cb	cb;
+	void			*data;
+};
+
+struct cmdq_thread {
+	struct mbox_chan	*chan;
+	void __iomem		*base;
+	struct list_head	task_busy_list;
+	struct timer_list	timeout;
+	bool			merge_wfe;
+};
+
+struct cmdq_task {
+	struct cmdq		*cmdq;
+	struct list_head	list_entry;
+	void			*va_base;
+	dma_addr_t		pa_base;
+	size_t			cmd_buf_size;
+	struct cmdq_thread	*thread;
+	struct cmdq_task_cb	cb;
+};
+
+struct cmdq {
+	struct mbox_controller	mbox;
+	void __iomem		*base;
+	u32			irq;
+	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
+	struct mutex		task_mutex;
+	struct clk		*clock;
+	bool			suspended;
+};
+
+struct cmdq_subsys {
+	u32	base;
+	int	id;
+};
+
+static const struct cmdq_subsys gce_subsys[] = {
+	{0x1400, 1},
+	{0x1401, 2},
+	{0x1402, 3},
+};
+
+static int cmdq_subsys_base_to_id(u32 base)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(gce_subsys); i++)
+		if (gce_subsys[i].base == base)
+			return gce_subsys[i].id;
+	return -EFAULT;
+}
+
+static struct cmdq_task *cmdq_task_acquire(struct cmdq_rec *rec,
+					   struct cmdq_task_cb cb)
+{
+	struct cmdq *cmdq = rec->cmdq;
+	struct device *dev = cmdq->mbox.dev;
+	struct cmdq_task *task;
+
+	task = kzalloc(sizeof(*task), GFP_KERNEL);
+	INIT_LIST_HEAD(&task->list_entry);
+	task->va_base = kmalloc(rec->cmd_buf_size, GFP_KERNEL);
+	if (!task->va_base) {
+		dev_err(dev, "allocate command buffer failed\n");
+		kfree(task);
+		return NULL;
+	}
+
+	task->cmdq = cmdq;
+	task->cmd_buf_size = rec->cmd_buf_size;
+	task->cb = cb;
+	memcpy(task->va_base, rec->buf, rec->cmd_buf_size);
+	task->pa_base = dma_map_single(dev, task->va_base, rec->cmd_buf_size,
+				       DMA_TO_DEVICE);
+	return task;
+}
+
+static int cmdq_thread_suspend(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	u32 status;
+
+	writel(CMDQ_THR_SUSPEND, thread->base + CMDQ_THR_SUSPEND_TASK);
+
+	/* If already disabled, treat as suspended successful. */
+	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+		return 0;
+
+	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_CURR_STATUS,
+			status, status & CMDQ_THR_STATUS_SUSPENDED, 0, 10)) {
+		dev_err(cmdq->mbox.dev, "suspend GCE thread 0x%x failed\n",
+			(u32)(thread->base - cmdq->base));
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static void cmdq_thread_resume(struct cmdq_thread *thread)
+{
+	writel(CMDQ_THR_RESUME, thread->base + CMDQ_THR_SUSPEND_TASK);
+}
+
+static int cmdq_thread_reset(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	u32 warm_reset;
+
+	writel(CMDQ_THR_DO_WARM_RESET, thread->base + CMDQ_THR_WARM_RESET);
+	if (readl_poll_timeout_atomic(thread->base + CMDQ_THR_WARM_RESET,
+			warm_reset, !(warm_reset & CMDQ_THR_DO_WARM_RESET),
+			0, 10)) {
+		dev_err(cmdq->mbox.dev, "reset GCE thread 0x%x failed\n",
+			(u32)(thread->base - cmdq->base));
+		return -EFAULT;
+	}
+	writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
+	return 0;
+}
+
+static void cmdq_thread_disable(struct cmdq *cmdq, struct cmdq_thread *thread)
+{
+	cmdq_thread_reset(cmdq, thread);
+	writel(CMDQ_THR_DISABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+}
+
+/* notify GCE to re-fetch commands by setting GCE thread PC */
+static void cmdq_thread_invalidate_fetched_data(struct cmdq_thread *thread)
+{
+	writel(readl(thread->base + CMDQ_THR_CURR_ADDR),
+	       thread->base + CMDQ_THR_CURR_ADDR);
+}
+
+static void cmdq_task_insert_into_thread(struct cmdq_task *task)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	struct cmdq_thread *thread = task->thread;
+	struct cmdq_task *prev_task = list_last_entry(
+			&thread->task_busy_list, typeof(*task), list_entry);
+	u64 *prev_task_base = prev_task->va_base;
+
+	/* let previous task jump to this task */
+	dma_sync_single_for_cpu(dev, prev_task->pa_base,
+				prev_task->cmd_buf_size, DMA_TO_DEVICE);
+	prev_task_base[CMDQ_NUM_CMD(prev_task) - 1] =
+		(u64)CMDQ_JUMP_BY_PA << 32 | task->pa_base;
+	dma_sync_single_for_device(dev, prev_task->pa_base,
+				   prev_task->cmd_buf_size, DMA_TO_DEVICE);
+
+	cmdq_thread_invalidate_fetched_data(thread);
+}
+
+/* we assume tasks in the same display GCE thread are waiting the same event. */
+static void cmdq_task_remove_wfe(struct cmdq_task *task)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	u64 wfe_option = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+	u64 wfe_op = (u64)(CMDQ_CODE_WFE << CMDQ_OP_CODE_SHIFT) << 32;
+	u64 wfe_mask = (u64)CMDQ_OP_CODE_MASK << 32 | 0xffffffff;
+	u64 *base = task->va_base;
+	int i;
+
+	dma_sync_single_for_cpu(dev, task->pa_base, task->cmd_buf_size,
+				DMA_TO_DEVICE);
+	for (i = 0; i < CMDQ_NUM_CMD(task); i++)
+		if ((base[i] & wfe_mask) == (wfe_op | wfe_option))
+			base[i] = (u64)CMDQ_JUMP_BY_OFFSET << 32 |
+				  CMDQ_JUMP_PASS;
+	dma_sync_single_for_device(dev, task->pa_base, task->cmd_buf_size,
+				   DMA_TO_DEVICE);
+}
+
+static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
+{
+	struct cmdq *cmdq = task->cmdq;
+	unsigned long curr_pa, end_pa;
+
+	task->thread = thread;
+	if (list_empty(&thread->task_busy_list)) {
+		WARN_ON(clk_enable(cmdq->clock) < 0);
+		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
+
+		writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+		writel(task->pa_base + task->cmd_buf_size,
+		       thread->base + CMDQ_THR_END_ADDR);
+		writel(CMDQ_THR_IRQ_EN, thread->base + CMDQ_THR_IRQ_ENABLE);
+		writel(CMDQ_THR_ENABLED, thread->base + CMDQ_THR_ENABLE_TASK);
+
+		mod_timer(&thread->timeout,
+			  jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+	} else {
+		WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+
+		/*
+		 * check boundary condition
+		 * PC = END - CMDQ_INST_SIZE, EOC is executed
+		 * PC = END, all CMDs are executed
+		 */
+		curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+		end_pa = readl(thread->base + CMDQ_THR_END_ADDR);
+		if (curr_pa == end_pa - CMDQ_INST_SIZE || curr_pa == end_pa) {
+			/* set to this task directly */
+			writel(task->pa_base,
+			       thread->base + CMDQ_THR_CURR_ADDR);
+		} else {
+			cmdq_task_insert_into_thread(task);
+
+			if (thread->merge_wfe)
+				cmdq_task_remove_wfe(task);
+
+			smp_mb(); /* modify jump before enable thread */
+		}
+
+		writel(task->pa_base + task->cmd_buf_size,
+		       thread->base + CMDQ_THR_END_ADDR);
+		cmdq_thread_resume(thread);
+	}
+	list_move_tail(&task->list_entry, &thread->task_busy_list);
+}
+
+static void cmdq_task_exec_done(struct cmdq_task *task, bool err)
+{
+	struct device *dev = task->cmdq->mbox.dev;
+	struct cmdq_cb_data cmdq_cb_data;
+
+	if (task->cb.cb) {
+		cmdq_cb_data.err = err;
+		cmdq_cb_data.data = task->cb.data;
+		task->cb.cb(cmdq_cb_data);
+	}
+	list_del(&task->list_entry);
+	dma_unmap_single(dev, task->pa_base, task->cmd_buf_size, DMA_TO_DEVICE);
+	kfree(task->va_base);
+}
+
+static void cmdq_task_handle_error(struct cmdq_task *task)
+{
+	struct cmdq_thread *thread = task->thread;
+	struct cmdq_task *next_task;
+
+	dev_err(task->cmdq->mbox.dev, "task 0x%p error\n", task);
+	WARN_ON(cmdq_thread_suspend(task->cmdq, thread) < 0);
+	next_task = list_first_entry_or_null(&thread->task_busy_list,
+			struct cmdq_task, list_entry);
+	if (next_task)
+		writel(next_task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
+	cmdq_thread_resume(thread);
+}
+
+static void cmdq_thread_irq_handler(struct cmdq *cmdq,
+				    struct cmdq_thread *thread)
+{
+	struct cmdq_task *task, *tmp, *curr_task = NULL;
+	u32 curr_pa, irq_flag, task_end_pa;
+	bool err;
+
+	irq_flag = readl(thread->base + CMDQ_THR_IRQ_STATUS);
+	writel(~irq_flag, thread->base + CMDQ_THR_IRQ_STATUS);
+
+	/*
+	 * When ISR call this function, another CPU core could run
+	 * "release task" right before we acquire the spin lock, and thus
+	 * reset / disable this GCE thread, so we need to check the enable
+	 * bit of this GCE thread.
+	 */
+	if (!(readl(thread->base + CMDQ_THR_ENABLE_TASK) & CMDQ_THR_ENABLED))
+		return;
+
+	if (irq_flag & CMDQ_THR_IRQ_ERROR)
+		err = true;
+	else if (irq_flag & CMDQ_THR_IRQ_DONE)
+		err = false;
+	else
+		return;
+
+	curr_pa = readl(thread->base + CMDQ_THR_CURR_ADDR);
+
+	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+				 list_entry) {
+		task_end_pa = task->pa_base + task->cmd_buf_size;
+		if (curr_pa >= task->pa_base && curr_pa < task_end_pa)
+			curr_task = task;
+
+		if (!curr_task || curr_pa == task_end_pa - CMDQ_INST_SIZE) {
+			cmdq_task_exec_done(task, false);
+			kfree(task);
+		} else if (err) {
+			cmdq_task_exec_done(task, true);
+			cmdq_task_handle_error(curr_task);
+			kfree(task);
+		}
+
+		if (curr_task)
+			break;
+	}
+
+	if (list_empty(&thread->task_busy_list)) {
+		cmdq_thread_disable(cmdq, thread);
+		clk_disable(cmdq->clock);
+	} else {
+		mod_timer(&thread->timeout,
+			  jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
+	}
+}
+
+static irqreturn_t cmdq_irq_handler(int irq, void *dev)
+{
+	struct cmdq *cmdq = dev;
+	unsigned long irq_status, flags = 0L;
+	int bit;
+
+	irq_status = readl(cmdq->base + CMDQ_CURR_IRQ_STATUS) & CMDQ_IRQ_MASK;
+	if (!(irq_status ^ CMDQ_IRQ_MASK))
+		return IRQ_NONE;
+
+	for_each_clear_bit(bit, &irq_status, fls(CMDQ_IRQ_MASK)) {
+		struct cmdq_thread *thread = &cmdq->thread[bit];
+
+		spin_lock_irqsave(&thread->chan->lock, flags);
+		cmdq_thread_irq_handler(cmdq, thread);
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+	}
+	return IRQ_HANDLED;
+}
+
+static void cmdq_thread_handle_timeout(unsigned long data)
+{
+	struct cmdq_thread *thread = (struct cmdq_thread *)data;
+	struct cmdq *cmdq = container_of(thread->chan->mbox, struct cmdq, mbox);
+	struct cmdq_task *task, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&thread->chan->lock, flags);
+	WARN_ON(cmdq_thread_suspend(cmdq, thread) < 0);
+
+	/*
+	 * Although IRQ is disabled, GCE continues to execute.
+	 * It may have pending IRQ before GCE thread is suspended,
+	 * so check this condition again.
+	 */
+	cmdq_thread_irq_handler(cmdq, thread);
+
+	if (list_empty(&thread->task_busy_list)) {
+		cmdq_thread_resume(thread);
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+		return;
+	}
+
+	dev_err(cmdq->mbox.dev, "timeout\n");
+	list_for_each_entry_safe(task, tmp, &thread->task_busy_list,
+				 list_entry) {
+		cmdq_task_exec_done(task, true);
+		kfree(task);
+	}
+
+	cmdq_thread_resume(thread);
+	cmdq_thread_disable(cmdq, thread);
+	clk_disable(cmdq->clock);
+	spin_unlock_irqrestore(&thread->chan->lock, flags);
+}
+
+static int cmdq_rec_realloc_cmd_buffer(struct cmdq_rec *rec, size_t size)
+{
+	void *new_buf;
+
+	new_buf = krealloc(rec->buf, size, GFP_KERNEL | __GFP_ZERO);
+	if (!new_buf)
+		return -ENOMEM;
+	rec->buf = new_buf;
+	rec->buf_size = size;
+	return 0;
+}
+
+struct cmdq_base *cmdq_register_device(struct device *dev)
+{
+	struct cmdq_base *cmdq_base;
+	struct resource res;
+	int subsys;
+	u32 base;
+
+	if (of_address_to_resource(dev->of_node, 0, &res))
+		return NULL;
+	base = (u32)res.start;
+
+	subsys = cmdq_subsys_base_to_id(base >> 16);
+	if (subsys < 0)
+		return NULL;
+
+	cmdq_base = devm_kmalloc(dev, sizeof(*cmdq_base), GFP_KERNEL);
+	if (!cmdq_base)
+		return NULL;
+	cmdq_base->subsys = subsys;
+	cmdq_base->base = base;
+
+	return cmdq_base;
+}
+EXPORT_SYMBOL(cmdq_register_device);
+
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index)
+{
+	struct cmdq_client *client;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	client->client.dev = dev;
+	client->client.tx_block = false;
+	client->chan = mbox_request_channel(&client->client, index);
+	return client;
+}
+EXPORT_SYMBOL(cmdq_mbox_create);
+
+int cmdq_rec_create(struct device *dev, struct cmdq_rec **rec_ptr)
+{
+	struct cmdq_rec *rec;
+	int err;
+
+	rec = kzalloc(sizeof(*rec), GFP_KERNEL);
+	if (!rec)
+		return -ENOMEM;
+	rec->cmdq = dev_get_drvdata(dev);
+	err = cmdq_rec_realloc_cmd_buffer(rec, PAGE_SIZE);
+	if (err < 0) {
+		kfree(rec);
+		return err;
+	}
+	*rec_ptr = rec;
+	return 0;
+}
+EXPORT_SYMBOL(cmdq_rec_create);
+
+static int cmdq_rec_append_command(struct cmdq_rec *rec, enum cmdq_code code,
+				   u32 arg_a, u32 arg_b)
+{
+	u64 *cmd_ptr;
+	int err;
+
+	if (WARN_ON(rec->finalized))
+		return -EBUSY;
+	if (unlikely(rec->cmd_buf_size + CMDQ_INST_SIZE > rec->buf_size)) {
+		err = cmdq_rec_realloc_cmd_buffer(rec, rec->buf_size * 2);
+		if (err < 0)
+			return err;
+	}
+	cmd_ptr = rec->buf + rec->cmd_buf_size;
+	(*cmd_ptr) = (u64)((code << CMDQ_OP_CODE_SHIFT) | arg_a) << 32 | arg_b;
+	rec->cmd_buf_size += CMDQ_INST_SIZE;
+	return 0;
+}
+
+int cmdq_rec_write(struct cmdq_rec *rec, u32 value, struct cmdq_base *base,
+		   u32 offset)
+{
+	u32 arg_a = ((base->base + offset) & CMDQ_ARG_A_WRITE_MASK) |
+		    (base->subsys << CMDQ_SUBSYS_SHIFT);
+	return cmdq_rec_append_command(rec, CMDQ_CODE_WRITE, arg_a, value);
+}
+EXPORT_SYMBOL(cmdq_rec_write);
+
+int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
+			struct cmdq_base *base, u32 offset, u32 mask)
+{
+	u32 offset_mask = offset;
+	int err;
+
+	if (mask != 0xffffffff) {
+		err = cmdq_rec_append_command(rec, CMDQ_CODE_MASK, 0, ~mask);
+		if (err < 0)
+			return err;
+		offset_mask |= CMDQ_WRITE_ENABLE_MASK;
+	}
+	return cmdq_rec_write(rec, value, base, offset_mask);
+}
+EXPORT_SYMBOL(cmdq_rec_write_mask);
+
+static const u32 cmdq_event_value[CMDQ_MAX_EVENT] = {
+	/* Display start of frame(SOF) events */
+	[CMDQ_EVENT_DISP_OVL0_SOF] = 11,
+	[CMDQ_EVENT_DISP_OVL1_SOF] = 12,
+	[CMDQ_EVENT_DISP_RDMA0_SOF] = 13,
+	[CMDQ_EVENT_DISP_RDMA1_SOF] = 14,
+	[CMDQ_EVENT_DISP_RDMA2_SOF] = 15,
+	[CMDQ_EVENT_DISP_WDMA0_SOF] = 16,
+	[CMDQ_EVENT_DISP_WDMA1_SOF] = 17,
+	/* Display end of frame(EOF) events */
+	[CMDQ_EVENT_DISP_OVL0_EOF] = 39,
+	[CMDQ_EVENT_DISP_OVL1_EOF] = 40,
+	[CMDQ_EVENT_DISP_RDMA0_EOF] = 41,
+	[CMDQ_EVENT_DISP_RDMA1_EOF] = 42,
+	[CMDQ_EVENT_DISP_RDMA2_EOF] = 43,
+	[CMDQ_EVENT_DISP_WDMA0_EOF] = 44,
+	[CMDQ_EVENT_DISP_WDMA1_EOF] = 45,
+	/* Mutex end of frame(EOF) events */
+	[CMDQ_EVENT_MUTEX0_STREAM_EOF] = 53,
+	[CMDQ_EVENT_MUTEX1_STREAM_EOF] = 54,
+	[CMDQ_EVENT_MUTEX2_STREAM_EOF] = 55,
+	[CMDQ_EVENT_MUTEX3_STREAM_EOF] = 56,
+	[CMDQ_EVENT_MUTEX4_STREAM_EOF] = 57,
+	/* Display underrun events */
+	[CMDQ_EVENT_DISP_RDMA0_UNDERRUN] = 63,
+	[CMDQ_EVENT_DISP_RDMA1_UNDERRUN] = 64,
+	[CMDQ_EVENT_DISP_RDMA2_UNDERRUN] = 65,
+};
+
+int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event)
+{
+	u32 arg_b;
+
+	if (event >= CMDQ_MAX_EVENT || event < 0)
+		return -EINVAL;
+
+	/*
+	 * WFE arg_b
+	 * bit 0-11: wait value
+	 * bit 15: 1 - wait, 0 - no wait
+	 * bit 16-27: update value
+	 * bit 31: 1 - update, 0 - no update
+	 */
+	arg_b = CMDQ_WFE_UPDATE | CMDQ_WFE_WAIT | CMDQ_WFE_WAIT_VALUE;
+	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE,
+			cmdq_event_value[event], arg_b);
+}
+EXPORT_SYMBOL(cmdq_rec_wfe);
+
+int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event)
+{
+	if (event >= CMDQ_MAX_EVENT || event < 0)
+		return -EINVAL;
+
+	return cmdq_rec_append_command(rec, CMDQ_CODE_WFE,
+			cmdq_event_value[event], CMDQ_WFE_UPDATE);
+}
+EXPORT_SYMBOL(cmdq_rec_clear_event);
+
+static int cmdq_rec_finalize(struct cmdq_rec *rec)
+{
+	int err;
+
+	if (rec->finalized)
+		return 0;
+
+	/* insert EOC and generate IRQ for each command iteration */
+	err = cmdq_rec_append_command(rec, CMDQ_CODE_EOC, 0, CMDQ_EOC_IRQ_EN);
+	if (err < 0)
+		return err;
+
+	/* JUMP to end */
+	err = cmdq_rec_append_command(rec, CMDQ_CODE_JUMP, 0, CMDQ_JUMP_PASS);
+	if (err < 0)
+		return err;
+
+	rec->finalized = true;
+	return 0;
+}
+
+int cmdq_rec_flush_async(struct cmdq_client *client, struct cmdq_rec *rec,
+			 cmdq_async_flush_cb cb, void *data)
+{
+	struct cmdq *cmdq = rec->cmdq;
+	struct cmdq_task *task;
+	struct cmdq_task_cb task_cb;
+	int err;
+
+	mutex_lock(&cmdq->task_mutex);
+	if (cmdq->suspended) {
+		dev_err(cmdq->mbox.dev, "%s is called after suspended\n",
+			__func__);
+		mutex_unlock(&cmdq->task_mutex);
+		return -EPERM;
+	}
+
+	err = cmdq_rec_finalize(rec);
+	if (err < 0) {
+		mutex_unlock(&cmdq->task_mutex);
+		return err;
+	}
+
+	task_cb.cb = cb;
+	task_cb.data = data;
+	task = cmdq_task_acquire(rec, task_cb);
+	if (!task) {
+		mutex_unlock(&cmdq->task_mutex);
+		return -EFAULT;
+	}
+
+	mbox_send_message(client->chan, task);
+	/* We can send next task immediately, so just call txdone. */
+	mbox_client_txdone(client->chan, 0);
+	mutex_unlock(&cmdq->task_mutex);
+	return 0;
+}
+EXPORT_SYMBOL(cmdq_rec_flush_async);
+
+struct cmdq_flush_completion {
+	struct completion cmplt;
+	bool err;
+};
+
+static void cmdq_rec_flush_cb(struct cmdq_cb_data data)
+{
+	struct cmdq_flush_completion *cmplt = data.data;
+
+	cmplt->err = data.err;
+	complete(&cmplt->cmplt);
+}
+
+int cmdq_rec_flush(struct cmdq_client *client, struct cmdq_rec *rec)
+{
+	struct cmdq_flush_completion cmplt;
+	int err;
+
+	init_completion(&cmplt.cmplt);
+	err = cmdq_rec_flush_async(client, rec, cmdq_rec_flush_cb, &cmplt);
+	if (err < 0)
+		return err;
+	wait_for_completion(&cmplt.cmplt);
+	return cmplt.err ? -EFAULT : 0;
+}
+EXPORT_SYMBOL(cmdq_rec_flush);
+
+void cmdq_rec_destroy(struct cmdq_rec *rec)
+{
+	kfree(rec->buf);
+	kfree(rec);
+}
+EXPORT_SYMBOL(cmdq_rec_destroy);
+
+void cmdq_mbox_free(struct cmdq_client *client)
+{
+	mbox_free_channel(client->chan);
+	kfree(client);
+}
+EXPORT_SYMBOL(cmdq_mbox_free);
+
+static int cmdq_suspend(struct device *dev)
+{
+	struct cmdq *cmdq = dev_get_drvdata(dev);
+	struct cmdq_thread *thread;
+	int i;
+	bool task_running = false;
+
+	mutex_lock(&cmdq->task_mutex);
+	cmdq->suspended = true;
+	mutex_unlock(&cmdq->task_mutex);
+
+	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+		thread = &cmdq->thread[i];
+		if (!list_empty(&thread->task_busy_list)) {
+			mod_timer(&thread->timeout, jiffies + 1);
+			task_running = true;
+		}
+	}
+
+	if (task_running) {
+		dev_warn(dev, "exist running task(s) in suspend\n");
+		msleep(20);
+	}
+
+	clk_unprepare(cmdq->clock);
+	return 0;
+}
+
+static int cmdq_resume(struct device *dev)
+{
+	struct cmdq *cmdq = dev_get_drvdata(dev);
+
+	WARN_ON(clk_prepare(cmdq->clock) < 0);
+	cmdq->suspended = false;
+	return 0;
+}
+
+static int cmdq_remove(struct platform_device *pdev)
+{
+	struct cmdq *cmdq = platform_get_drvdata(pdev);
+
+	mbox_controller_unregister(&cmdq->mbox);
+	clk_unprepare(cmdq->clock);
+	return 0;
+}
+
+static int cmdq_mbox_send_data(struct mbox_chan *chan, void *data)
+{
+	cmdq_task_exec(data, chan->con_priv);
+	return 0;
+}
+
+static int cmdq_mbox_startup(struct mbox_chan *chan)
+{
+	return 0;
+}
+
+static void cmdq_mbox_shutdown(struct mbox_chan *chan)
+{
+}
+
+static bool cmdq_mbox_last_tx_done(struct mbox_chan *chan)
+{
+	return true;
+}
+
+static const struct mbox_chan_ops cmdq_mbox_chan_ops = {
+	.send_data = cmdq_mbox_send_data,
+	.startup = cmdq_mbox_startup,
+	.shutdown = cmdq_mbox_shutdown,
+	.last_tx_done = cmdq_mbox_last_tx_done,
+};
+
+static struct mbox_chan *cmdq_xlate(struct mbox_controller *mbox,
+		const struct of_phandle_args *sp)
+{
+	int ind = sp->args[0];
+	struct cmdq_thread *thread;
+
+	if (ind >= mbox->num_chans)
+		return ERR_PTR(-EINVAL);
+
+	thread = mbox->chans[ind].con_priv;
+	thread->merge_wfe = (sp->args[1] != 0);
+	thread->chan = &mbox->chans[ind];
+
+	return &mbox->chans[ind];
+}
+
+static int cmdq_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource *res;
+	struct cmdq *cmdq;
+	int err, i;
+
+	cmdq = devm_kzalloc(dev, sizeof(*cmdq), GFP_KERNEL);
+	if (!cmdq)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cmdq->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cmdq->base)) {
+		dev_err(dev, "failed to ioremap gce\n");
+		return PTR_ERR(cmdq->base);
+	}
+
+	cmdq->irq = irq_of_parse_and_map(node, 0);
+	if (!cmdq->irq) {
+		dev_err(dev, "failed to get irq\n");
+		return -EINVAL;
+	}
+	err = devm_request_irq(dev, cmdq->irq, cmdq_irq_handler, IRQF_SHARED,
+			       "mtk_cmdq", cmdq);
+	if (err < 0) {
+		dev_err(dev, "failed to register ISR (%d)\n", err);
+		return err;
+	}
+
+	dev_dbg(dev, "cmdq device: addr:0x%p, va:0x%p, irq:%d\n",
+		dev, cmdq->base, cmdq->irq);
+
+	cmdq->clock = devm_clk_get(dev, "gce");
+	if (IS_ERR(cmdq->clock)) {
+		dev_err(dev, "failed to get gce clk\n");
+		return PTR_ERR(cmdq->clock);
+	}
+
+	cmdq->mbox.dev = dev;
+	cmdq->mbox.chans = devm_kcalloc(dev, CMDQ_THR_MAX_COUNT,
+					sizeof(*cmdq->mbox.chans), GFP_KERNEL);
+	if (!cmdq->mbox.chans)
+		return -ENOMEM;
+
+	cmdq->mbox.num_chans = CMDQ_THR_MAX_COUNT;
+	cmdq->mbox.ops = &cmdq_mbox_chan_ops;
+	cmdq->mbox.of_xlate = cmdq_xlate;
+
+	/* make use of TXDONE_BY_ACK */
+	cmdq->mbox.txdone_irq = false;
+	cmdq->mbox.txdone_poll = false;
+
+	for (i = 0; i < ARRAY_SIZE(cmdq->thread); i++) {
+		cmdq->thread[i].base = cmdq->base + CMDQ_THR_BASE +
+				CMDQ_THR_SIZE * i;
+		INIT_LIST_HEAD(&cmdq->thread[i].task_busy_list);
+		init_timer(&cmdq->thread[i].timeout);
+		cmdq->thread[i].timeout.function = cmdq_thread_handle_timeout;
+		cmdq->thread[i].timeout.data = (unsigned long)&cmdq->thread[i];
+		cmdq->mbox.chans[i].con_priv = &cmdq->thread[i];
+	}
+
+	err = mbox_controller_register(&cmdq->mbox);
+	if (err < 0) {
+		dev_err(dev, "failed to register mailbox: %d\n", err);
+		return err;
+	}
+
+	mutex_init(&cmdq->task_mutex);
+	platform_set_drvdata(pdev, cmdq);
+	WARN_ON(clk_prepare(cmdq->clock) < 0);
+	return 0;
+}
+
+static const struct dev_pm_ops cmdq_pm_ops = {
+	.suspend = cmdq_suspend,
+	.resume = cmdq_resume,
+};
+
+static const struct of_device_id cmdq_of_ids[] = {
+	{.compatible = "mediatek,mt8173-gce",},
+	{}
+};
+
+static struct platform_driver cmdq_drv = {
+	.probe = cmdq_probe,
+	.remove = cmdq_remove,
+	.driver = {
+		.name = "mtk_cmdq",
+		.owner = THIS_MODULE,
+		.pm = &cmdq_pm_ops,
+		.of_match_table = cmdq_of_ids,
+	}
+};
+
+builtin_platform_driver(cmdq_drv);
diff --git a/include/soc/mediatek/cmdq.h b/include/soc/mediatek/cmdq.h
new file mode 100644
index 0000000..c5f575c
--- /dev/null
+++ b/include/soc/mediatek/cmdq.h
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2015 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MTK_CMDQ_H__
+#define __MTK_CMDQ_H__
+
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+
+/* display events in command queue(CMDQ) */
+enum cmdq_event {
+	/* Display start of frame(SOF) events */
+	CMDQ_EVENT_DISP_OVL0_SOF,
+	CMDQ_EVENT_DISP_OVL1_SOF,
+	CMDQ_EVENT_DISP_RDMA0_SOF,
+	CMDQ_EVENT_DISP_RDMA1_SOF,
+	CMDQ_EVENT_DISP_RDMA2_SOF,
+	CMDQ_EVENT_DISP_WDMA0_SOF,
+	CMDQ_EVENT_DISP_WDMA1_SOF,
+	/* Display end of frame(EOF) events */
+	CMDQ_EVENT_DISP_OVL0_EOF,
+	CMDQ_EVENT_DISP_OVL1_EOF,
+	CMDQ_EVENT_DISP_RDMA0_EOF,
+	CMDQ_EVENT_DISP_RDMA1_EOF,
+	CMDQ_EVENT_DISP_RDMA2_EOF,
+	CMDQ_EVENT_DISP_WDMA0_EOF,
+	CMDQ_EVENT_DISP_WDMA1_EOF,
+	/* Mutex end of frame(EOF) events */
+	CMDQ_EVENT_MUTEX0_STREAM_EOF,
+	CMDQ_EVENT_MUTEX1_STREAM_EOF,
+	CMDQ_EVENT_MUTEX2_STREAM_EOF,
+	CMDQ_EVENT_MUTEX3_STREAM_EOF,
+	CMDQ_EVENT_MUTEX4_STREAM_EOF,
+	/* Display underrun events */
+	CMDQ_EVENT_DISP_RDMA0_UNDERRUN,
+	CMDQ_EVENT_DISP_RDMA1_UNDERRUN,
+	CMDQ_EVENT_DISP_RDMA2_UNDERRUN,
+	/* Keep this at the end */
+	CMDQ_MAX_EVENT,
+};
+
+struct cmdq_cb_data {
+	bool	err;
+	void	*data;
+};
+
+typedef void (*cmdq_async_flush_cb)(struct cmdq_cb_data data);
+
+struct cmdq_task;
+struct cmdq;
+
+struct cmdq_rec {
+	struct cmdq	*cmdq;
+	size_t		cmd_buf_size;	/* command occupied size */
+	void		*buf;
+	size_t		buf_size;	/* real buffer size */
+	bool		finalized;
+};
+
+struct cmdq_base {
+	int	subsys;
+	u32	base;
+};
+
+struct cmdq_client {
+	struct mbox_client client;
+	struct mbox_chan *chan;
+};
+
+/**
+ * cmdq_register_device() - register device which needs CMDQ
+ * @dev:	device for CMDQ to access its registers
+ *
+ * Return: cmdq_base pointer or NULL for failed
+ */
+struct cmdq_base *cmdq_register_device(struct device *dev);
+
+/**
+ * cmdq_mbox_create() - create CMDQ mailbox client and channel
+ * @dev:	device of CMDQ mailbox client
+ * @index:	index of CMDQ mailbox channel
+ *
+ * Return: CMDQ mailbox client pointer
+ */
+struct cmdq_client *cmdq_mbox_create(struct device *dev, int index);
+
+/**
+ * cmdq_rec_create() - create CMDQ record
+ * @dev:	CMDQ device
+ * @rec_ptr:	CMDQ record pointer to retrieve cmdq_rec
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_rec_create(struct device *dev, struct cmdq_rec **rec_ptr);
+
+/**
+ * cmdq_rec_write() - append write command to the CMDQ record
+ * @rec:	the CMDQ record
+ * @value:	the specified target register value
+ * @base:	the CMDQ base
+ * @offset:	register offset from module base
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_rec_write(struct cmdq_rec *rec, u32 value,
+		   struct cmdq_base *base, u32 offset);
+
+/**
+ * cmdq_rec_write_mask() - append write command with mask to the CMDQ record
+ * @rec:	the CMDQ record
+ * @value:	the specified target register value
+ * @base:	the CMDQ base
+ * @offset:	register offset from module base
+ * @mask:	the specified target register mask
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_rec_write_mask(struct cmdq_rec *rec, u32 value,
+			struct cmdq_base *base, u32 offset, u32 mask);
+
+/**
+ * cmdq_rec_wfe() - append wait for event command to the CMDQ record
+ * @rec:	the CMDQ record
+ * @event:	the desired event type to "wait and CLEAR"
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_rec_wfe(struct cmdq_rec *rec, enum cmdq_event event);
+
+/**
+ * cmdq_rec_clear_event() - append clear event command to the CMDQ record
+ * @rec:	the CMDQ record
+ * @event:	the desired event to be cleared
+ *
+ * Return: 0 for success; else the error code is returned
+ */
+int cmdq_rec_clear_event(struct cmdq_rec *rec, enum cmdq_event event);
+
+/**
+ * cmdq_rec_flush() - trigger CMDQ to execute the recorded commands
+ * @client:	the CMDQ mailbox client
+ * @rec:	the CMDQ record
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to execute the recorded commands. Note that this is a
+ * synchronous flush function. When the function returned, the recorded
+ * commands have been done.
+ */
+int cmdq_rec_flush(struct cmdq_client *client, struct cmdq_rec *rec);
+
+/**
+ * cmdq_rec_flush_async() - trigger CMDQ to asynchronously execute the recorded
+ *			    commands and call back at the end of ISR
+ * @client:	the CMDQ mailbox client
+ * @rec:	the CMDQ record
+ * @cb:		called at the end of CMDQ ISR
+ * @data:	this data will pass back to cb
+ *
+ * Return: 0 for success; else the error code is returned
+ *
+ * Trigger CMDQ to asynchronously execute the recorded commands and call back
+ * at the end of ISR. Note that this is an ASYNC function. When the function
+ * returned, it may or may not be finished.
+ */
+int cmdq_rec_flush_async(struct cmdq_client *client, struct cmdq_rec *rec,
+			 cmdq_async_flush_cb cb, void *data);
+
+/**
+ * cmdq_rec_destroy() - destroy CMDQ record
+ * @rec:	the CMDQ record
+ */
+void cmdq_rec_destroy(struct cmdq_rec *rec);
+
+/**
+ * cmdq_mbox_free() - destroy CMDQ mailbox client and channel
+ * @client:	the CMDQ mailbox client
+ */
+void cmdq_mbox_free(struct cmdq_client *client);
+
+#endif	/* __MTK_CMDQ_H__ */
-- 
1.9.1



From hs.liao at mediatek.com  Thu Jun 30 00:14:42 2016
From: hs.liao at mediatek.com (HS Liao)
Date: Thu, 30 Jun 2016 15:14:42 +0800
Subject: [PATCH v9 3/4] arm64: dts: mt8173: Add GCE node
In-Reply-To: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
References: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
Message-ID: <1467270883-1407-4-git-send-email-hs.liao@mediatek.com>

This patch adds the device node of the GCE hardware for CMDQ module.

Signed-off-by: HS Liao <hs.liao at mediatek.com>
---
 arch/arm64/boot/dts/mediatek/mt8173.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 05f89c4..14a62e0 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -343,6 +343,16 @@
 			#clock-cells = <1>;
 		};
 
+		gce: gce at 10212000 {
+			compatible = "mediatek,mt8173-gce";
+			reg = <0 0x10212000 0 0x1000>;
+			interrupts = <GIC_SPI 135 IRQ_TYPE_LEVEL_LOW>;
+			clocks = <&infracfg CLK_INFRA_GCE>;
+			clock-names = "gce";
+
+			#mbox-cells = <1>;
+		};
+
 		gic: interrupt-controller at 10220000 {
 			compatible = "arm,gic-400";
 			#interrupt-cells = <3>;
-- 
1.9.1



From hs.liao at mediatek.com  Thu Jun 30 00:14:43 2016
From: hs.liao at mediatek.com (HS Liao)
Date: Thu, 30 Jun 2016 15:14:43 +0800
Subject: [PATCH v9 4/4] CMDQ: save more energy in idle
In-Reply-To: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
References: <1467270883-1407-1-git-send-email-hs.liao@mediatek.com>
Message-ID: <1467270883-1407-5-git-send-email-hs.liao@mediatek.com>

Use clk_disable_unprepare instead of clk_disable to save more energy
when CMDQ is idle.

Signed-off-by: HS Liao <hs.liao at mediatek.com>
---
 drivers/soc/mediatek/mtk-cmdq.c | 55 +++++++++++++++++++++++++++++++++++------
 1 file changed, 47 insertions(+), 8 deletions(-)

diff --git a/drivers/soc/mediatek/mtk-cmdq.c b/drivers/soc/mediatek/mtk-cmdq.c
index 6a6c4c3..549bbdd 100644
--- a/drivers/soc/mediatek/mtk-cmdq.c
+++ b/drivers/soc/mediatek/mtk-cmdq.c
@@ -32,6 +32,7 @@
 #include <linux/spinlock.h>
 #include <linux/suspend.h>
 #include <linux/timer.h>
+#include <linux/workqueue.h>
 #include <soc/mediatek/cmdq.h>
 
 #define CMDQ_THR_MAX_COUNT		3 /* main, sub, general(misc) */
@@ -130,10 +131,16 @@ struct cmdq_task {
 	struct cmdq_task_cb	cb;
 };
 
+struct cmdq_clk_release {
+	struct cmdq		*cmdq;
+	struct work_struct	release_work;
+};
+
 struct cmdq {
 	struct mbox_controller	mbox;
 	void __iomem		*base;
 	u32			irq;
+	struct workqueue_struct	*clk_release_wq;
 	struct cmdq_thread	thread[CMDQ_THR_MAX_COUNT];
 	struct mutex		task_mutex;
 	struct clk		*clock;
@@ -282,11 +289,19 @@ static void cmdq_task_remove_wfe(struct cmdq_task *task)
 static void cmdq_task_exec(struct cmdq_task *task, struct cmdq_thread *thread)
 {
 	struct cmdq *cmdq = task->cmdq;
-	unsigned long curr_pa, end_pa;
+	unsigned long curr_pa, end_pa, flags;
 
 	task->thread = thread;
 	if (list_empty(&thread->task_busy_list)) {
-		WARN_ON(clk_enable(cmdq->clock) < 0);
+		/*
+		 * Unlock for clk prepare (sleeping function).
+		 * We are safe to do that since we have task_mutex and
+		 * only flush will add task.
+		 */
+		spin_unlock_irqrestore(&thread->chan->lock, flags);
+		WARN_ON(clk_prepare_enable(cmdq->clock) < 0);
+		spin_lock_irqsave(&thread->chan->lock, flags);
+
 		WARN_ON(cmdq_thread_reset(cmdq, thread) < 0);
 
 		writel(task->pa_base, thread->base + CMDQ_THR_CURR_ADDR);
@@ -356,6 +371,26 @@ static void cmdq_task_handle_error(struct cmdq_task *task)
 	cmdq_thread_resume(thread);
 }
 
+static void cmdq_clk_release_work(struct work_struct *work_item)
+{
+	struct cmdq_clk_release *clk_release = container_of(work_item,
+			struct cmdq_clk_release, release_work);
+	struct cmdq *cmdq = clk_release->cmdq;
+
+	clk_disable_unprepare(cmdq->clock);
+	kfree(clk_release);
+}
+
+static void cmdq_clk_release_schedule(struct cmdq *cmdq)
+{
+	struct cmdq_clk_release *clk_release;
+
+	clk_release = kmalloc(sizeof(*clk_release), GFP_ATOMIC);
+	clk_release->cmdq = cmdq;
+	INIT_WORK(&clk_release->release_work, cmdq_clk_release_work);
+	queue_work(cmdq->clk_release_wq, &clk_release->release_work);
+}
+
 static void cmdq_thread_irq_handler(struct cmdq *cmdq,
 				    struct cmdq_thread *thread)
 {
@@ -405,7 +440,7 @@ static void cmdq_thread_irq_handler(struct cmdq *cmdq,
 
 	if (list_empty(&thread->task_busy_list)) {
 		cmdq_thread_disable(cmdq, thread);
-		clk_disable(cmdq->clock);
+		cmdq_clk_release_schedule(cmdq);
 	} else {
 		mod_timer(&thread->timeout,
 			  jiffies + msecs_to_jiffies(CMDQ_TIMEOUT_MS));
@@ -464,7 +499,7 @@ static void cmdq_thread_handle_timeout(unsigned long data)
 
 	cmdq_thread_resume(thread);
 	cmdq_thread_disable(cmdq, thread);
-	clk_disable(cmdq->clock);
+	cmdq_clk_release_schedule(cmdq);
 	spin_unlock_irqrestore(&thread->chan->lock, flags);
 }
 
@@ -763,7 +798,7 @@ static int cmdq_suspend(struct device *dev)
 		msleep(20);
 	}
 
-	clk_unprepare(cmdq->clock);
+	flush_workqueue(cmdq->clk_release_wq);
 	return 0;
 }
 
@@ -771,7 +806,6 @@ static int cmdq_resume(struct device *dev)
 {
 	struct cmdq *cmdq = dev_get_drvdata(dev);
 
-	WARN_ON(clk_prepare(cmdq->clock) < 0);
 	cmdq->suspended = false;
 	return 0;
 }
@@ -780,8 +814,8 @@ static int cmdq_remove(struct platform_device *pdev)
 {
 	struct cmdq *cmdq = platform_get_drvdata(pdev);
 
+	destroy_workqueue(cmdq->clk_release_wq);
 	mbox_controller_unregister(&cmdq->mbox);
-	clk_unprepare(cmdq->clock);
 	return 0;
 }
 
@@ -899,8 +933,13 @@ static int cmdq_probe(struct platform_device *pdev)
 	}
 
 	mutex_init(&cmdq->task_mutex);
+
+	cmdq->clk_release_wq = alloc_ordered_workqueue(
+			"%s", WQ_MEM_RECLAIM | WQ_HIGHPRI,
+			"cmdq_clk_release");
+
 	platform_set_drvdata(pdev, cmdq);
-	WARN_ON(clk_prepare(cmdq->clock) < 0);
+
 	return 0;
 }
 
-- 
1.9.1



From john at phrozen.org  Thu Jun 30 00:33:33 2016
From: john at phrozen.org (John Crispin)
Date: Thu, 30 Jun 2016 09:33:33 +0200
Subject: [PATCH V2 2/4] net-next: mediatek: don't use intermediate
 variables to store IRQ masks
In-Reply-To: <201606301404.EoyOs9Sz%fengguang.wu@intel.com>
References: <201606301404.EoyOs9Sz%fengguang.wu@intel.com>
Message-ID: <3eb8dd5f-7b21-a2f5-dbd5-9016bf328753@phrozen.org>



On 30/06/2016 08:03, kbuild test robot wrote:
> Hi,
> 
> [auto build test ERROR on net/master]
> [also build test ERROR on v4.7-rc5 next-20160629]
> [cannot apply to net-next/master]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
> 
> url:    https://github.com/0day-ci/linux/commits/John-Crispin/net-next-mediatek-IRQ-cleanups-fixes-and-grouping/20160629-194341
> config: arm-allmodconfig (attached as .config)
> compiler: arm-linux-gnueabi-gcc (Debian 5.3.1-8) 5.3.1 20160205
> 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 
> 
> Note: the linux-review/John-Crispin/net-next-mediatek-IRQ-cleanups-fixes-and-grouping/20160629-194341 HEAD e8462c612b33003fba7c1a70328c7fb7872d8507 builds fine.
>       It only hurts bisectibility.
> 
> All errors (new ones prefixed by >>):
> 
>    drivers/net/ethernet/mediatek/mtk_eth_soc.c: In function 'mtk_poll':
>>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:16: error: 'tx_intr' undeclared (first use in this function)
>      if (status & (tx_intr | rx_intr))
>                    ^
>    drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:16: note: each undeclared identifier is reported only once for each function it appears in
>>> drivers/net/ethernet/mediatek/mtk_eth_soc.c:1003:26: error: 'rx_intr' undeclared (first use in this function)
>      if (status & (tx_intr | rx_intr))
>                              ^
> 
> vim +/tx_intr +1003 drivers/net/ethernet/mediatek/mtk_eth_soc.c
> 
> 656e7052 John Crispin 2016-03-08   997  	}
> 656e7052 John Crispin 2016-03-08   998  
> 656e7052 John Crispin 2016-03-08   999  	if (tx_again || rx_done == budget)
> 656e7052 John Crispin 2016-03-08  1000  		return budget;
> 656e7052 John Crispin 2016-03-08  1001  
> 656e7052 John Crispin 2016-03-08  1002  	status = mtk_r32(eth, MTK_QMTK_INT_STATUS);
> 656e7052 John Crispin 2016-03-08 @1003  	if (status & (tx_intr | rx_intr))
> 656e7052 John Crispin 2016-03-08  1004  		return budget;
> 656e7052 John Crispin 2016-03-08  1005  
> 656e7052 John Crispin 2016-03-08  1006  	napi_complete(napi);
> 
> :::::: The code at line 1003 was first introduced by commit
> :::::: 656e705243fd0c2864b89634ea16ed444ef64dc6 net-next: mediatek: add support for MT7623 ethernet
> 
> :::::: TO: John Crispin <blogic at openwrt.org>
> :::::: CC: David S. Miller <davem at davemloft.net>
> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

Hi,

This series depends on a previous series currently sitting in net.git
starting with
	commit 562c5a70400c ("net: mediatek: only wake the queue if it is stopped")
up to
	commit 82c6544dddc6 ("net: mediatek: remove superfluous queue wake up
call")

i just double checked and with the series applied this compile error is gone

	John



From k.kozlowski at samsung.com  Thu Jun 30 01:23:39 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 30 Jun 2016 10:23:39 +0200
Subject: [PATCH v5 00/44] dma-mapping: Use unsigned long for dma_attrs
Message-ID: <1467275019-30789-1-git-send-email-k.kozlowski@samsung.com>

Hi,


This is fifth approach for replacing struct dma_attrs with unsigned
long.

The main patch (1/44) doing the change is split into many subpatches
for easier review (2-42).  They should be squashed together when
applying.


Rebased on v4.7-rc5.

For easier testing the patchset is available here:
repo:   https://github.com/krzk/linux
branch: for-next/dma-attrs-const-v5


Changes since v4
================
1. Collect some acks. Still need more.
2. Minor fixes pointed by Robin Murphy.
3. Applied changes from Bart Van Assche's comment.
4. More tests and builds (using https://www.kernel.org/pub/tools/crosstool/).


Changes since v3
================
1. Collect some acks.
2. Drop wrong patch 1/45 ("powerpc: dma-mapping: Don't hard-code
   the value of DMA_ATTR_WEAK_ORDERING").
3. Minor fix pointed out by Michael Ellerman.


Changes since v2
================
1. Follow Christoph Hellwig's comments (don't use BIT add
   documentation, remove dma_get_attr).


Rationale
=========
The dma-mapping core and the implementations do not change the
DMA attributes passed by pointer.  Thus the pointer can point to const
data.  However the attributes do not have to be a bitfield. Instead
unsigned long will do fine:

1. This is just simpler.  Both in terms of reading the code and setting
   attributes.  Instead of initializing local attributes on the stack
   and passing pointer to it to dma_set_attr(), just set the bits.

2. It brings safeness and checking for const correctness because the
   attributes are passed by value.


Best regards,
Krzysztof


Krzysztof Kozlowski (44):
  dma-mapping: Use unsigned long for dma_attrs
  alpha: dma-mapping: Use unsigned long for dma_attrs
  arc: dma-mapping: Use unsigned long for dma_attrs
  ARM: dma-mapping: Use unsigned long for dma_attrs
  arm64: dma-mapping: Use unsigned long for dma_attrs
  avr32: dma-mapping: Use unsigned long for dma_attrs
  blackfin: dma-mapping: Use unsigned long for dma_attrs
  c6x: dma-mapping: Use unsigned long for dma_attrs
  cris: dma-mapping: Use unsigned long for dma_attrs
  frv: dma-mapping: Use unsigned long for dma_attrs
  drm/exynos: dma-mapping: Use unsigned long for dma_attrs
  drm/mediatek: dma-mapping: Use unsigned long for dma_attrs
  drm/msm: dma-mapping: Use unsigned long for dma_attrs
  drm/nouveau: dma-mapping: Use unsigned long for dma_attrs
  drm/rockship: dma-mapping: Use unsigned long for dma_attrs
  infiniband: dma-mapping: Use unsigned long for dma_attrs
  iommu: dma-mapping: Use unsigned long for dma_attrs
  [media] dma-mapping: Use unsigned long for dma_attrs
  xen: dma-mapping: Use unsigned long for dma_attrs
  swiotlb: dma-mapping: Use unsigned long for dma_attrs
  powerpc: dma-mapping: Use unsigned long for dma_attrs
  video: dma-mapping: Use unsigned long for dma_attrs
  x86: dma-mapping: Use unsigned long for dma_attrs
  iommu: intel: dma-mapping: Use unsigned long for dma_attrs
  h8300: dma-mapping: Use unsigned long for dma_attrs
  hexagon: dma-mapping: Use unsigned long for dma_attrs
  ia64: dma-mapping: Use unsigned long for dma_attrs
  m68k: dma-mapping: Use unsigned long for dma_attrs
  metag: dma-mapping: Use unsigned long for dma_attrs
  microblaze: dma-mapping: Use unsigned long for dma_attrs
  mips: dma-mapping: Use unsigned long for dma_attrs
  mn10300: dma-mapping: Use unsigned long for dma_attrs
  nios2: dma-mapping: Use unsigned long for dma_attrs
  openrisc: dma-mapping: Use unsigned long for dma_attrs
  parisc: dma-mapping: Use unsigned long for dma_attrs
  misc: mic: dma-mapping: Use unsigned long for dma_attrs
  s390: dma-mapping: Use unsigned long for dma_attrs
  sh: dma-mapping: Use unsigned long for dma_attrs
  sparc: dma-mapping: Use unsigned long for dma_attrs
  tile: dma-mapping: Use unsigned long for dma_attrs
  unicore32: dma-mapping: Use unsigned long for dma_attrs
  xtensa: dma-mapping: Use unsigned long for dma_attrs
  dma-mapping: Remove dma_get_attr
  dma-mapping: Document the DMA attributes next to the declaration

 Documentation/DMA-API.txt                          |  33 +++---
 Documentation/DMA-attributes.txt                   |   2 +-
 arch/alpha/include/asm/dma-mapping.h               |   2 -
 arch/alpha/kernel/pci-noop.c                       |   2 +-
 arch/alpha/kernel/pci_iommu.c                      |  12 +-
 arch/arc/mm/dma.c                                  |  12 +-
 arch/arm/common/dmabounce.c                        |   4 +-
 arch/arm/include/asm/dma-mapping.h                 |  13 +--
 arch/arm/include/asm/xen/page-coherent.h           |  16 +--
 arch/arm/mm/dma-mapping.c                          | 117 +++++++++----------
 arch/arm/xen/mm.c                                  |   8 +-
 arch/arm64/mm/dma-mapping.c                        |  66 +++++------
 arch/avr32/mm/dma-coherent.c                       |  12 +-
 arch/blackfin/kernel/dma-mapping.c                 |   8 +-
 arch/c6x/include/asm/dma-mapping.h                 |   4 +-
 arch/c6x/kernel/dma.c                              |   9 +-
 arch/c6x/mm/dma-coherent.c                         |   4 +-
 arch/cris/arch-v32/drivers/pci/dma.c               |   9 +-
 arch/frv/mb93090-mb00/pci-dma-nommu.c              |   8 +-
 arch/frv/mb93090-mb00/pci-dma.c                    |   9 +-
 arch/h8300/kernel/dma.c                            |   8 +-
 arch/hexagon/include/asm/dma-mapping.h             |   1 -
 arch/hexagon/kernel/dma.c                          |   8 +-
 arch/ia64/hp/common/sba_iommu.c                    |  22 ++--
 arch/ia64/include/asm/machvec.h                    |   1 -
 arch/ia64/kernel/pci-swiotlb.c                     |   4 +-
 arch/ia64/sn/pci/pci_dma.c                         |  22 ++--
 arch/m68k/kernel/dma.c                             |  12 +-
 arch/metag/kernel/dma.c                            |  16 +--
 arch/microblaze/include/asm/dma-mapping.h          |   1 -
 arch/microblaze/kernel/dma.c                       |  12 +-
 arch/mips/cavium-octeon/dma-octeon.c               |   8 +-
 arch/mips/loongson64/common/dma-swiotlb.c          |  10 +-
 arch/mips/mm/dma-default.c                         |  20 ++--
 arch/mips/netlogic/common/nlm-dma.c                |   4 +-
 arch/mn10300/mm/dma-alloc.c                        |   8 +-
 arch/nios2/mm/dma-mapping.c                        |  12 +-
 arch/openrisc/kernel/dma.c                         |  21 ++--
 arch/parisc/kernel/pci-dma.c                       |  18 +--
 arch/powerpc/include/asm/dma-mapping.h             |   7 +-
 arch/powerpc/include/asm/iommu.h                   |  10 +-
 arch/powerpc/kernel/dma-iommu.c                    |  12 +-
 arch/powerpc/kernel/dma.c                          |  18 +--
 arch/powerpc/kernel/ibmebus.c                      |  12 +-
 arch/powerpc/kernel/iommu.c                        |  12 +-
 arch/powerpc/kernel/vio.c                          |  12 +-
 arch/powerpc/platforms/cell/iommu.c                |  28 ++---
 arch/powerpc/platforms/pasemi/iommu.c              |   2 +-
 arch/powerpc/platforms/powernv/npu-dma.c           |   8 +-
 arch/powerpc/platforms/powernv/pci-ioda.c          |   4 +-
 arch/powerpc/platforms/powernv/pci.c               |   2 +-
 arch/powerpc/platforms/powernv/pci.h               |   2 +-
 arch/powerpc/platforms/ps3/system-bus.c            |  18 +--
 arch/powerpc/platforms/pseries/iommu.c             |   6 +-
 arch/powerpc/sysdev/dart_iommu.c                   |   2 +-
 arch/s390/include/asm/dma-mapping.h                |   1 -
 arch/s390/pci/pci_dma.c                            |  23 ++--
 arch/sh/include/asm/dma-mapping.h                  |   4 +-
 arch/sh/kernel/dma-nommu.c                         |   4 +-
 arch/sh/mm/consistent.c                            |   4 +-
 arch/sparc/kernel/iommu.c                          |  12 +-
 arch/sparc/kernel/ioport.c                         |  24 ++--
 arch/sparc/kernel/pci_sun4v.c                      |  12 +-
 arch/tile/kernel/pci-dma.c                         |  28 ++---
 arch/unicore32/mm/dma-swiotlb.c                    |   4 +-
 arch/x86/include/asm/dma-mapping.h                 |   5 +-
 arch/x86/include/asm/swiotlb.h                     |   4 +-
 arch/x86/include/asm/xen/page-coherent.h           |   9 +-
 arch/x86/kernel/amd_gart_64.c                      |  20 ++--
 arch/x86/kernel/pci-calgary_64.c                   |  14 +--
 arch/x86/kernel/pci-dma.c                          |   4 +-
 arch/x86/kernel/pci-nommu.c                        |   4 +-
 arch/x86/kernel/pci-swiotlb.c                      |   4 +-
 arch/x86/pci/sta2x11-fixup.c                       |   2 +-
 arch/x86/pci/vmd.c                                 |  16 +--
 arch/xtensa/kernel/pci-dma.c                       |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_fbdev.c          |   2 +-
 drivers/gpu/drm/exynos/exynos_drm_g2d.c            |  12 +-
 drivers/gpu/drm/exynos/exynos_drm_gem.c            |  20 ++--
 drivers/gpu/drm/exynos/exynos_drm_gem.h            |   2 +-
 drivers/gpu/drm/mediatek/mtk_drm_gem.c             |  13 +--
 drivers/gpu/drm/mediatek/mtk_drm_gem.h             |   2 +-
 drivers/gpu/drm/msm/msm_drv.c                      |  13 +--
 .../gpu/drm/nouveau/nvkm/subdev/instmem/gk20a.c    |  13 +--
 drivers/gpu/drm/rockchip/rockchip_drm_gem.c        |  17 ++-
 drivers/gpu/drm/rockchip/rockchip_drm_gem.h        |   2 +-
 drivers/infiniband/core/umem.c                     |   7 +-
 drivers/iommu/amd_iommu.c                          |  12 +-
 drivers/iommu/dma-iommu.c                          |   8 +-
 drivers/iommu/intel-iommu.c                        |  12 +-
 drivers/media/platform/sti/bdisp/bdisp-hw.c        |  26 ++---
 drivers/media/v4l2-core/videobuf2-dma-contig.c     |  30 ++---
 drivers/media/v4l2-core/videobuf2-dma-sg.c         |  19 +--
 drivers/misc/mic/host/mic_boot.c                   |  20 ++--
 drivers/parisc/ccio-dma.c                          |  16 +--
 drivers/parisc/sba_iommu.c                         |  16 +--
 drivers/video/fbdev/omap2/omapfb/omapfb-main.c     |  12 +-
 drivers/video/fbdev/omap2/omapfb/omapfb.h          |   3 +-
 drivers/xen/swiotlb-xen.c                          |  14 +--
 include/linux/dma-attrs.h                          |  71 ------------
 include/linux/dma-iommu.h                          |   6 +-
 include/linux/dma-mapping.h                        | 128 ++++++++++++++-------
 include/linux/swiotlb.h                            |  10 +-
 include/media/videobuf2-dma-contig.h               |   7 +-
 include/rdma/ib_verbs.h                            |   8 +-
 include/xen/swiotlb-xen.h                          |  12 +-
 lib/dma-noop.c                                     |   9 +-
 lib/swiotlb.c                                      |  13 ++-
 108 files changed, 689 insertions(+), 789 deletions(-)
 delete mode 100644 include/linux/dma-attrs.h

-- 
1.9.1



From k.kozlowski at samsung.com  Thu Jun 30 01:25:39 2016
From: k.kozlowski at samsung.com (Krzysztof Kozlowski)
Date: Thu, 30 Jun 2016 10:25:39 +0200
Subject: [PATCH v5 12/44] drm/mediatek: dma-mapping: Use unsigned long for
 dma_attrs
In-Reply-To: <1467275171-6298-1-git-send-email-k.kozlowski@samsung.com>
References: <1467275019-30789-1-git-send-email-k.kozlowski@samsung.com>
 <1467275171-6298-1-git-send-email-k.kozlowski@samsung.com>
Message-ID: <1467275171-6298-12-git-send-email-k.kozlowski@samsung.com>

Split out subsystem specific changes for easier reviews. This will be
squashed with main commit.

Signed-off-by: Krzysztof Kozlowski <k.kozlowski at samsung.com>
---
 drivers/gpu/drm/mediatek/mtk_drm_gem.c | 13 ++++++-------
 drivers/gpu/drm/mediatek/mtk_drm_gem.h |  2 +-
 2 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.c b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
index fa2ec0cd00e8..7abc550ebc00 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.c
@@ -54,15 +54,14 @@ struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev,
 
 	obj = &mtk_gem->base;
 
-	init_dma_attrs(&mtk_gem->dma_attrs);
-	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &mtk_gem->dma_attrs);
+	mtk_gem->dma_attrs = DMA_ATTR_WRITE_COMBINE;
 
 	if (!alloc_kmap)
-		dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &mtk_gem->dma_attrs);
+		mtk_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
 
 	mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size,
 					  &mtk_gem->dma_addr, GFP_KERNEL,
-					  &mtk_gem->dma_attrs);
+					  mtk_gem->dma_attrs);
 	if (!mtk_gem->cookie) {
 		DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size);
 		ret = -ENOMEM;
@@ -93,7 +92,7 @@ void mtk_drm_gem_free_object(struct drm_gem_object *obj)
 		drm_prime_gem_destroy(obj, mtk_gem->sg);
 	else
 		dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie,
-			       mtk_gem->dma_addr, &mtk_gem->dma_attrs);
+			       mtk_gem->dma_addr, mtk_gem->dma_attrs);
 
 	/* release file pointer to gem object. */
 	drm_gem_object_release(obj);
@@ -173,7 +172,7 @@ static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj,
 	vma->vm_pgoff = 0;
 
 	ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie,
-			     mtk_gem->dma_addr, obj->size, &mtk_gem->dma_attrs);
+			     mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs);
 	if (ret)
 		drm_gem_vm_close(vma);
 
@@ -224,7 +223,7 @@ struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj)
 
 	ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie,
 				    mtk_gem->dma_addr, obj->size,
-				    &mtk_gem->dma_attrs);
+				    mtk_gem->dma_attrs);
 	if (ret) {
 		DRM_ERROR("failed to allocate sgt, %d\n", ret);
 		kfree(sgt);
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_gem.h b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
index 3a2a5624a1cb..2752718fa5b2 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_gem.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_gem.h
@@ -35,7 +35,7 @@ struct mtk_drm_gem_obj {
 	void			*cookie;
 	void			*kvaddr;
 	dma_addr_t		dma_addr;
-	struct dma_attrs	dma_attrs;
+	unsigned long		dma_attrs;
 	struct sg_table		*sg;
 };
 
-- 
1.9.1



From tj at kernel.org  Thu Jun 30 02:51:03 2016
From: tj at kernel.org (Tejun Heo)
Date: Thu, 30 Jun 2016 04:51:03 -0500
Subject: [PATCH] PM / suspend: show workqueue state in suspend flow
In-Reply-To: <1467256666-27808-1-git-send-email-roger.lu@mediatek.com>
References: <1467256666-27808-1-git-send-email-roger.lu@mediatek.com>
Message-ID: <20160630095103.GB28719@htj.duckdns.org>

Hello,

On Thu, Jun 30, 2016 at 11:17:46AM +0800, Roger Lu wrote:
> +		if (wq->flags & WQ_FREEZABLE)
> +			pr_info("freezable ");

The flag is already printed below and doing this will make the output
more difficult to read.

>  		pr_info("workqueue %s: flags=0x%x\n", wq->name, wq->flags);

Thanks.

-- 
tejun


From broonie at kernel.org  Wed Jun 29 12:13:22 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:13:22 +0100
Subject: [alsa-devel] [PATCH v5 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <1466149440-23889-7-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
 <1466149440-23889-7-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <20160629191322.GT6247@sirena.org.uk>

On Fri, Jun 17, 2016 at 03:43:57PM +0800, Garlic Tseng wrote:

> +static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
> +				     int div)
> +{

Why are we adding a set_clkdiv() operation?  I would expect the driver
to be able to figure things out automatically.

> +	case DIV_ID_MCLK_TO_BCK:
> +		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
> +		break;
> +	case DIV_ID_BCK_TO_LRCK:
> +		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
> +		break;

Especially in the case where we're configuring LRCLK, that's trivial
when we know the sample rate which we have to know anyway.
-------------- 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-mediatek/attachments/20160629/ec6c3bdd/attachment.sig>

From broonie at kernel.org  Wed Jun 29 12:15:00 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:15:00 +0100
Subject: [alsa-devel] [PATCH v5 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1466149440-23889-8-git-send-email-garlic.tseng@mediatek.com>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
 <1466149440-23889-8-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <20160629191500.GU6247@sirena.org.uk>

On Fri, Jun 17, 2016 at 03:43:58PM +0800, Garlic Tseng wrote:
> Add supports for 16k (wideband BT) and add a general compatible
> string "linux,bt-sco"

This will claim that we support 16k on existing systems which we clearly
don't.  It also seems unwise to advertise multiple rates when we've no
way to configure the rates...  how does the BT controller figure out
what the sample rate is?
-------------- 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-mediatek/attachments/20160629/b693362a/attachment-0001.sig>

From broonie at kernel.org  Wed Jun 29 12:16:13 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:16:13 +0100
Subject: Applied "ASoC: mediatek: add documents for mt2701" to the asoc tree
In-Reply-To: <1464929784-5489-5-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <E1bIKy5-0007Bm-Ix@finisterre>

The patch

   ASoC: mediatek: add documents for mt2701

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From d3986650647d5e5478b4c64c1b01098628577ac1 Mon Sep 17 00:00:00 2001
From: Garlic Tseng <garlic.tseng at mediatek.com>
Date: Fri, 17 Jun 2016 15:43:55 +0800
Subject: [PATCH] ASoC: mediatek: add documents for mt2701

add mt2701-afe-pcm.txt and mt2701-cs42448.txt for mt2701

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 .../devicetree/bindings/sound/mt2701-afe-pcm.txt   | 150 +++++++++++++++++++++
 .../devicetree/bindings/sound/mt2701-cs42448.txt   |  43 ++++++
 2 files changed, 193 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
 create mode 100644 Documentation/devicetree/bindings/sound/mt2701-cs42448.txt

diff --git a/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
new file mode 100644
index 000000000000..3e623a724e55
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-afe-pcm.txt
@@ -0,0 +1,150 @@
+Mediatek AFE PCM controller for mt2701
+
+Required properties:
+- compatible = "mediatek,mt2701-audio";
+- reg: register location and size
+- interrupts: Should contain AFE interrupt
+- clock-names: should have these clock names:
+		"infra_sys_audio_clk",
+		"top_audio_mux1_sel",
+		"top_audio_mux2_sel",
+		"top_audio_mux1_div",
+		"top_audio_mux2_div",
+		"top_audio_48k_timing",
+		"top_audio_44k_timing",
+		"top_audpll_mux_sel",
+		"top_apll_sel",
+		"top_aud1_pll_98M",
+		"top_aud2_pll_90M",
+		"top_hadds2_pll_98M",
+		"top_hadds2_pll_294M",
+		"top_audpll",
+		"top_audpll_d4",
+		"top_audpll_d8",
+		"top_audpll_d16",
+		"top_audpll_d24",
+		"top_audintbus_sel",
+		"clk_26m",
+		"top_syspll1_d4",
+		"top_aud_k1_src_sel",
+		"top_aud_k2_src_sel",
+		"top_aud_k3_src_sel",
+		"top_aud_k4_src_sel",
+		"top_aud_k5_src_sel",
+		"top_aud_k6_src_sel",
+		"top_aud_k1_src_div",
+		"top_aud_k2_src_div",
+		"top_aud_k3_src_div",
+		"top_aud_k4_src_div",
+		"top_aud_k5_src_div",
+		"top_aud_k6_src_div",
+		"top_aud_i2s1_mclk",
+		"top_aud_i2s2_mclk",
+		"top_aud_i2s3_mclk",
+		"top_aud_i2s4_mclk",
+		"top_aud_i2s5_mclk",
+		"top_aud_i2s6_mclk",
+		"top_asm_m_sel",
+		"top_asm_h_sel",
+		"top_univpll2_d4",
+		"top_univpll2_d2",
+		"top_syspll_d5";
+
+Example:
+
+	afe: mt2701-afe-pcm at 11220000 {
+		compatible = "mediatek,mt2701-audio";
+		reg = <0 0x11220000 0 0x2000>,
+		      <0 0x112A0000 0 0x20000>;
+		interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_LOW>,
+			     <GIC_SPI 132 IRQ_TYPE_LEVEL_LOW>;
+		clocks = <&infracfg CLK_INFRA_AUDIO>,
+			 <&topckgen CLK_TOP_AUD_MUX1_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX2_SEL>,
+			 <&topckgen CLK_TOP_AUD_MUX1_DIV>,
+			 <&topckgen CLK_TOP_AUD_MUX2_DIV>,
+			 <&topckgen CLK_TOP_AUD_48K_TIMING>,
+			 <&topckgen CLK_TOP_AUD_44K_TIMING>,
+			 <&topckgen CLK_TOP_AUDPLL_MUX_SEL>,
+			 <&topckgen CLK_TOP_APLL_SEL>,
+			 <&topckgen CLK_TOP_AUD1PLL_98M>,
+			 <&topckgen CLK_TOP_AUD2PLL_90M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_98M>,
+			 <&topckgen CLK_TOP_HADDS2PLL_294M>,
+			 <&topckgen CLK_TOP_AUDPLL>,
+			 <&topckgen CLK_TOP_AUDPLL_D4>,
+			 <&topckgen CLK_TOP_AUDPLL_D8>,
+			 <&topckgen CLK_TOP_AUDPLL_D16>,
+			 <&topckgen CLK_TOP_AUDPLL_D24>,
+			 <&topckgen CLK_TOP_AUDINTBUS_SEL>,
+			 <&clk26m>,
+			 <&topckgen CLK_TOP_SYSPLL1_D4>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_SEL>,
+			 <&topckgen CLK_TOP_AUD_K1_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K2_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K3_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K4_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K5_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_K6_SRC_DIV>,
+			 <&topckgen CLK_TOP_AUD_I2S1_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S2_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S3_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S4_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S5_MCLK>,
+			 <&topckgen CLK_TOP_AUD_I2S6_MCLK>,
+			 <&topckgen CLK_TOP_ASM_M_SEL>,
+			 <&topckgen CLK_TOP_ASM_H_SEL>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D4>,
+			 <&topckgen CLK_TOP_UNIVPLL2_D2>,
+			 <&topckgen CLK_TOP_SYSPLL_D5>;
+
+		clock-names = "infra_sys_audio_clk",
+			      "top_audio_mux1_sel",
+			      "top_audio_mux2_sel",
+			      "top_audio_mux1_div",
+			      "top_audio_mux2_div",
+			      "top_audio_48k_timing",
+			      "top_audio_44k_timing",
+			      "top_audpll_mux_sel",
+			      "top_apll_sel",
+			      "top_aud1_pll_98M",
+			      "top_aud2_pll_90M",
+			      "top_hadds2_pll_98M",
+			      "top_hadds2_pll_294M",
+			      "top_audpll",
+			      "top_audpll_d4",
+			      "top_audpll_d8",
+			      "top_audpll_d16",
+			      "top_audpll_d24",
+			      "top_audintbus_sel",
+			      "clk_26m",
+			      "top_syspll1_d4",
+			      "top_aud_k1_src_sel",
+			      "top_aud_k2_src_sel",
+			      "top_aud_k3_src_sel",
+			      "top_aud_k4_src_sel",
+			      "top_aud_k5_src_sel",
+			      "top_aud_k6_src_sel",
+			      "top_aud_k1_src_div",
+			      "top_aud_k2_src_div",
+			      "top_aud_k3_src_div",
+			      "top_aud_k4_src_div",
+			      "top_aud_k5_src_div",
+			      "top_aud_k6_src_div",
+			      "top_aud_i2s1_mclk",
+			      "top_aud_i2s2_mclk",
+			      "top_aud_i2s3_mclk",
+			      "top_aud_i2s4_mclk",
+			      "top_aud_i2s5_mclk",
+			      "top_aud_i2s6_mclk",
+			      "top_asm_m_sel",
+			      "top_asm_h_sel",
+			      "top_univpll2_d4",
+			      "top_univpll2_d2",
+			      "top_syspll_d5";
+	};
diff --git a/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
new file mode 100644
index 000000000000..05574446ceb6
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/mt2701-cs42448.txt
@@ -0,0 +1,43 @@
+MT2701 with CS42448 CODEC
+
+Required properties:
+- compatible: "mediatek,mt2701-cs42448-machine"
+- mediatek,platform: the phandle of MT2701 ASoC platform
+- audio-routing: a list of the connections between audio
+- mediatek,audio-codec: the phandles of cs42448 codec
+- mediatek,audio-codec-bt-mrg the phandles of bt-sco dummy codec
+- pinctrl-names: Should contain only one value - "default"
+- pinctrl-0: Should specify pin control groups used for this controller.
+- i2s1-in-sel-gpio1, i2s1-in-sel-gpio2: Should specify two gpio pins to
+					control I2S1-in mux.
+
+Example:
+
+	sound:sound {
+		compatible = "mediatek,mt2701-cs42448-machine";
+		mediatek,platform = <&afe>;
+		/* CS42448 Machine name */
+		audio-routing =
+			"Line Out Jack", "AOUT1L",
+			"Line Out Jack", "AOUT1R",
+			"Line Out Jack", "AOUT2L",
+			"Line Out Jack", "AOUT2R",
+			"Line Out Jack", "AOUT3L",
+			"Line Out Jack", "AOUT3R",
+			"Line Out Jack", "AOUT4L",
+			"Line Out Jack", "AOUT4R",
+			"AIN1L", "AMIC",
+			"AIN1R", "AMIC",
+			"AIN2L", "Tuner In",
+			"AIN2R", "Tuner In",
+			"AIN3L", "Satellite Tuner In",
+			"AIN3R", "Satellite Tuner In",
+			"AIN3L", "AUX In",
+			"AIN3R", "AUX In";
+		mediatek,audio-codec = <&cs42448>;
+		mediatek,audio-codec-bt-mrg = <&bt_sco_codec>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&aud_pins_default>;
+		i2s1-in-sel-gpio1 = <&pio 53 0>;
+		i2s1-in-sel-gpio2 = <&pio 54 0>;
+	};
-- 
2.8.1



From broonie at kernel.org  Wed Jun 29 12:16:36 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:16:36 +0100
Subject: Applied "ASoC: mediatek: implement mediatek common structure" to the
 asoc tree
In-Reply-To: <1465802806-19197-3-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <E1bIKyS-0007E7-4H@finisterre>

The patch

   ASoC: mediatek: implement mediatek common structure

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 283b612429a279b4c8f5a90f38d26c80bf8ec628 Mon Sep 17 00:00:00 2001
From: Garlic Tseng <garlic.tseng at mediatek.com>
Date: Fri, 17 Jun 2016 15:43:53 +0800
Subject: [PATCH] ASoC: mediatek: implement mediatek common structure

implement mediatek basic structure, include common private data,
afe fe dai operator and afe platform driver.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 sound/soc/mediatek/common/mtk-afe-fe-dai.c         | 379 +++++++++++++++++++++
 sound/soc/mediatek/common/mtk-afe-fe-dai.h         |  45 +++
 .../soc/mediatek/common/mtk-afe-platform-driver.c  |  90 +++++
 .../soc/mediatek/common/mtk-afe-platform-driver.h  |  23 ++
 sound/soc/mediatek/common/mtk-base-afe.h           | 104 ++++++
 5 files changed, 641 insertions(+)
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-fe-dai.h
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.c
 create mode 100644 sound/soc/mediatek/common/mtk-afe-platform-driver.h
 create mode 100644 sound/soc/mediatek/common/mtk-base-afe.h

diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
new file mode 100644
index 000000000000..b788791b0a35
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,379 @@
+/*
+ * mtk-afe-fe-dais.c  --  Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "mtk-afe-fe-dai.h"
+#include "mtk-base-afe.h"
+
+#define AFE_BASE_END_OFFSET 8
+
+int mtk_regmap_update_bits(struct regmap *map, int reg, unsigned int mask,
+			   unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_update_bits(map, reg, mask, val);
+}
+
+int mtk_regmap_write(struct regmap *map, int reg, unsigned int val)
+{
+	if (reg < 0)
+		return 0;
+	return regmap_write(map, reg, val);
+}
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int memif_num = rtd->cpu_dai->id;
+	struct mtk_base_afe_memif *memif = &afe->memif[memif_num];
+	const struct snd_pcm_hardware *mtk_afe_hardware = afe->mtk_afe_hardware;
+	int ret;
+
+	memif->substream = substream;
+
+	snd_pcm_hw_constraint_step(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	/* enable agent */
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       0 << memif->data->agent_disable_shift);
+
+	snd_soc_set_runtime_hwparams(substream, mtk_afe_hardware);
+
+	/*
+	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
+	 * smaller than period_size due to AFE's internal buffer.
+	 * This easily leads to overrun when avail_min is period_size.
+	 * One more period can hold the possible unread buffer.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		int periods_max = mtk_afe_hardware->periods_max;
+
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+						   SNDRV_PCM_HW_PARAM_PERIODS,
+						   3, periods_max);
+		if (ret < 0) {
+			dev_err(afe->dev, "hw_constraint_minmax failed\n");
+			return ret;
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
+
+	/* dynamic allocate irq to memif */
+	if (memif->irq_usage < 0) {
+		int irq_id = mtk_dynamic_irq_acquire(afe);
+
+		if (irq_id != afe->irqs_size) {
+			/* link */
+			memif->irq_usage = irq_id;
+		} else {
+			dev_err(afe->dev, "%s() error: no more asys irq\n",
+				__func__);
+			ret = -EBUSY;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_startup);
+
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int irq_id;
+
+	irq_id = memif->irq_usage;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+			       1 << memif->data->agent_disable_shift,
+			       1 << memif->data->agent_disable_shift);
+
+	if (!memif->const_irq) {
+		mtk_dynamic_irq_release(afe, irq_id);
+		memif->irq_usage = -1;
+		memif->substream = NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_shutdown);
+
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int msb_at_bit33 = 0;
+	int ret, fs = 0;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+	memif->buffer_size = substream->runtime->dma_bytes;
+
+	/* start */
+	mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+			 memif->phys_buf_addr);
+	/* end */
+	mtk_regmap_write(afe->regmap,
+			 memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+			 memif->phys_buf_addr + memif->buffer_size - 1);
+
+	/* set MSB to 33-bit */
+	mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+			       1 << memif->data->msb_shift,
+			       msb_at_bit33 << memif->data->msb_shift);
+
+	/* set channel */
+	if (memif->data->mono_shift >= 0) {
+		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+		mtk_regmap_update_bits(afe->regmap, memif->data->mono_reg,
+				       1 << memif->data->mono_shift,
+				       mono << memif->data->mono_shift);
+	}
+
+	/* set rate */
+	if (memif->data->fs_shift < 0)
+		return 0;
+
+	fs = afe->memif_fs(substream, params_rate(params));
+
+	if (fs < 0)
+		return -EINVAL;
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+			       memif->data->fs_maskbit << memif->data->fs_shift,
+			       fs << memif->data->fs_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_params);
+
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_hw_free);
+
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime * const runtime = substream->runtime;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
+	const struct mtk_base_irq_data *irq_data = irqs->irq_data;
+	unsigned int counter = runtime->period_size;
+	int fs;
+
+	dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (memif->data->enable_shift >= 0)
+			mtk_regmap_update_bits(afe->regmap,
+					       memif->data->enable_reg,
+					       1 << memif->data->enable_shift,
+					       1 << memif->data->enable_shift);
+
+		/* set irq counter */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+				       irq_data->irq_cnt_maskbit
+				       << irq_data->irq_cnt_shift,
+				       counter << irq_data->irq_cnt_shift);
+
+		/* set irq fs */
+		fs = afe->irq_fs(substream, runtime->rate);
+
+		if (fs < 0)
+			return -EINVAL;
+
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+				       irq_data->irq_fs_maskbit
+				       << irq_data->irq_fs_shift,
+				       fs << irq_data->irq_fs_shift);
+
+		/* enable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       1 << irq_data->irq_en_shift);
+
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+				       1 << memif->data->enable_shift, 0);
+		/* disable interrupt */
+		mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+				       1 << irq_data->irq_en_shift,
+				       0 << irq_data->irq_en_shift);
+		/* and clear pending IRQ */
+		mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+				 1 << irq_data->irq_clr_shift);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_trigger);
+
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd  = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int hd_audio = 0;
+
+	/* set hd mode */
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		hd_audio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		hd_audio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		hd_audio = 1;
+		break;
+	default:
+		dev_err(afe->dev, "%s() error: unsupported format %d\n",
+			__func__, substream->runtime->format);
+		break;
+	}
+
+	mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+			       1 << memif->data->hd_shift,
+			       hd_audio << memif->data->hd_shift);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_fe_prepare);
+
+const struct snd_soc_dai_ops mtk_afe_fe_ops = {
+	.startup	= mtk_afe_fe_startup,
+	.shutdown	= mtk_afe_fe_shutdown,
+	.hw_params	= mtk_afe_fe_hw_params,
+	.hw_free	= mtk_afe_fe_hw_free,
+	.prepare	= mtk_afe_fe_prepare,
+	.trigger	= mtk_afe_fe_trigger,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_fe_ops);
+
+static DEFINE_MUTEX(irqs_lock);
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe)
+{
+	int i;
+
+	mutex_lock(&afe->irq_alloc_lock);
+	for (i = 0; i < afe->irqs_size; ++i) {
+		if (afe->irqs[i].irq_occupyed == 0) {
+			afe->irqs[i].irq_occupyed = 1;
+			mutex_unlock(&afe->irq_alloc_lock);
+			return i;
+		}
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return afe->irqs_size;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_acquire);
+
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id)
+{
+	mutex_lock(&afe->irq_alloc_lock);
+	if (irq_id >= 0 && irq_id < afe->irqs_size) {
+		afe->irqs[irq_id].irq_occupyed = 0;
+		mutex_unlock(&afe->irq_alloc_lock);
+		return 0;
+	}
+	mutex_unlock(&afe->irq_alloc_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtk_dynamic_irq_release);
+
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i;
+
+	if (pm_runtime_status_suspended(dev) || afe->suspended)
+		return 0;
+
+	if (!afe->reg_back_up)
+		afe->reg_back_up =
+			devm_kcalloc(dev, afe->reg_back_up_list_num,
+				     sizeof(unsigned int), GFP_KERNEL);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		regmap_read(regmap, afe->reg_back_up_list[i],
+			    &afe->reg_back_up[i]);
+
+	afe->suspended = true;
+	afe->runtime_suspend(dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+	struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+	struct device *dev = afe->dev;
+	struct regmap *regmap = afe->regmap;
+	int i = 0;
+
+	if (pm_runtime_status_suspended(dev) || !afe->suspended)
+		return 0;
+
+	afe->runtime_resume(dev);
+
+	if (!afe->reg_back_up)
+		dev_dbg(dev, "%s no reg_backup\n", __func__);
+
+	for (i = 0; i < afe->reg_back_up_list_num; i++)
+		mtk_regmap_write(regmap, afe->reg_back_up_list[i],
+				 afe->reg_back_up[i]);
+
+	afe->suspended = false;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_afe_dai_resume);
+
+MODULE_DESCRIPTION("Mediatek simple fe dai operator");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
new file mode 100644
index 000000000000..28cb17854da1
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h  --  Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_FE_DAI_H_
+#define _MTK_AFE_FE_DAI_H_
+
+struct snd_soc_dai_ops;
+struct mtk_base_afe;
+struct mtk_base_afe_memif;
+
+int mtk_afe_fe_startup(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+void mtk_afe_fe_shutdown(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params,
+			 struct snd_soc_dai *dai);
+int mtk_afe_fe_hw_free(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_prepare(struct snd_pcm_substream *substream,
+		       struct snd_soc_dai *dai);
+int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
+		       struct snd_soc_dai *dai);
+
+extern const struct snd_soc_dai_ops mtk_afe_fe_ops;
+
+int mtk_dynamic_irq_acquire(struct mtk_base_afe *afe);
+int mtk_dynamic_irq_release(struct mtk_base_afe *afe, int irq_id);
+int mtk_afe_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#endif
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
new file mode 100644
index 000000000000..82d439c15f4e
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c  --  Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+static snd_pcm_uframes_t mtk_afe_pcm_pointer
+			 (struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	const struct mtk_base_memif_data *memif_data = memif->data;
+	struct regmap *regmap = afe->regmap;
+	struct device *dev = afe->dev;
+	int reg_ofs_base = memif_data->reg_ofs_base;
+	int reg_ofs_cur = memif_data->reg_ofs_cur;
+	unsigned int hw_ptr = 0, hw_base = 0;
+	int ret, pcm_ptr_bytes;
+
+	ret = regmap_read(regmap, reg_ofs_cur, &hw_ptr);
+	if (ret || hw_ptr == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	ret = regmap_read(regmap, reg_ofs_base, &hw_base);
+	if (ret || hw_base == 0) {
+		dev_err(dev, "%s hw_ptr err\n", __func__);
+		pcm_ptr_bytes = 0;
+		goto POINTER_RETURN_FRAMES;
+	}
+
+	pcm_ptr_bytes = hw_ptr - hw_base;
+
+POINTER_RETURN_FRAMES:
+	return bytes_to_frames(substream->runtime, pcm_ptr_bytes);
+}
+
+static const struct snd_pcm_ops mtk_afe_pcm_ops = {
+	.ioctl = snd_pcm_lib_ioctl,
+	.pointer = mtk_afe_pcm_pointer,
+};
+
+static int mtk_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	size_t size;
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+	size = afe->mtk_afe_hardware->buffer_bytes_max;
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						     card->dev, size, size);
+}
+
+static void mtk_afe_pcm_free(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+const struct snd_soc_platform_driver mtk_afe_pcm_platform = {
+	.ops = &mtk_afe_pcm_ops,
+	.pcm_new = mtk_afe_pcm_new,
+	.pcm_free = mtk_afe_pcm_free,
+};
+EXPORT_SYMBOL_GPL(mtk_afe_pcm_platform);
+
+MODULE_DESCRIPTION("Mediatek simple platform driver");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
+
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.h b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
new file mode 100644
index 000000000000..a973fc9253b4
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h  --  Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_AFE_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#endif
+
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
new file mode 100644
index 000000000000..3a78f6f17195
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h  --  Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MTK_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+	int id;
+	const char *name;
+	int reg_ofs_base;
+	int reg_ofs_cur;
+	int fs_reg;
+	int fs_shift;
+	int fs_maskbit;
+	int mono_reg;
+	int mono_shift;
+	int enable_reg;
+	int enable_shift;
+	int hd_reg;
+	int hd_shift;
+	int msb_reg;
+	int msb_shift;
+	int agent_disable_reg;
+	int agent_disable_shift;
+};
+
+struct mtk_base_irq_data {
+	int id;
+	int irq_cnt_reg;
+	int irq_cnt_shift;
+	int irq_cnt_maskbit;
+	int irq_fs_reg;
+	int irq_fs_shift;
+	int irq_fs_maskbit;
+	int irq_en_reg;
+	int irq_en_shift;
+	int irq_clr_reg;
+	int irq_clr_shift;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+struct regmap;
+struct snd_pcm_substream;
+struct snd_soc_dai;
+
+struct mtk_base_afe {
+	void __iomem *base_addr;
+	struct device *dev;
+	struct regmap *regmap;
+	struct mutex irq_alloc_lock; /* dynamic alloc irq lock */
+
+	unsigned int const *reg_back_up_list;
+	unsigned int *reg_back_up;
+	unsigned int reg_back_up_list_num;
+
+	int (*runtime_suspend)(struct device *dev);
+	int (*runtime_resume)(struct device *dev);
+	bool suspended;
+
+	struct mtk_base_afe_memif *memif;
+	int memif_size;
+	struct mtk_base_afe_irq *irqs;
+	int irqs_size;
+
+	const struct snd_pcm_hardware *mtk_afe_hardware;
+	int (*memif_fs)(struct snd_pcm_substream *substream,
+			unsigned int rate);
+	int (*irq_fs)(struct snd_pcm_substream *substream,
+		      unsigned int rate);
+
+	void *platform_priv;
+};
+
+struct mtk_base_afe_memif {
+	unsigned int phys_buf_addr;
+	int buffer_size;
+	struct snd_pcm_substream *substream;
+	const struct mtk_base_memif_data *data;
+	int irq_usage;
+	int const_irq;
+};
+
+struct mtk_base_afe_irq {
+	const struct mtk_base_irq_data *irq_data;
+	int irq_occupyed;
+};
+
+#endif
+
-- 
2.8.1



From broonie at kernel.org  Wed Jun 29 12:16:07 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:16:07 +0100
Subject: Applied "ASoC: mediatek: add structure define and clock control for
 2701" to the asoc tree
In-Reply-To: <1465799052-9657-6-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <E1bIKxz-0007Ai-TG@finisterre>

The patch

   ASoC: mediatek: add structure define and clock control for 2701

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From d6f3710a56e10b42945ed2dbcca71d2748174299 Mon Sep 17 00:00:00 2001
From: Garlic Tseng <garlic.tseng at mediatek.com>
Date: Fri, 17 Jun 2016 15:43:56 +0800
Subject: [PATCH] ASoC: mediatek: add structure define and clock control for
 2701

add structure define and clock control function for 2701.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c | 464 ++++++++++++++++++++++
 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h |  38 ++
 sound/soc/mediatek/mt2701/mt2701-afe-common.h     | 181 +++++++++
 sound/soc/mediatek/mt2701/mt2701-reg.h            | 186 +++++++++
 4 files changed, 869 insertions(+)
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-afe-common.h
 create mode 100644 sound/soc/mediatek/mt2701/mt2701-reg.h

diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
new file mode 100644
index 000000000000..b815ecc6bbf6
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c  --  Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+	[MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+	[MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+	[MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+	[MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+	[MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+	[MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+	[MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+	[MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+	[MT2701_AUD_APLL_SEL] = "top_apll_sel",
+	[MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+	[MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+	[MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+	[MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+	[MT2701_AUD_AUDPLL] = "top_audpll",
+	[MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+	[MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+	[MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+	[MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+	[MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+	[MT2701_AUD_CLK_26M] = "clk_26m",
+	[MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+	[MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+	[MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+	[MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+	[MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+	[MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+	[MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+	[MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+	[MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+	[MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+	[MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+	[MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+	[MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+	[MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+	[MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+	[MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+	[MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+	[MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+	[MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+	[MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+	[MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+	[MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+	[MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+	[MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int i = 0;
+
+	for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(aud_clks[i])) {
+			dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+				 __func__, aud_clks[i]);
+			return PTR_ERR(aud_clks[i]);
+		}
+	}
+
+	return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+	int ret = 0;
+
+	ret = mt2701_turn_on_a1sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_a2sys_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		return ret;
+	}
+
+	ret = mt2701_turn_on_afe_clock(afe);
+	if (ret) {
+		dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+			__func__, ret);
+		mt2701_turn_off_a1sys_clock(afe);
+		mt2701_turn_off_a2sys_clock(afe);
+		return ret;
+	}
+
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON,
+			   AFE_DAC_CON0_AFE_ON);
+	regmap_write(afe->regmap, PWR2_TOP_CON,
+		     PWR2_TOP_CON_INIT_VAL);
+	regmap_write(afe->regmap, PWR1_ASM_CON1,
+		     PWR1_ASM_CON1_INIT_VAL);
+	regmap_write(afe->regmap, PWR2_ASM_CON1,
+		     PWR2_ASM_CON1_INIT_VAL);
+
+	return 0;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+	mt2701_turn_off_afe_clock(afe);
+	mt2701_turn_off_a1sys_clock(afe);
+	mt2701_turn_off_a2sys_clock(afe);
+	regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+			   AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+	regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+			   AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+			aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+		goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+			   MT2701_AUD_AUD_MUX1_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+			MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+		goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+		goto A1SYS_CLK_AUD_48K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A1SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A1SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret = 0;
+
+	/* Set Mux */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+			     afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+			aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+		goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+	}
+
+	/* Set Divider */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+			   MT2701_AUD_AUD_MUX2_DIV_RATE);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+			MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+		goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+	}
+
+	/* Enable clock gate */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+		goto A2SYS_CLK_AUD_44K_ERR;
+	}
+
+	/* Enable infra audio */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto A2SYS_CLK_INFRA_ERR;
+	}
+
+	return 0;
+
+A2SYS_CLK_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+	return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+
+	/* enable INFRA_SYS */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+		goto AFE_AUD_INFRA_ERR;
+	}
+
+	/* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+			     afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_AUDINTBUS],
+			aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+		goto AFE_AUD_AUDINTBUS_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_H_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+		goto AFE_AUD_ASM_H_ERR;
+	}
+
+	/* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+	ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+			     afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+	if (ret) {
+		dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+			aud_clks[MT2701_AUD_ASM_M_SEL],
+			aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+		goto AFE_AUD_ASM_M_ERR;
+	}
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS, 0);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+	return 0;
+
+AFE_AUD_ASM_M_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+	clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+			   AUDIO_TOP_CON0_PDN_APLL_CK,
+			   AUDIO_TOP_CON0_PDN_APLL_CK);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A1SYS,
+			   AUDIO_TOP_CON4_PDN_A1SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_A2SYS,
+			   AUDIO_TOP_CON4_PDN_A2SYS);
+	regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN,
+			   AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk)
+{
+	struct mt2701_afe_private *afe_priv = afe->platform_priv;
+	int ret;
+	int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+	int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+	/* Set MCLK Kx_SRC_SEL(domain) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_clk_id], ret);
+
+	if (domain == 0) {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+	} else {
+		ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+				     afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+		if (ret)
+			dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+				__func__, aud_clks[aud_src_clk_id],
+				aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+	}
+	clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+	/* Set MCLK Kx_SRC_DIV(divider) */
+	ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+	if (ret)
+		dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+			__func__, aud_clks[aud_src_div_id], ret);
+
+	ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+	if (ret)
+		dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+			aud_clks[aud_src_div_id], mclk, ret);
+	clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng at mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
new file mode 100644
index 000000000000..6497d570cf09
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h  --  Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+			       int mclk);
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-common.h b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
new file mode 100644
index 000000000000..c77166eb7132
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,181 @@
+/*
+ * mt2701-afe-common.h  --  Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT_2701_AFE_COMMON_H_
+#define _MT_2701_AFE_COMMON_H_
+#include <sound/soc.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include "mt2701-reg.h"
+#include "../common/mtk-base-afe.h"
+
+#define MT2701_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE	98304000
+#define MT2701_PLL_DOMAIN_1_RATE	90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+	MT2701_I2S_1,
+	MT2701_I2S_2,
+	MT2701_I2S_3,
+	MT2701_I2S_4,
+	MT2701_I2S_NUM,
+};
+
+enum {
+	MT2701_MEMIF_DL1,
+	MT2701_MEMIF_DL2,
+	MT2701_MEMIF_DL3,
+	MT2701_MEMIF_DL4,
+	MT2701_MEMIF_DL5,
+	MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_DLM = MT2701_MEMIF_DL_SINGLE_NUM,
+	MT2701_MEMIF_UL1,
+	MT2701_MEMIF_UL2,
+	MT2701_MEMIF_UL3,
+	MT2701_MEMIF_UL4,
+	MT2701_MEMIF_UL5,
+	MT2701_MEMIF_DLBT,
+	MT2701_MEMIF_ULBT,
+	MT2701_MEMIF_NUM,
+	MT2701_IO_I2S = MT2701_MEMIF_NUM,
+	MT2701_IO_2ND_I2S,
+	MT2701_IO_3RD_I2S,
+	MT2701_IO_4TH_I2S,
+	MT2701_IO_5TH_I2S,
+	MT2701_IO_6TH_I2S,
+	MT2701_IO_MRG,
+};
+
+enum {
+	MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+	MT2701_IRQ_ASYS_IRQ2,
+	MT2701_IRQ_ASYS_IRQ3,
+	MT2701_IRQ_ASYS_END,
+};
+
+enum {
+	DIV_ID_MCLK_TO_BCK,
+	DIV_ID_BCK_TO_LRCK,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+	MT2701_AUD_INFRA_SYS_AUDIO,
+	MT2701_AUD_AUD_MUX1_SEL,
+	MT2701_AUD_AUD_MUX2_SEL,
+	MT2701_AUD_AUD_MUX1_DIV,
+	MT2701_AUD_AUD_MUX2_DIV,
+	MT2701_AUD_AUD_48K_TIMING,
+	MT2701_AUD_AUD_44K_TIMING,
+	MT2701_AUD_AUDPLL_MUX_SEL,
+	MT2701_AUD_APLL_SEL,
+	MT2701_AUD_AUD1PLL_98M,
+	MT2701_AUD_AUD2PLL_90M,
+	MT2701_AUD_HADDS2PLL_98M,
+	MT2701_AUD_HADDS2PLL_294M,
+	MT2701_AUD_AUDPLL,
+	MT2701_AUD_AUDPLL_D4,
+	MT2701_AUD_AUDPLL_D8,
+	MT2701_AUD_AUDPLL_D16,
+	MT2701_AUD_AUDPLL_D24,
+	MT2701_AUD_AUDINTBUS,
+	MT2701_AUD_CLK_26M,
+	MT2701_AUD_SYSPLL1_D4,
+	MT2701_AUD_AUD_K1_SRC_SEL,
+	MT2701_AUD_AUD_K2_SRC_SEL,
+	MT2701_AUD_AUD_K3_SRC_SEL,
+	MT2701_AUD_AUD_K4_SRC_SEL,
+	MT2701_AUD_AUD_K5_SRC_SEL,
+	MT2701_AUD_AUD_K6_SRC_SEL,
+	MT2701_AUD_AUD_K1_SRC_DIV,
+	MT2701_AUD_AUD_K2_SRC_DIV,
+	MT2701_AUD_AUD_K3_SRC_DIV,
+	MT2701_AUD_AUD_K4_SRC_DIV,
+	MT2701_AUD_AUD_K5_SRC_DIV,
+	MT2701_AUD_AUD_K6_SRC_DIV,
+	MT2701_AUD_AUD_I2S1_MCLK,
+	MT2701_AUD_AUD_I2S2_MCLK,
+	MT2701_AUD_AUD_I2S3_MCLK,
+	MT2701_AUD_AUD_I2S4_MCLK,
+	MT2701_AUD_AUD_I2S5_MCLK,
+	MT2701_AUD_AUD_I2S6_MCLK,
+	MT2701_AUD_ASM_M_SEL,
+	MT2701_AUD_ASM_H_SEL,
+	MT2701_AUD_UNIVPLL2_D4,
+	MT2701_AUD_UNIVPLL2_D2,
+	MT2701_AUD_SYSPLL_D5,
+	MT2701_CLOCK_NUM
+};
+
+static const unsigned int mt2701_afe_backup_list[] = {
+	AUDIO_TOP_CON0,
+	AUDIO_TOP_CON4,
+	AUDIO_TOP_CON5,
+	ASYS_TOP_CON,
+	AFE_CONN0,
+	AFE_CONN1,
+	AFE_CONN2,
+	AFE_CONN3,
+	AFE_CONN15,
+	AFE_CONN16,
+	AFE_CONN17,
+	AFE_CONN18,
+	AFE_CONN19,
+	AFE_CONN20,
+	AFE_CONN21,
+	AFE_CONN22,
+	AFE_DAC_CON0,
+	AFE_MEMIF_PBUF_SIZE,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+	int i2s_ctrl_reg;
+	int i2s_pwn_shift;
+	int i2s_asrc_fs_shift;
+	int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+	I2S_OUT,
+	I2S_IN,
+	I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+	int dai_id;
+	int mclk_rate;
+	int div_mclk_to_bck;
+	int div_bck_to_lrck;
+	int format;
+	snd_pcm_format_t stream_fmt;
+	int on[I2S_DIR_NUM];
+	int occupied[I2S_DIR_NUM];
+	const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+	struct clk *clocks[MT2701_CLOCK_NUM];
+	struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+	bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#endif
diff --git a/sound/soc/mediatek/mt2701/mt2701-reg.h b/sound/soc/mediatek/mt2701/mt2701-reg.h
new file mode 100644
index 000000000000..bb62b1c55957
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h  --  Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng at mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include "mt2701-afe-common.h"
+
+/*****************************************************************************
+ *                  R E G I S T E R       D E F I N I T I O N
+ *****************************************************************************/
+#define AUDIO_TOP_CON0 0x0000
+#define AUDIO_TOP_CON4 0x0010
+#define AUDIO_TOP_CON5 0x0014
+#define AFE_DAIBT_CON0 0x001c
+#define AFE_MRGIF_CON 0x003c
+#define ASMI_TIMING_CON1 0x0100
+#define ASMO_TIMING_CON1 0x0104
+#define PWR1_ASM_CON1 0x0108
+#define ASYS_TOP_CON 0x0600
+#define ASYS_I2SIN1_CON 0x0604
+#define ASYS_I2SIN2_CON 0x0608
+#define ASYS_I2SIN3_CON 0x060c
+#define ASYS_I2SIN4_CON 0x0610
+#define ASYS_I2SIN5_CON 0x0614
+#define ASYS_I2SO1_CON 0x061C
+#define ASYS_I2SO2_CON 0x0620
+#define ASYS_I2SO3_CON 0x0624
+#define ASYS_I2SO4_CON 0x0628
+#define ASYS_I2SO5_CON 0x062c
+#define PWR2_TOP_CON 0x0634
+#define AFE_CONN0 0x06c0
+#define AFE_CONN1 0x06c4
+#define AFE_CONN2 0x06c8
+#define AFE_CONN3 0x06cc
+#define AFE_CONN14 0x06f8
+#define AFE_CONN15 0x06fc
+#define AFE_CONN16 0x0700
+#define AFE_CONN17 0x0704
+#define AFE_CONN18 0x0708
+#define AFE_CONN19 0x070c
+#define AFE_CONN20 0x0710
+#define AFE_CONN21 0x0714
+#define AFE_CONN22 0x0718
+#define AFE_CONN23 0x071c
+#define AFE_CONN24 0x0720
+#define AFE_CONN41 0x0764
+#define ASYS_IRQ1_CON 0x0780
+#define ASYS_IRQ2_CON 0x0784
+#define ASYS_IRQ3_CON 0x0788
+#define ASYS_IRQ_CLR 0x07c0
+#define ASYS_IRQ_STATUS 0x07c4
+#define PWR2_ASM_CON1 0x1070
+#define AFE_DAC_CON0 0x1200
+#define AFE_DAC_CON1 0x1204
+#define AFE_DAC_CON2 0x1208
+#define AFE_DAC_CON3 0x120c
+#define AFE_DAC_CON4 0x1210
+#define AFE_MEMIF_HD_CON1 0x121c
+#define AFE_MEMIF_PBUF_SIZE 0x1238
+#define AFE_MEMIF_HD_CON0 0x123c
+#define AFE_DL1_BASE 0x1240
+#define AFE_DL1_CUR 0x1244
+#define AFE_DL2_BASE 0x1250
+#define AFE_DL2_CUR 0x1254
+#define AFE_DL3_BASE 0x1260
+#define AFE_DL3_CUR 0x1264
+#define AFE_DL4_BASE 0x1270
+#define AFE_DL4_CUR 0x1274
+#define AFE_DL5_BASE 0x1280
+#define AFE_DL5_CUR 0x1284
+#define AFE_DLMCH_BASE 0x12a0
+#define AFE_DLMCH_CUR 0x12a4
+#define AFE_ARB1_BASE 0x12b0
+#define AFE_ARB1_CUR 0x12b4
+#define AFE_VUL_BASE 0x1300
+#define AFE_VUL_CUR 0x130c
+#define AFE_UL2_BASE 0x1310
+#define AFE_UL2_END 0x1318
+#define AFE_UL2_CUR 0x131c
+#define AFE_UL3_BASE 0x1320
+#define AFE_UL3_END 0x1328
+#define AFE_UL3_CUR 0x132c
+#define AFE_UL4_BASE 0x1330
+#define AFE_UL4_END 0x1338
+#define AFE_UL4_CUR 0x133c
+#define AFE_UL5_BASE 0x1340
+#define AFE_UL5_END 0x1348
+#define AFE_UL5_CUR 0x134c
+#define AFE_DAI_BASE 0x1370
+#define AFE_DAI_CUR 0x137c
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON	(0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE		(0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK	(0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN	(0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS	(0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS	(0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN	(0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF	(0x1 << 25)
+
+/* AFE_DAIBT_CON0 (0x001c) */
+#define AFE_DAIBT_CON0_DAIBT_EN		(0x1 << 0)
+#define AFE_DAIBT_CON0_BT_FUNC_EN	(0x1 << 1)
+#define AFE_DAIBT_CON0_BT_FUNC_RDY	(0x1 << 3)
+#define AFE_DAIBT_CON0_BT_WIDE_MODE_EN	(0x1 << 9)
+#define AFE_DAIBT_CON0_MRG_USE		(0x1 << 12)
+
+/* PWR1_ASM_CON1 (0x0108) */
+#define PWR1_ASM_CON1_INIT_VAL		(0x492)
+
+/* AFE_MRGIF_CON (0x003c) */
+#define AFE_MRGIF_CON_MRG_EN		(0x1 << 0)
+#define AFE_MRGIF_CON_MRG_I2S_EN	(0x1 << 16)
+#define AFE_MRGIF_CON_I2S_MODE_MASK	(0xf << 20)
+#define AFE_MRGIF_CON_I2S_MODE_32K	(0x4 << 20)
+
+/* ASYS_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS		(0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x)	((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH		(0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN		(0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN		(0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE	(0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x)	((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL		(0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL		(0xffffffff)
+
+/* PWR2_ASM_CON1 (0x1070) */
+#define PWR2_ASM_CON1_INIT_VAL		(0x492492)
+
+/* AFE_DAC_CON0 (0x1200) */
+#define AFE_DAC_CON0_AFE_ON		(0x1 << 0)
+
+/* AFE_MEMIF_PBUF_SIZE (0x1238) */
+#define AFE_MEMIF_PBUF_SIZE_DLM_MASK		(0x1 << 29)
+#define AFE_MEMIF_PBUF_SIZE_PAIR_INTERLEAVE	(0x0 << 29)
+#define AFE_MEMIF_PBUF_SIZE_FULL_INTERLEAVE	(0x1 << 29)
+#define DLMCH_BIT_WIDTH_MASK			(0x1 << 28)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH_MASK		(0xf << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_CH(x)		((x) << 24)
+#define AFE_MEMIF_PBUF_SIZE_DLM_BYTE_MASK	(0x3 << 12)
+#define AFE_MEMIF_PBUF_SIZE_DLM_32BYTES		(0x1 << 12)
+
+/* I2S in/out register bit control */
+#define ASYS_I2S_CON_FS			(0x1f << 8)
+#define ASYS_I2S_CON_FS_SET(x)		((x) << 8)
+#define ASYS_I2S_CON_RESET		(0x1 << 30)
+#define ASYS_I2S_CON_I2S_EN		(0x1 << 0)
+#define ASYS_I2S_CON_I2S_COUPLE_MODE	(0x1 << 17)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2S_CON_I2S_MODE		(0x1 << 3)
+#define ASYS_I2S_CON_WIDE_MODE		(0x1 << 1)
+#define ASYS_I2S_CON_WIDE_MODE_SET(x)	((x) << 1)
+#define ASYS_I2S_IN_PHASE_FIX		(0x1 << 31)
+
+#define AFE_END_ADDR 0x15e0
+#endif
-- 
2.8.1



From broonie at kernel.org  Wed Jun 29 12:16:30 2016
From: broonie at kernel.org (Mark Brown)
Date: Wed, 29 Jun 2016 20:16:30 +0100
Subject: Applied "ASoC: mediatek: let mt8173 use mediatek common structure" to
 the asoc tree
In-Reply-To: <1466149440-23889-4-git-send-email-garlic.tseng@mediatek.com>
Message-ID: <E1bIKyM-0007D2-MQ@finisterre>

The patch

   ASoC: mediatek: let mt8173 use mediatek common structure

has been applied to the asoc tree at

   git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git 

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.  

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 6b1e19d91d0bf3053716a2636ffd5722c2afb47e Mon Sep 17 00:00:00 2001
From: Garlic Tseng <garlic.tseng at mediatek.com>
Date: Fri, 17 Jun 2016 15:43:54 +0800
Subject: [PATCH] ASoC: mediatek: let mt8173 use mediatek common structure

Modify mt8173 driver implementation to use common structure.

Signed-off-by: Garlic Tseng <garlic.tseng at mediatek.com>
Signed-off-by: Mark Brown <broonie at kernel.org>
---
 sound/soc/mediatek/Kconfig                    |   4 +
 sound/soc/mediatek/Makefile                   |   2 +-
 sound/soc/mediatek/common/Makefile            |  16 +
 sound/soc/mediatek/mt8173/mt8173-afe-common.h |  42 +-
 sound/soc/mediatek/mt8173/mt8173-afe-pcm.c    | 679 +++++++++++---------------
 5 files changed, 318 insertions(+), 425 deletions(-)
 create mode 100644 sound/soc/mediatek/common/Makefile

diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index ae9f664348ff..705904ba10f7 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,6 +1,10 @@
+config SND_SOC_MEDIATEK
+	tristate
+
 config SND_SOC_MT8173
 	tristate "ASoC support for Mediatek MT8173 chip"
 	depends on ARCH_MEDIATEK
+	select SND_SOC_MEDIATEK
 	help
 	  This adds ASoC platform driver support for Mediatek MT8173 chip
 	  that can be used with other codecs.
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index 240dfc70cf05..4fe8068542f1 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,2 +1,2 @@
-# 8173 Machine support
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
 obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 000000000000..a55d33bc7b01
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2015 MediaTek Inc.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.o
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-common.h b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
index 8f2936d62faf..9a4837cc181a 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-common.h
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -46,14 +46,13 @@ enum {
 };
 
 enum {
-	MT8173_AFE_IRQ_1,
-	MT8173_AFE_IRQ_2,
-	MT8173_AFE_IRQ_3,
-	MT8173_AFE_IRQ_4,
-	MT8173_AFE_IRQ_5,
-	MT8173_AFE_IRQ_6,
-	MT8173_AFE_IRQ_7,
-	MT8173_AFE_IRQ_8,
+	MT8173_AFE_IRQ_DL1,
+	MT8173_AFE_IRQ_DL2,
+	MT8173_AFE_IRQ_VUL,
+	MT8173_AFE_IRQ_DAI,
+	MT8173_AFE_IRQ_AWB,
+	MT8173_AFE_IRQ_MOD_DAI,
+	MT8173_AFE_IRQ_HDMI,
 	MT8173_AFE_IRQ_NUM,
 };
 
@@ -71,31 +70,4 @@ enum {
 	MT8173_CLK_NUM
 };
 
-struct mt8173_afe;
-struct snd_pcm_substream;
-
-struct mt8173_afe_memif_data {
-	int id;
-	const char *name;
-	int reg_ofs_base;
-	int reg_ofs_cur;
-	int fs_shift;
-	int mono_shift;
-	int enable_shift;
-	int irq_reg_cnt;
-	int irq_cnt_shift;
-	int irq_en_shift;
-	int irq_fs_shift;
-	int irq_clr_shift;
-	int msb_shift;
-};
-
-struct mt8173_afe_memif {
-	unsigned int phys_buf_addr;
-	int buffer_size;
-	struct snd_pcm_substream *substream;
-	const struct mt8173_afe_memif_data *data;
-	const struct mt8173_afe_irq_data *irqdata;
-};
-
 #endif
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 4fc52bc84547..8a643a35d3d4 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -25,6 +25,9 @@
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mt8173-afe-common.h"
+#include "../common/mtk-base-afe.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
 
 /*****************************************************************************
  *                  R E G I S T E R       D E F I N I T I O N
@@ -81,7 +84,6 @@
 #define AFE_TDM_CON1		0x0548
 #define AFE_TDM_CON2		0x054c
 
-#define AFE_BASE_END_OFFSET	8
 #define AFE_IRQ_STATUS_BITS	0xff
 
 /* AUDIO_TOP_CON0 (0x0000) */
@@ -152,15 +154,8 @@ static const unsigned int mt8173_afe_backup_list[] = {
 	AFE_DAC_CON0,
 };
 
-struct mt8173_afe {
-	/* address for ioremap audio hardware register */
-	void __iomem *base_addr;
-	struct device *dev;
-	struct regmap *regmap;
-	struct mt8173_afe_memif memif[MT8173_AFE_MEMIF_NUM];
+struct mt8173_afe_private {
 	struct clk *clocks[MT8173_CLK_NUM];
-	unsigned int backup_regs[ARRAY_SIZE(mt8173_afe_backup_list)];
-	bool suspended;
 };
 
 static const struct snd_pcm_hardware mt8173_afe_hardware = {
@@ -174,53 +169,6 @@ static const struct snd_pcm_hardware mt8173_afe_hardware = {
 	.fifo_size = 0,
 };
 
-static snd_pcm_uframes_t mt8173_afe_pcm_pointer
-			 (struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int hw_ptr;
-	int ret;
-
-	ret = regmap_read(afe->regmap, memif->data->reg_ofs_cur, &hw_ptr);
-	if (ret || hw_ptr == 0) {
-		dev_err(afe->dev, "%s hw_ptr err\n", __func__);
-		hw_ptr = memif->phys_buf_addr;
-	}
-
-	return bytes_to_frames(substream->runtime,
-			       hw_ptr - memif->phys_buf_addr);
-}
-
-static const struct snd_pcm_ops mt8173_afe_pcm_ops = {
-	.ioctl = snd_pcm_lib_ioctl,
-	.pointer = mt8173_afe_pcm_pointer,
-};
-
-static int mt8173_afe_pcm_new(struct snd_soc_pcm_runtime *rtd)
-{
-	size_t size;
-	struct snd_card *card = rtd->card->snd_card;
-	struct snd_pcm *pcm = rtd->pcm;
-
-	size = mt8173_afe_hardware.buffer_bytes_max;
-
-	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-						     card->dev, size, size);
-}
-
-static void mt8173_afe_pcm_free(struct snd_pcm *pcm)
-{
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static const struct snd_soc_platform_driver mt8173_afe_pcm_platform = {
-	.ops = &mt8173_afe_pcm_ops,
-	.pcm_new = mt8173_afe_pcm_new,
-	.pcm_free = mt8173_afe_pcm_free,
-};
-
 struct mt8173_afe_rate {
 	unsigned int rate;
 	unsigned int regvalue;
@@ -253,7 +201,7 @@ static int mt8173_afe_i2s_fs(unsigned int sample_rate)
 	return -EINVAL;
 }
 
-static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
 {
 	unsigned int val;
 	int fs = mt8173_afe_i2s_fs(rate);
@@ -281,7 +229,7 @@ static int mt8173_afe_set_i2s(struct mt8173_afe *afe, unsigned int rate)
 	return 0;
 }
 
-static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
 {
 	unsigned int val;
 
@@ -296,7 +244,7 @@ static void mt8173_afe_set_i2s_enable(struct mt8173_afe *afe, bool enable)
 	regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
 }
 
-static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
 				       struct clk *m_ck, struct clk *b_ck)
 {
 	int ret;
@@ -319,7 +267,7 @@ static int mt8173_afe_dais_enable_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
+static int mt8173_afe_dais_set_clks(struct mtk_base_afe *afe,
 				    struct clk *m_ck, unsigned int mck_rate,
 				    struct clk *b_ck, unsigned int bck_rate)
 {
@@ -343,7 +291,7 @@ static int mt8173_afe_dais_set_clks(struct mt8173_afe *afe,
 	return 0;
 }
 
-static void mt8173_afe_dais_disable_clks(struct mt8173_afe *afe,
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
 					 struct clk *m_ck, struct clk *b_ck)
 {
 	if (m_ck)
@@ -356,7 +304,7 @@ static int mt8173_afe_i2s_startup(struct snd_pcm_substream *substream,
 				  struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return 0;
@@ -370,7 +318,7 @@ static void mt8173_afe_i2s_shutdown(struct snd_pcm_substream *substream,
 				    struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	if (dai->active)
 		return;
@@ -386,12 +334,13 @@ static int mt8173_afe_i2s_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S1_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S1_M],
 				 runtime->rate * 256, NULL, 0);
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S2_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S2_M],
 				 runtime->rate * 256, NULL, 0);
 	/* config I2S */
 	ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
@@ -407,13 +356,14 @@ static int mt8173_afe_hdmi_startup(struct snd_pcm_substream *substream,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return 0;
 
-	mt8173_afe_dais_enable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				    afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				    afe_priv->clocks[MT8173_CLK_I2S3_B]);
 	return 0;
 }
 
@@ -421,13 +371,14 @@ static void mt8173_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
 				     struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	if (dai->active)
 		return;
 
-	mt8173_afe_dais_disable_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
-				     afe->clocks[MT8173_CLK_I2S3_B]);
+	mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+				     afe_priv->clocks[MT8173_CLK_I2S3_B]);
 }
 
 static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
@@ -435,12 +386,14 @@ static int mt8173_afe_hdmi_prepare(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
 	unsigned int val;
 
-	mt8173_afe_dais_set_clks(afe, afe->clocks[MT8173_CLK_I2S3_M],
+	mt8173_afe_dais_set_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
 				 runtime->rate * 128,
-				 afe->clocks[MT8173_CLK_I2S3_B],
+				 afe_priv->clocks[MT8173_CLK_I2S3_B],
 				 runtime->rate * runtime->channels * 32);
 
 	val = AFE_TDM_CON1_BCK_INV |
@@ -496,7 +449,7 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 				   struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
 
 	dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
 
@@ -508,10 +461,14 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 
 		/* set connections:  O30~O37: L/R/LS/RS/C/LFE/CH7/CH8 */
 		regmap_write(afe->regmap, AFE_HDMI_CONN0,
-			     AFE_HDMI_CONN0_O30_I30 | AFE_HDMI_CONN0_O31_I31 |
-			     AFE_HDMI_CONN0_O32_I34 | AFE_HDMI_CONN0_O33_I35 |
-			     AFE_HDMI_CONN0_O34_I32 | AFE_HDMI_CONN0_O35_I33 |
-			     AFE_HDMI_CONN0_O36_I36 | AFE_HDMI_CONN0_O37_I37);
+				 AFE_HDMI_CONN0_O30_I30 |
+				 AFE_HDMI_CONN0_O31_I31 |
+				 AFE_HDMI_CONN0_O32_I34 |
+				 AFE_HDMI_CONN0_O33_I35 |
+				 AFE_HDMI_CONN0_O34_I32 |
+				 AFE_HDMI_CONN0_O35_I33 |
+				 AFE_HDMI_CONN0_O36_I36 |
+				 AFE_HDMI_CONN0_O37_I37);
 
 		/* enable Out control */
 		regmap_update_bits(afe->regmap, AFE_HDMI_OUT_CON0, 0x1, 0x1);
@@ -531,224 +488,46 @@ static int mt8173_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
 		regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF,
 				   AUD_TCON0_PDN_HDMI | AUD_TCON0_PDN_SPDF);
-
 		return 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int mt8173_afe_dais_startup(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int ret;
-
-	memif->substream = substream;
-
-	snd_soc_set_runtime_hwparams(substream, &mt8173_afe_hardware);
-
-	/*
-	 * Capture cannot use ping-pong buffer since hw_ptr at IRQ may be
-	 * smaller than period_size due to AFE's internal buffer.
-	 * This easily leads to overrun when avail_min is period_size.
-	 * One more period can hold the possible unread buffer.
-	 */
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		ret = snd_pcm_hw_constraint_minmax(runtime,
-						   SNDRV_PCM_HW_PARAM_PERIODS,
-						   3,
-						   mt8173_afe_hardware.periods_max);
-		if (ret < 0) {
-			dev_err(afe->dev, "hw_constraint_minmax failed\n");
-			return ret;
-		}
-	}
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					    SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		dev_err(afe->dev, "snd_pcm_hw_constraint_integer failed\n");
-	return ret;
-}
-
-static void mt8173_afe_dais_shutdown(struct snd_pcm_substream *substream,
-				     struct snd_soc_dai *dai)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+			   unsigned int rate)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-
-	memif->substream = NULL;
-}
-
-static int mt8173_afe_dais_hw_params(struct snd_pcm_substream *substream,
-				     struct snd_pcm_hw_params *params,
-				     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	int msb_at_bit33 = 0;
-	int ret;
-
-	dev_dbg(afe->dev,
-		"%s period = %u, rate= %u, channels=%u\n",
-		__func__, params_period_size(params), params_rate(params),
-		params_channels(params));
-
-	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-	if (ret < 0)
-		return ret;
+	struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+	struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+	int fs;
 
-	msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
-	memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
-	memif->buffer_size = substream->runtime->dma_bytes;
-
-	/* start */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base, memif->phys_buf_addr);
-	/* end */
-	regmap_write(afe->regmap,
-		     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
-		     memif->phys_buf_addr + memif->buffer_size - 1);
-
-	/* set MSB to 33-bit */
-	regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
-			   1 << memif->data->msb_shift,
-			   msb_at_bit33 << memif->data->msb_shift);
-
-	/* set channel */
-	if (memif->data->mono_shift >= 0) {
-		unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   1 << memif->data->mono_shift,
-				   mono << memif->data->mono_shift);
-	}
-
-	/* set rate */
-	if (memif->data->fs_shift < 0)
-		return 0;
 	if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
 	    memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
-		unsigned int val;
-
-		switch (params_rate(params)) {
+		switch (rate) {
 		case 8000:
-			val = 0;
+			fs = 0;
 			break;
 		case 16000:
-			val = 1;
+			fs = 1;
 			break;
 		case 32000:
-			val = 2;
+			fs = 2;
 			break;
 		default:
 			return -EINVAL;
 		}
-
-		if (memif->data->id == MT8173_AFE_MEMIF_DAI)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-		else
-			regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-					   0x3 << memif->data->fs_shift,
-					   val << memif->data->fs_shift);
-
 	} else {
-		int fs = mt8173_afe_i2s_fs(params_rate(params));
-
-		if (fs < 0)
-			return -EINVAL;
-
-		regmap_update_bits(afe->regmap, AFE_DAC_CON1,
-				   0xf << memif->data->fs_shift,
-				   fs << memif->data->fs_shift);
+		fs = mt8173_afe_i2s_fs(rate);
 	}
-
-	return 0;
+	return fs;
 }
 
-static int mt8173_afe_dais_hw_free(struct snd_pcm_substream *substream,
-				   struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
 {
-	return snd_pcm_lib_free_pages(substream);
+	return mt8173_afe_i2s_fs(rate);
 }
 
-static int mt8173_afe_dais_trigger(struct snd_pcm_substream *substream, int cmd,
-				   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_pcm_runtime * const runtime = substream->runtime;
-	struct mt8173_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
-	struct mt8173_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
-	unsigned int counter = runtime->period_size;
-
-	dev_info(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift,
-					   1 << memif->data->enable_shift);
-
-		/* set irq counter */
-		regmap_update_bits(afe->regmap,
-				   memif->data->irq_reg_cnt,
-				   0x3ffff << memif->data->irq_cnt_shift,
-				   counter << memif->data->irq_cnt_shift);
-
-		/* set irq fs */
-		if (memif->data->irq_fs_shift >= 0) {
-			int fs = mt8173_afe_i2s_fs(runtime->rate);
-
-			if (fs < 0)
-				return -EINVAL;
-
-			regmap_update_bits(afe->regmap,
-					   AFE_IRQ_MCU_CON,
-					   0xf << memif->data->irq_fs_shift,
-					   fs << memif->data->irq_fs_shift);
-		}
-		/* enable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   1 << memif->data->irq_en_shift);
-
-		return 0;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		if (memif->data->enable_shift >= 0)
-			regmap_update_bits(afe->regmap, AFE_DAC_CON0,
-					   1 << memif->data->enable_shift, 0);
-		/* disable interrupt */
-		regmap_update_bits(afe->regmap, AFE_IRQ_MCU_CON,
-				   1 << memif->data->irq_en_shift,
-				   0 << memif->data->irq_en_shift);
-		/* and clear pending IRQ */
-		regmap_write(afe->regmap, AFE_IRQ_CLR,
-			     1 << memif->data->irq_clr_shift);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-
-/* FE DAIs */
-static const struct snd_soc_dai_ops mt8173_afe_dai_ops = {
-	.startup	= mt8173_afe_dais_startup,
-	.shutdown	= mt8173_afe_dais_shutdown,
-	.hw_params	= mt8173_afe_dais_hw_params,
-	.hw_free	= mt8173_afe_dais_hw_free,
-	.trigger	= mt8173_afe_dais_trigger,
-};
-
 /* BE DAIs */
 static const struct snd_soc_dai_ops mt8173_afe_i2s_ops = {
 	.startup	= mt8173_afe_i2s_startup,
@@ -761,56 +540,15 @@ static const struct snd_soc_dai_ops mt8173_afe_hdmi_ops = {
 	.shutdown	= mt8173_afe_hdmi_shutdown,
 	.prepare	= mt8173_afe_hdmi_prepare,
 	.trigger	= mt8173_afe_hdmi_trigger,
-
 };
 
-static int mt8173_afe_runtime_suspend(struct device *dev);
-static int mt8173_afe_runtime_resume(struct device *dev);
-
-static int mt8173_afe_dai_suspend(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || afe->suspended)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_read(afe->regmap, mt8173_afe_backup_list[i],
-			    &afe->backup_regs[i]);
-
-	afe->suspended = true;
-	mt8173_afe_runtime_suspend(afe->dev);
-	return 0;
-}
-
-static int mt8173_afe_dai_resume(struct snd_soc_dai *dai)
-{
-	struct mt8173_afe *afe = snd_soc_dai_get_drvdata(dai);
-	int i = 0;
-
-	dev_dbg(afe->dev, "%s\n", __func__);
-	if (pm_runtime_status_suspended(afe->dev) || !afe->suspended)
-		return 0;
-
-	mt8173_afe_runtime_resume(afe->dev);
-
-	for (i = 0; i < ARRAY_SIZE(mt8173_afe_backup_list); i++)
-		regmap_write(afe->regmap, mt8173_afe_backup_list[i],
-			     afe->backup_regs[i]);
-
-	afe->suspended = false;
-	return 0;
-}
-
 static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 	/* FE DAIs: memory intefaces to CPU */
 	{
 		.name = "DL1", /* downlink 1 */
 		.id = MT8173_AFE_MEMIF_DL1,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "DL1",
 			.channels_min = 1,
@@ -818,12 +556,12 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 		.name = "VUL", /* voice uplink */
 		.id = MT8173_AFE_MEMIF_VUL,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.capture = {
 			.stream_name = "VUL",
 			.channels_min = 1,
@@ -831,7 +569,7 @@ static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
 			.rates = SNDRV_PCM_RATE_8000_48000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "I2S",
@@ -860,8 +598,8 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 	{
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
-		.suspend = mt8173_afe_dai_suspend,
-		.resume = mt8173_afe_dai_resume,
+		.suspend = mtk_afe_dai_suspend,
+		.resume = mtk_afe_dai_resume,
 		.playback = {
 			.stream_name = "HDMI",
 			.channels_min = 2,
@@ -872,7 +610,7 @@ static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
 				SNDRV_PCM_RATE_192000,
 			.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		},
-		.ops = &mt8173_afe_dai_ops,
+		.ops = &mtk_afe_fe_ops,
 	}, {
 	/* BE DAIs */
 		.name = "HDMIO",
@@ -978,105 +716,222 @@ static const char *aud_clks[MT8173_CLK_NUM] = {
 	[MT8173_CLK_BCK1] =  "bck1",
 };
 
-static const struct mt8173_afe_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
 	{
 		.name = "DL1",
 		.id = MT8173_AFE_MEMIF_DL1,
 		.reg_ofs_base = AFE_DL1_BASE,
 		.reg_ofs_cur = AFE_DL1_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 0,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 21,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 1,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 0,
-		.irq_fs_shift = 4,
-		.irq_clr_shift = 0,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 0,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DL2",
 		.id = MT8173_AFE_MEMIF_DL2,
 		.reg_ofs_base = AFE_DL2_BASE,
 		.reg_ofs_cur = AFE_DL2_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 4,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 22,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 2,
-		.irq_reg_cnt = AFE_IRQ_CNT1,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 2,
-		.irq_fs_shift = 16,
-		.irq_clr_shift = 2,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 1,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "VUL",
 		.id = MT8173_AFE_MEMIF_VUL,
 		.reg_ofs_base = AFE_VUL_BASE,
 		.reg_ofs_cur = AFE_VUL_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 16,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 27,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 3,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 1,
-		.irq_fs_shift = 8,
-		.irq_clr_shift = 1,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 6,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "DAI",
 		.id = MT8173_AFE_MEMIF_DAI,
 		.reg_ofs_base = AFE_DAI_BASE,
 		.reg_ofs_cur = AFE_DAI_CUR,
+		.fs_reg = AFE_DAC_CON0,
 		.fs_shift = 24,
+		.fs_maskbit = 0x3,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 4,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 5,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "AWB",
 		.id = MT8173_AFE_MEMIF_AWB,
 		.reg_ofs_base = AFE_AWB_BASE,
 		.reg_ofs_cur = AFE_AWB_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 12,
+		.fs_maskbit = 0xf,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 24,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 6,
-		.irq_reg_cnt = AFE_IRQ_CNT7,
-		.irq_cnt_shift = 0,
-		.irq_en_shift = 14,
-		.irq_fs_shift = 24,
-		.irq_clr_shift = 6,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 3,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "MOD_DAI",
 		.id = MT8173_AFE_MEMIF_MOD_DAI,
 		.reg_ofs_base = AFE_MOD_PCM_BASE,
 		.reg_ofs_cur = AFE_MOD_PCM_CUR,
+		.fs_reg = AFE_DAC_CON1,
 		.fs_shift = 30,
+		.fs_maskbit = 0x3,
+		.mono_reg = AFE_DAC_CON1,
 		.mono_shift = 30,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = AFE_DAC_CON0,
 		.enable_shift = 7,
-		.irq_reg_cnt = AFE_IRQ_CNT2,
-		.irq_cnt_shift = 20,
-		.irq_en_shift = 3,
-		.irq_fs_shift = 20,
-		.irq_clr_shift = 3,
+		.msb_reg = AFE_MEMIF_MSB,
 		.msb_shift = 4,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
 	}, {
 		.name = "HDMI",
 		.id = MT8173_AFE_MEMIF_HDMI,
 		.reg_ofs_base = AFE_HDMI_OUT_BASE,
 		.reg_ofs_cur = AFE_HDMI_OUT_CUR,
+		.fs_reg = -1,
 		.fs_shift = -1,
+		.fs_maskbit = -1,
+		.mono_reg = -1,
 		.mono_shift = -1,
+		.hd_reg = -1,
+		.hd_shift = -1,
+		.enable_reg = -1,
 		.enable_shift = -1,
-		.irq_reg_cnt = AFE_IRQ_CNT5,
+		.msb_reg = AFE_MEMIF_MSB,
+		.msb_shift = 8,
+		.agent_disable_reg = -1,
+		.agent_disable_shift = -1,
+	},
+};
+
+static const struct mtk_base_irq_data irq_data[MT8173_AFE_IRQ_NUM] = {
+	{
+		.id = MT8173_AFE_IRQ_DL1,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 0,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 4,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 0,
+	}, {
+		.id = MT8173_AFE_IRQ_DL2,
+		.irq_cnt_reg = AFE_IRQ_CNT1,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 2,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 16,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 2,
+
+	}, {
+		.id = MT8173_AFE_IRQ_VUL,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 1,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 8,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 1,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_AWB,
+		.irq_cnt_reg = AFE_IRQ_CNT7,
 		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 14,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 24,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 6,
+	}, {
+		.id = MT8173_AFE_IRQ_DAI,
+		.irq_cnt_reg = AFE_IRQ_CNT2,
+		.irq_cnt_shift = 20,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
+		.irq_en_shift = 3,
+		.irq_fs_reg = AFE_IRQ_MCU_CON,
+		.irq_fs_shift = 20,
+		.irq_fs_maskbit = 0xf,
+		.irq_clr_reg = AFE_IRQ_CLR,
+		.irq_clr_shift = 3,
+	}, {
+		.id = MT8173_AFE_IRQ_HDMI,
+		.irq_cnt_reg = AFE_IRQ_CNT5,
+		.irq_cnt_shift = 0,
+		.irq_cnt_maskbit = 0x3ffff,
+		.irq_en_reg = AFE_IRQ_MCU_CON,
 		.irq_en_shift = 12,
+		.irq_fs_reg = -1,
 		.irq_fs_shift = -1,
+		.irq_fs_maskbit = -1,
+		.irq_clr_reg = AFE_IRQ_CLR,
 		.irq_clr_shift = 4,
-		.msb_shift = 8,
 	},
 };
 
@@ -1090,7 +945,7 @@ static const struct regmap_config mt8173_afe_regmap_config = {
 
 static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 {
-	struct mt8173_afe *afe = dev_id;
+	struct mtk_base_afe *afe = dev_id;
 	unsigned int reg_value;
 	int i, ret;
 
@@ -1102,9 +957,15 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 	}
 
 	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
-		struct mt8173_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_memif *memif = &afe->memif[i];
+		struct mtk_base_afe_irq *irq;
 
-		if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+		if (memif->irq_usage < 0)
+			continue;
+
+		irq = &afe->irqs[memif->irq_usage];
+
+		if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
 			continue;
 
 		snd_pcm_period_elapsed(memif->substream);
@@ -1112,14 +973,16 @@ static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
 
 err_irq:
 	/* clear irq */
-	regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+	regmap_write(afe->regmap, AFE_IRQ_CLR,
+		     reg_value & AFE_IRQ_STATUS_BITS);
 
 	return IRQ_HANDLED;
 }
 
 static int mt8173_afe_runtime_suspend(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	/* disable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0);
@@ -1127,44 +990,46 @@ static int mt8173_afe_runtime_suspend(struct device *dev)
 	/* disable AFE clk */
 	regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
 			   AUD_TCON0_PDN_AFE, AUD_TCON0_PDN_AFE);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK1]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return 0;
 }
 
 static int mt8173_afe_runtime_resume(struct device *dev)
 {
-	struct mt8173_afe *afe = dev_get_drvdata(dev);
+	struct mtk_base_afe *afe = dev_get_drvdata(dev);
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 	int ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 	if (ret)
 		goto err_infra;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 	if (ret)
 		goto err_top_aud_bus;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK0]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
 	if (ret)
 		goto err_top_aud;
 
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_BCK1]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK1]);
 	if (ret)
 		goto err_bck0;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S1_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 	if (ret)
 		goto err_i2s1_m;
-	ret = clk_prepare_enable(afe->clocks[MT8173_CLK_I2S2_M]);
+	ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 	if (ret)
 		goto err_i2s2_m;
 
@@ -1181,35 +1046,37 @@ static int mt8173_afe_runtime_resume(struct device *dev)
 	/* enable AFE */
 	regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
 	return 0;
+
 err_i2s1_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S1_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
 err_i2s2_m:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_I2S2_M]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
 err_bck0:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_BCK0]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
 err_top_aud:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
 err_top_aud_bus:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
 err_infra:
-	clk_disable_unprepare(afe->clocks[MT8173_CLK_INFRASYS_AUD]);
+	clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
 	return ret;
 }
 
-static int mt8173_afe_init_audio_clk(struct mt8173_afe *afe)
+static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
 {
 	size_t i;
+	struct mt8173_afe_private *afe_priv = afe->platform_priv;
 
 	for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
-		afe->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
-		if (IS_ERR(afe->clocks[i])) {
+		afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+		if (IS_ERR(afe_priv->clocks[i])) {
 			dev_err(afe->dev, "%s devm_clk_get %s fail\n",
 				__func__, aud_clks[i]);
-			return PTR_ERR(afe->clocks[i]);
+			return PTR_ERR(afe_priv->clocks[i]);
 		}
 	}
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
-	clk_set_rate(afe->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
+	clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
 	return 0;
 }
 
@@ -1217,7 +1084,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 {
 	int ret, i;
 	unsigned int irq_id;
-	struct mt8173_afe *afe;
+	struct mtk_base_afe *afe;
+	struct mt8173_afe_private *afe_priv;
 	struct resource *res;
 
 	ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1228,6 +1096,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 	if (!afe)
 		return -ENOMEM;
 
+	afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+					  GFP_KERNEL);
+	afe_priv = afe->platform_priv;
+	if (!afe_priv)
+		return -ENOMEM;
+
 	afe->dev = &pdev->dev;
 
 	irq_id = platform_get_irq(pdev, 0);
@@ -1259,8 +1133,30 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++)
+	/* memif % irq initialize*/
+	afe->memif_size = MT8173_AFE_MEMIF_NUM;
+	afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+				  sizeof(*afe->memif), GFP_KERNEL);
+	if (!afe->memif)
+		return -ENOMEM;
+
+	afe->irqs_size = MT8173_AFE_IRQ_NUM;
+	afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+				 sizeof(*afe->irqs), GFP_KERNEL);
+	if (!afe->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < afe->irqs_size; i++) {
 		afe->memif[i].data = &memif_data[i];
+		afe->irqs[i].irq_data = &irq_data[i];
+		afe->irqs[i].irq_occupyed = true;
+		afe->memif[i].irq_usage = i;
+		afe->memif[i].const_irq = 1;
+	}
+
+	afe->mtk_afe_hardware = &mt8173_afe_hardware;
+	afe->memif_fs = mt8173_memif_fs;
+	afe->irq_fs = mt8173_irq_fs;
 
 	platform_set_drvdata(pdev, afe);
 
@@ -1271,7 +1167,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
 			goto err_pm_disable;
 	}
 
-	ret = snd_soc_register_platform(&pdev->dev, &mt8173_afe_pcm_platform);
+	afe->reg_back_up_list = mt8173_afe_backup_list;
+	afe->reg_back_up_list_num = ARRAY_SIZE(mt8173_afe_backup_list);
+	afe->runtime_resume = mt8173_afe_runtime_resume;
+	afe->runtime_suspend = mt8173_afe_runtime_suspend;
+
+	ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
 	if (ret)
 		goto err_pm_disable;
 
-- 
2.8.1



From davem at davemloft.net  Thu Jun 30 05:52:50 2016
From: davem at davemloft.net (David Miller)
Date: Thu, 30 Jun 2016 08:52:50 -0400 (EDT)
Subject: [PATCH V2 0/4] net-next: mediatek: IRQ cleanups, fixes and
 grouping
In-Reply-To: <1467200291-60106-1-git-send-email-john@phrozen.org>
References: <1467200291-60106-1-git-send-email-john@phrozen.org>
Message-ID: <20160630.085250.1236377588787063326.davem@davemloft.net>

From: John Crispin <john at phrozen.org>
Date: Wed, 29 Jun 2016 13:38:07 +0200

> This series contains 2 small code cleanups that are leftovers from the
> MIPS support. There is also a small fix that adds proper locking to the
> code accessing the IRQ registers. Without this fix we saw deadlocks caused
> by the last patch of the series, which adds IRQ grouping. The grouping
> feature allows us to use different IRQs for TX and RX. By doing so we can
> use affinity to let the SoC handle the IRQs on different cores.
> 
> This series depends on a previous series currently sitting in net.git
> starting with
> 	commit 562c5a70400c ("net: mediatek: only wake the queue if it is stopped")
> up to
> 	commit 82c6544dddc6 ("net: mediatek: remove superfluous queue wake up call")

Series applied, thanks.


From garlic.tseng at mediatek.com  Thu Jun 30 05:55:45 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Thu, 30 Jun 2016 20:55:45 +0800
Subject: [alsa-devel] [PATCH v5 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <20160629191500.GU6247@sirena.org.uk>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
 <1466149440-23889-8-git-send-email-garlic.tseng@mediatek.com>
 <20160629191500.GU6247@sirena.org.uk>
Message-ID: <1467291345.11091.16.camel@mtksdaap41>

On Wed, 2016-06-29 at 20:15 +0100, Mark Brown wrote:
> On Fri, Jun 17, 2016 at 03:43:58PM +0800, Garlic Tseng wrote:
> > Add supports for 16k (wideband BT) and add a general compatible
> > string "linux,bt-sco"
> 
> This will claim that we support 16k on existing systems which we clearly
> don't.  It also seems unwise to advertise multiple rates when we've no
> way to configure the rates...  how does the BT controller figure out
> what the sample rate is?

The codec driver is a dummy driver for bt device and actually do
nothing. The user-space will control both bt part and alsa part (at
least in mt2701 platform). Yes the sound/soc/codecs/bt-sco.c was only
support 8k and was already there before the patch, but I think it might
be ok to extend it to 16k without any side effect.

If you worry about some potential risk (I don't see any) maybe we have
to develop another dummy bt-sco codec driver which support both 8k and
16k?



From garlic.tseng at mediatek.com  Thu Jun 30 06:39:41 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Thu, 30 Jun 2016 21:39:41 +0800
Subject: [alsa-devel] [PATCH v5 6/9] ASoC: mediatek: add mt2701 platform
 driver implementation.
In-Reply-To: <20160629191322.GT6247@sirena.org.uk>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
 <1466149440-23889-7-git-send-email-garlic.tseng@mediatek.com>
 <20160629191322.GT6247@sirena.org.uk>
Message-ID: <1467293981.17087.18.camel@mtksdaap41>

On Wed, 2016-06-29 at 20:13 +0100, Mark Brown wrote:
> On Fri, Jun 17, 2016 at 03:43:57PM +0800, Garlic Tseng wrote:
> 
> > +static int mt2701_afe_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id,
> > +				     int div)
> > +{
> 
> Why are we adding a set_clkdiv() operation?  I would expect the driver
> to be able to figure things out automatically.
> 
> > +	case DIV_ID_MCLK_TO_BCK:
> > +		afe_priv->i2s_path[i2s_num].div_mclk_to_bck = div;
> > +		break;
> > +	case DIV_ID_BCK_TO_LRCK:
> > +		afe_priv->i2s_path[i2s_num].div_bck_to_lrck = div;
> > +		break;
> 
> Especially in the case where we're configuring LRCLK, that's trivial
> when we know the sample rate which we have to know anyway.

Oh... actually I want to say 'div_mclk_over_bck' and 'div_bck_over_lrck'
I'll fix the naming if we decide to reserve the set_clkdiv() operation.

For div_bck_over_lrck, yes we only support bck = 64fs now so I'll remove
it. Maybe in the future we'll allow bck = 32fs for some board and I'll
send patch on that time.

For div_mclk_over_bck, mt2635 can offer 128fs and 256fs mclk to codec,
and it depends on the board so I tend to let machine driver configure
it. However, maybe changing it to div_mclk_over_lrck will be better.
I'll do the change.

BTW, some patch is applied for the patchset so I need not to send the
new version but send another patchset, right?


> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel at alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel




From garlic.tseng at mediatek.com  Thu Jun 30 19:49:46 2016
From: garlic.tseng at mediatek.com (Garlic Tseng)
Date: Fri, 1 Jul 2016 10:49:46 +0800
Subject: [alsa-devel] [PATCH v5 7/9] ASoC: bt-sco: extend rate and add a
 general compatible string
In-Reply-To: <1467291345.11091.16.camel@mtksdaap41>
References: <1466149440-23889-1-git-send-email-garlic.tseng@mediatek.com>
 <1466149440-23889-8-git-send-email-garlic.tseng@mediatek.com>
 <20160629191500.GU6247@sirena.org.uk>
 <1467291345.11091.16.camel@mtksdaap41>
Message-ID: <1467341386.21689.6.camel@mtksdaap41>

On Thu, 2016-06-30 at 20:55 +0800, Garlic Tseng wrote:
> On Wed, 2016-06-29 at 20:15 +0100, Mark Brown wrote:
> > On Fri, Jun 17, 2016 at 03:43:58PM +0800, Garlic Tseng wrote:
> > > Add supports for 16k (wideband BT) and add a general compatible
> > > string "linux,bt-sco"
> > 
> > This will claim that we support 16k on existing systems which we clearly
> > don't.  It also seems unwise to advertise multiple rates when we've no
> > way to configure the rates...  how does the BT controller figure out
> > what the sample rate is?
> 
> The codec driver is a dummy driver for bt device and actually do
> nothing. The user-space will control both bt part and alsa part (at
> least in mt2701 platform). Yes the sound/soc/codecs/bt-sco.c was only
> support 8k and was already there before the patch, but I think it might
> be ok to extend it to 16k without any side effect.
> 
> If you worry about some potential risk (I don't see any) maybe we have
> to develop another dummy bt-sco codec driver which support both 8k and
> 16k?

Ah! If someone whose bluetooth modules only support 8k use the driver,
they might be broken, right? Maybe we can add another snd_soc_dai_driver
which can support both 8k and 16k.
(Actually I found the issue is discussed before
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-November/084687.html )



From roger.lu at mediatek.com  Thu Jun 30 20:05:02 2016
From: roger.lu at mediatek.com (Roger Lu)
Date: Fri, 1 Jul 2016 11:05:02 +0800
Subject: [PATCH v2] PM / suspend: show workqueue state in suspend flow
Message-ID: <1467342302-16129-1-git-send-email-roger.lu@mediatek.com>

If freezable workqueue aborts suspend flow, show
workqueue state for debug purpose.

Signed-off-by: Roger Lu <roger.lu at mediatek.com>
---
Changes in v2:
- remove prints in show_workqueue_state().
---
 kernel/power/process.c | 3 +++
 kernel/workqueue.c     | 4 ++--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/kernel/power/process.c b/kernel/power/process.c
index df058be..6eef250 100644
--- a/kernel/power/process.c
+++ b/kernel/power/process.c
@@ -89,6 +89,9 @@ static int try_to_freeze_tasks(bool user_only)
 		       elapsed_msecs / 1000, elapsed_msecs % 1000,
 		       todo - wq_busy, wq_busy);
 
+		if (wq_busy)
+			show_workqueue_state();
+
 		if (!wakeup) {
 			read_lock(&tasklist_lock);
 			for_each_process_thread(g, p) {
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index e1c0e99..619e80c 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -4369,8 +4369,8 @@ static void show_pwq(struct pool_workqueue *pwq)
 /**
  * show_workqueue_state - dump workqueue state
  *
- * Called from a sysrq handler and prints out all busy workqueues and
- * pools.
+ * Called from a sysrq handler or try_to_freeze_tasks() and prints out
+ * all busy workqueues and pools.
  */
 void show_workqueue_state(void)
 {
-- 
2.6.4



