Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH V2] arm: xen: mm: use __GPF_DMA32 for arm64
From: Juergen Gross @ 2019-09-03 11:48 UTC (permalink / raw)
  To: Christoph Hellwig, Stefano Stabellini
  Cc: Peng Fan, will@kernel.org, Catalin Marinas, linux@armlinux.org.uk,
	Julien Grall, dl-linux-imx, xen-devel@lists.xenproject.org,
	boris.ostrovsky, nd, Robin Murphy,
	linux-arm-kernel@lists.infradead.org
In-Reply-To: <20190902155732.GA8311@infradead.org>

On 02.09.19 17:57, Christoph Hellwig wrote:
> On Fri, Aug 30, 2019 at 07:40:42PM -0700, Stefano Stabellini wrote:
>> + Juergen, Boris
>>
>> On Fri, 30 Aug 2019, Christoph Hellwig wrote:
>>> Can we take a step back and figure out what we want to do here?
>>>
>>> AFAICS this function allocates memory for the swiotlb-xen buffer,
>>> and that means it must be <= 32-bit addressable to satisfy the DMA API
>>> guarantees.  That means we generally want to use GFP_DMA32 everywhere
>>> that exists, but on systems with odd zones we might want to dip into
>>> GFP_DMA.  This also means swiotlb-xen doesn't actually do the right
>>> thing on x86 at the moment.  So shouldn't we just have one common
>>> routine in swiotlb-xen.c that checks if we have CONFIG_ZONE_DMA32
>>> set, then try GFP_DMA32, and if not check if CONFIG_ZONE_DMA is set
>>> and then try that, else default to GFP_KERNEL?
>>
>> Yes, for ARM/ARM64 it makes a lot of sense given that dom0 is 1:1 mapped
>> (pseudo-physical == physical).  I'll let Juergen and Boris comment on
>> the x86 side of things, but on x86 PV Dom0 is not 1:1 mapped so
>> GFP_DMA32 is probably not meaningful.
> 
> But is it actually harmful?  If the GFP_DMA32 doesn't hurt we could
> just use it there.  Or if that seems to ugly we can make the dma
> flags dependents on a XEN_1TO1_MAPPED config option set by arm/arm64.
> 

I'd rather have it only if needed. Especially on X86 PV dom0 I'd like to
avoid GFP_DMA32 as memory below 4GB (guest physical) might be rather
scarce in some configurations.

I think X86 PVH dom0 should need GFP_DMA32, too, as the limit is related
to the address as communicated to the device (before being translated by
the IOMMU), right? This would mean on a X86 kernel configured to support
PV and PVH the test for setting GFP_DMA32 can't depend on a config
option alone, it needs to be dynamic.

BTW, for PV guests the DMA address width is handled via
xen_create_contiguous_region().


Juergen

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [RFC PATCH V2 4/4] platform: mtk-isp: Add Mediatek FD driver
From: Jerry-ch Chen @ 2019-09-03 11:46 UTC (permalink / raw)
  To: Tomasz Figa
  Cc: devicetree@vger.kernel.org, Sean Cheng (鄭昇弘),
	laurent.pinchart+renesas@ideasonboard.com,
	Rynn Wu (吳育恩), srv_heupstream,
	Po-Yang Huang (黃柏陽), mchehab@kernel.org,
	suleiman@chromium.org, shik@chromium.org,
	Jungo Lin (林明俊),
	Sj Huang (黃信璋), yuzhao@chromium.org,
	linux-mediatek@lists.infradead.org, zwisler@chromium.org,
	matthias.bgg@gmail.com, Christie Yu (游雅惠),
	Frederic Chen (陳俊元), hans.verkuil@cisco.com,
	linux-arm-kernel@lists.infradead.org, linux-media@vger.kernel.org
In-Reply-To: <CAAFQd5DWM=R7sFHYGhhR_rXrzgRnc4xtH_t8Pig-4tcP9KTSYg@mail.gmail.com>

Hi Tomasz,

On Tue, 2019-09-03 at 15:04 +0800, Tomasz Figa wrote:
> On Tue, Sep 3, 2019 at 3:44 PM Jerry-ch Chen <Jerry-ch.Chen@mediatek.com> wrote:
> >
> > On Tue, 2019-09-03 at 13:19 +0800, Tomasz Figa wrote:
> > > On Mon, Sep 2, 2019 at 8:47 PM Jerry-ch Chen <Jerry-ch.Chen@mediatek.com> wrote:
> > > >
> > > > Hi Tomasz,
> > > >
> > > > On Fri, 2019-08-30 at 16:33 +0800, Tomasz Figa wrote:
> > > > > On Wed, Aug 28, 2019 at 11:00 AM Jerry-ch Chen
> > > > > <Jerry-ch.Chen@mediatek.com> wrote:
> > > > > >
> > > > > > Hi Tomasz,
> > > > > >
> > > > > > On Mon, 2019-08-26 at 14:36 +0800, Tomasz Figa wrote:
> > > > > > > Hi Jerry,
> > > > > > >
> > > > > > > On Sun, Aug 25, 2019 at 6:18 PM Jerry-ch Chen
> > > > > > > <Jerry-ch.Chen@mediatek.com> wrote:
> > > > > > > >
> > > > > > > > Hi Tomasz,
> > > > > > > >
> > > > > > > > On Fri, 2019-08-02 at 16:28 +0800, Tomasz Figa wrote:
> > > > > > > > > Hi Jerry,
> > > > > > > > >
> > > > > > > > > On Tue, Jul 09, 2019 at 04:41:12PM +0800, Jerry-ch Chen wrote:
> > > [snip]
> > > > > > static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> > > > > >                                   unsigned int *num_buffers,
> > > > > >                                   unsigned int *num_planes,
> > > > > >                                   unsigned int sizes[],
> > > > > >                                   struct device *alloc_devs[])
> > > > > > {
> > > > > >         struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> > > > > >         struct device *dev = ctx->dev;
> > > > > >         unsigned int size[2];
> > > > > >
> > > > > >         switch (vq->type) {
> > > > > >         case V4L2_BUF_TYPE_META_CAPTURE:
> > > > > >                 size[0] = ctx->dst_fmt.buffersize;
> > > > > >                 break;
> > > > > >         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> > > > > >                 size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> > > > > >                 if (*num_planes == 2)
> > > > > >                         size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> > > > > >                 break;
> > > > > >         }
> > > > > >
> > > > > >         if (*num_planes == 1) {
> > > > > >                 if (sizes[0] < size[0])
> > > > > >                         return -EINVAL;
> > > > > >         } else if (*num_planes == 2) {
> > > > > >                 if ((sizes[0] < size[0]) && (sizes[1] < size[1]))
> > > > > >                         return -EINVAL;
> > > > >
> > > > > Can we just use a loop here and combine the 2 cases above?
> > > > >
> > > > > Also, we need to fail with -EINVAL if *num_planes is > 2.
> > > > >
> > > > > >         } else {
> > > > > >                 *num_planes = 1;
> > > > > >                 sizes[0] = size[0];
> > > > >
> > > > > This should be the case if *num_planes == 0 and the number of planes
> > > > > and sizes should match the currently active format.
> > > > >
> > > > I appreciate your comments,
> > > >
> > > > Ok, I will update as following:
> > > > static int mtk_fd_vb2_queue_setup(struct vb2_queue *vq,
> > > >                                   unsigned int *num_buffers,
> > > >                                   unsigned int *num_planes,
> > > >                                   unsigned int sizes[],
> > > >                                   struct device *alloc_devs[])
> > > > {
> > > >         struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> > > >         unsigned int size[2];
> > > >         unsigned int plane;
> > > >
> > > >         switch (vq->type) {
> > > >         case V4L2_BUF_TYPE_META_CAPTURE:
> > > >                 size[0] = ctx->dst_fmt.buffersize;
> > > >                 break;
> > > >         case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> > > >                 size[0] = ctx->src_fmt.plane_fmt[0].sizeimage;
> > > >                 if (*num_planes == 2)
> > > >                         size[1] = ctx->src_fmt.plane_fmt[1].sizeimage;
> > > >                 break;
> > > >         }
> > > >
> > > >         if (*num_planes > 2)
> > > >                 return -EINVAL;
> > > >         if (*num_planes == 0) {
> > > >                 if (vq->type == V4L2_BUF_TYPE_META_CAPTURE) {
> > > >                         sizes[0] = ctx->dst_fmt.buffersize;
> > > >                         *num_planes = 1;
> > > >                         return 0;
> > > >                 }
> > > >
> > > >                 *num_planes = ctx->src_fmt.num_planes;
> > > >                 for (plane = 0; plane < *num_planes; plane++)
> > > >                         sizes[plane] = ctx->src_fmt.plane_fmt[plane].sizeimage;
> > > >                 return 0;
> > > >         }
> > > >
> > > >         for (plane = 0; plane < *num_planes; plane++) {
> > > >                 if(sizes[plane] < size[plane])
> > > >                         return -EINVAL;
> > > >         }
> > > >         return 0;
> > > > }
> > > >
> > >
> > > Looks good, thanks!
> > >
> > > > > >         }
> > > > > >
> > > > > >         return 0;
> > > > > > }
> > > > > >
> > > > > > > [snip]
> > > > > > >
> > > > > > > > > > +static void mtk_fd_vb2_stop_streaming(struct vb2_queue *vq)
> > > > > > > > > > +{
> > > > > > > > > > +   struct mtk_fd_ctx *ctx = vb2_get_drv_priv(vq);
> > > > > > > > > > +   struct vb2_buffer *vb;
> > > > > > > > >
> > > > > > > > > How do we guarantee here that the hardware isn't still accessing the buffers
> > > > > > > > > removed below?
> > > > > > > > >
> > > > > > > > Maybe we can check the driver state flag and aborting the unfinished
> > > > > > > > jobs?
> > > > > > > > (fd_hw->state == FD_ENQ)
> > > > > > > >
> > > > > > >
> > > > > > > Yes, we need to either cancel or wait for the currently processing
> > > > > > > job. It depends on hardware capabilities, but cancelling is generally
> > > > > > > preferred for the lower latency.
> > > > > > >
> > > > > > Ok, it the state is ENQ, then we can disable the FD hw by controlling
> > > > > > the registers.
> > > > > >
> > > > > > for example:
> > > > > >         writel(0x0, fd->fd_base + FD_HW_ENABLE);
> > > > > >         writel(0x0, fd->fd_base + FD_INT_EN);
> > > > > >
> > > > >
> > > > > What's exactly the effect of writing 0 to FD_HW_ENABLE?
> > > > >
> > > > Sorry, my last reply didn't solve the question,
> > > > we should implement a mtk_fd_job_abort() for v4l2_m2m_ops().
> > > >
> > > > which is able to readl_poll_timeout_atomic()
> > > > and check the HW busy bits in the register FD_INT_EN;
> > > >
> > > > if they are not cleared until timeout, we could handle the last
> > > > processing job.
> > > > Otherwise, the FD irq handler should have handled the last processing
> > > > job and we could continue the stop_streaming().
> > > >
> > > > For job_abort():
> > > > static void mtk_fd_job_abort(void *priv)
> > > > {
> > > >         struct mtk_fd_ctx *ctx = priv;
> > > >         struct mtk_fd_dev *fd = ctx->fd_dev;
> > > >         u32 val;
> > > >         u32 ret;
> > > >
> > > >         ret = readl_poll_timeout_atomic(fd->fd_base + MTK_FD_REG_OFFSET_INT_EN,
> > > >                                         val,
> > > >                                         (val & MTK_FD_HW_BUSY_MASK) ==
> > > >                                         MTK_FD_HW_STATE_IS_BUSY,
> > > >                                         USEC_PER_MSEC, MTK_FD_STOP_HW_TIMEOUT);
> > >
> > > Hmm, would it be possible to avoid the busy wait by having a
> > > completion that could be signalled from the interrupt handler?
> > >
> > > Best regards,
> > > Tomasz
> >
> > I suppose that would be wakeup a wait queue in the interrupt handler,
> > the the wait_event_interrupt_timeout() will be used in here and system
> > suspend e.g. mtk_fd_suspend().
> 
> Yes, that should work.
> 
> > Or do you suggest to wait_event_interrupt_timeout() every frame in the
> > mtk_fd_ipi_handler()?
> 
> Nope, we shouldn't need that.
> 
> > I think maybe the readl_poll_timeout_atomic would be good enough.
> >
> 
> Not really. Busy waiting should be avoided as much as possible. What's
> the point of entering suspend if you end up burning the power by
> spinning the CPU for some milliseconds?
> 
Ok, I see, busy waiting is not a good idea, I will use the wait queue
instead. the job abort will refine as following:

static void mtk_fd_job_abort(void *priv)
{
	struct mtk_fd_ctx *ctx = priv;
	struct mtk_fd_dev *fd = ctx->fd_dev;
	u32 ret;

	ret = wait_event_interruptible_timeout
		(fd->wq, (fd->fd_irq_result & MTK_FD_HW_IRQ_MASK),
		 usecs_to_jiffies(50000));
	if (ret)
		mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_ERROR);
	dev_dbg(fd->dev, "%s, ret:%d\n", __func__, ret);

	fd->fd_irq_result = 0;
}

static struct v4l2_m2m_ops fd_m2m_ops = {
	.device_run = mtk_fd_device_run,
	.job_abort = mtk_fd_job_abort,
};

and we could use it in suspend.
static int mtk_fd_suspend(struct device *dev)
{
	struct mtk_fd_dev *fd = dev_get_drvdata(dev);

	if (pm_runtime_suspended(dev))
		return 0;

	if (fd->fd_stream_count)
		mtk_fd_job_abort(fd->ctx);

	/* suspend FD HW */
	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_INT_EN);
	writel(0x0, fd->fd_base + MTK_FD_REG_OFFSET_HW_ENABLE);
	clk_disable_unprepare(fd->fd_clk);
	dev_dbg(dev, "%s:disable clock\n", __func__);

	return 0;
}

static irqreturn_t mtk_fd_irq(int irq, void *data)
{
	struct mtk_fd_dev *fd = (struct mtk_fd_dev *)data;

	fd->fd_irq_result = readl(fd->fd_base + MTK_FD_REG_OFFSET_INT_VAL);
	wake_up_interruptible(&fd->wq);
	fd->output->number = readl(fd->fd_base + MTK_FD_REG_OFFSET_RESULT);
	dev_dbg(fd->dev, "mtk_fd_face_num:%d\n", fd->output->number);

	pm_runtime_put((fd->dev));
	mtk_fd_hw_job_finish(fd, VB2_BUF_STATE_DONE);
	return IRQ_HANDLED;
}
> >
> > One more thing, for the mtk_fd_video_device_register()
> > Sorry that I would need to use intermediate variable here since the 80
> > columns check.
> >
> >         function = MEDIA_ENT_F_PROC_VIDEO_STATISTICS;
> >         ret = v4l2_m2m_register_media_controller(m2m_dev, vfd, function);
> 
> Why not just make it like this:
> 
> ret = v4l2_m2m_register_media_controller(m2m_dev,
>                 MEDIA_ENT_F_PROC_VIDEO_STATISTICS);
> 
> The above line is aligned using tabs so that its end is as close to
> the 80 character boundary as possible.
> 
I tried but the checkpatch script still gave me a check saying align to
the scope, I will refine as following:

	ret = v4l2_m2m_register_media_controller
		(m2m_dev, vfd, MEDIA_ENT_F_PROC_VIDEO_STATISTICS);


Thanks and best regards,
Jerry
> Best regards,
> Tomasz



_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH] KVM: arm/arm64: vgic: Allow more than 256 vcpus for KVM_IRQ_LINE
From: Peter Maydell @ 2019-09-03 11:33 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: kvm-devel, Suzuki K Poulose, qemu-arm, James Morse,
	Julien Thierry, Zenghui Yu, kvmarm, arm-mail-list
In-Reply-To: <20190818140710.23920-1-maz@kernel.org>

On Sun, 18 Aug 2019 at 15:07, Marc Zyngier <maz@kernel.org> wrote:
>
> While parts of the VGIC support a large number of vcpus (we
> bravely allow up to 512), other parts are more limited.
>
> One of these limits is visible in the KVM_IRQ_LINE ioctl, which
> only allows 256 vcpus to be signalled when using the CPU or PPI
> types. Unfortunately, we've cornered ourselves badly by allocating
> all the bits in the irq field.
>
> Since the irq_type subfield (8 bit wide) is currently only taking
> the values 0, 1 and 2 (and we have been careful not to allow anything
> else), let's reduce this field to only 4 bits, and allocate the
> remaining 4 bits to a vcpu2_index, which acts as a multiplier:
>
>   vcpu_id = 256 * vcpu2_index + vcpu_index
>
> With that, and a new capability (KVM_CAP_ARM_IRQ_LINE_LAYOUT_2)
> allowing this to be discovered, it becomes possible to inject
> PPIs to up to 4096 vcpus. But please just don't.
>
> Reported-by: Zenghui Yu <yuzenghui@huawei.com>
> Signed-off-by: Marc Zyngier <maz@kernel.org>

> diff --git a/Documentation/virt/kvm/api.txt b/Documentation/virt/kvm/api.txt
> index 2d067767b617..85518bfb2a99 100644
> --- a/Documentation/virt/kvm/api.txt
> +++ b/Documentation/virt/kvm/api.txt
> @@ -753,8 +753,8 @@ in-kernel irqchip (GIC), and for in-kernel irqchip can tell the GIC to
>  use PPIs designated for specific cpus.  The irq field is interpreted
>  like this:
>
> -  bits:  | 31 ... 24 | 23  ... 16 | 15    ...    0 |
> -  field: | irq_type  | vcpu_index |     irq_id     |
> +  bits:  |  31 ... 28  | 27 ... 24 | 23  ... 16 | 15 ... 0 |
> +  field: | vcpu2_index | irq_type  | vcpu_index |  irq_id  |
>
>  The irq_type field has the following values:
>  - irq_type[0]: out-of-kernel GIC: irq_id 0 is IRQ, irq_id 1 is FIQ
> @@ -766,6 +766,10 @@ The irq_type field has the following values:
>
>  In both cases, level is used to assert/deassert the line.
>
> +When KVM_CAP_ARM_IRQ_LINE_LAYOUT_2 is supported, the target vcpu is
> +identified as (256 * vcpu2_index + vcpu_index). Otherwise, vcpu2_index
> +must be zero.
> +
>  struct kvm_irq_level {
>         union {
>                 __u32 irq;     /* GSI */

> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
> index 35a069815baf..c1385911de69 100644
> --- a/virt/kvm/arm/arm.c
> +++ b/virt/kvm/arm/arm.c
> @@ -182,6 +182,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
>         int r;
>         switch (ext) {
>         case KVM_CAP_IRQCHIP:
> +       case KVM_CAP_ARM_IRQ_LINE_LAYOUT_2:
>                 r = vgic_present;
>                 break;

Shouldn't we be advertising the capability always, not just if
the VGIC is present? The KVM_IRQ_LINE ioctl can be used for
directly signalling IRQs to vCPUs even if we're using an
out-of-kernel irqchip model.

The general principle of the API change/extension looks OK to me.

thanks
-- PMM

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v2] PCI: Remove unused includes and superfluous struct declaration
From: Krzysztof Wilczynski @ 2019-09-03 11:30 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: devicetree, Lorenzo Pieralisi, Jingoo Han, Joerg Roedel,
	linux-pci, linux-kernel, iommu, Rob Herring, Thomas Petazzoni,
	Gustavo Pimentel, Frank Rowand, linux-arm-kernel
In-Reply-To: <20190901112506.8469-1-kw@linux.com>

Remove <linux/pci.h> and <linux/msi.h> from being included
directly as part of the include/linux/of_pci.h, and remove
superfluous declaration of struct of_phandle_args.

Move users of include <linux/of_pci.h> to include <linux/pci.h>
and <linux/msi.h> directly rather than rely on both being
included transitively through <linux/of_pci.h>.

Signed-off-by: Krzysztof Wilczynski <kw@linux.com>
---
 drivers/iommu/of_iommu.c                          | 2 ++
 drivers/irqchip/irq-gic-v2m.c                     | 1 +
 drivers/irqchip/irq-gic-v3-its-pci-msi.c          | 1 +
 drivers/pci/controller/dwc/pcie-designware-host.c | 1 +
 drivers/pci/controller/pci-aardvark.c             | 1 +
 drivers/pci/controller/pci-thunder-pem.c          | 1 +
 drivers/pci/pci.c                                 | 1 +
 drivers/pci/probe.c                               | 1 +
 include/linux/of_pci.h                            | 5 ++---
 9 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 614a93aa5305..026ad2b29dcd 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -8,6 +8,8 @@
 #include <linux/export.h>
 #include <linux/iommu.h>
 #include <linux/limits.h>
+#include <linux/pci.h>
+#include <linux/msi.h>
 #include <linux/of.h>
 #include <linux/of_iommu.h>
 #include <linux/of_pci.h>
diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index e88e75c22b6a..fbec07d634ad 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -17,6 +17,7 @@
 #include <linux/irq.h>
 #include <linux/irqdomain.h>
 #include <linux/kernel.h>
+#include <linux/pci.h>
 #include <linux/msi.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
diff --git a/drivers/irqchip/irq-gic-v3-its-pci-msi.c b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
index 229d586c3d7a..87711e0f8014 100644
--- a/drivers/irqchip/irq-gic-v3-its-pci-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-pci-msi.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/acpi_iort.h>
+#include <linux/pci.h>
 #include <linux/msi.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index d3156446ff27..7a9bef993e57 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -10,6 +10,7 @@
 
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdomain.h>
+#include <linux/msi.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
 #include <linux/pci_regs.h>
diff --git a/drivers/pci/controller/pci-aardvark.c b/drivers/pci/controller/pci-aardvark.c
index fc0fe4d4de49..3a05f6ca95b0 100644
--- a/drivers/pci/controller/pci-aardvark.c
+++ b/drivers/pci/controller/pci-aardvark.c
@@ -16,6 +16,7 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
+#include <linux/msi.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
 
diff --git a/drivers/pci/controller/pci-thunder-pem.c b/drivers/pci/controller/pci-thunder-pem.c
index f127ce8bd4ef..9491e266b1ea 100644
--- a/drivers/pci/controller/pci-thunder-pem.c
+++ b/drivers/pci/controller/pci-thunder-pem.c
@@ -6,6 +6,7 @@
 #include <linux/bitfield.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
+#include <linux/pci.h>
 #include <linux/of_address.h>
 #include <linux/of_pci.h>
 #include <linux/pci-acpi.h>
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 484e35349565..571e7e00984b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -13,6 +13,7 @@
 #include <linux/delay.h>
 #include <linux/dmi.h>
 #include <linux/init.h>
+#include <linux/msi.h>
 #include <linux/of.h>
 #include <linux/of_pci.h>
 #include <linux/pci.h>
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 169943f17a4c..11b11a652d18 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -7,6 +7,7 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/pci.h>
+#include <linux/msi.h>
 #include <linux/of_device.h>
 #include <linux/of_pci.h>
 #include <linux/pci_hotplug.h>
diff --git a/include/linux/of_pci.h b/include/linux/of_pci.h
index 21a89c4880fa..29658c0ee71f 100644
--- a/include/linux/of_pci.h
+++ b/include/linux/of_pci.h
@@ -2,11 +2,10 @@
 #ifndef __OF_PCI_H
 #define __OF_PCI_H
 
-#include <linux/pci.h>
-#include <linux/msi.h>
+#include <linux/types.h>
+#include <linux/errno.h>
 
 struct pci_dev;
-struct of_phandle_args;
 struct device_node;
 
 #if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_PCI)
-- 
2.23.0


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [PATCH v3 5/7] arm64: dts: mt8183: add usb and phy nodes
From: kbuild test robot @ 2019-09-03 11:25 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: Mark Rutland, devicetree, Mathias Nyman, Greg Kroah-Hartman,
	linux-usb, linux-kernel, Chunfeng Yun, Rob Herring,
	linux-mediatek, kbuild-all, Matthias Brugger, linux-arm-kernel
In-Reply-To: <1567150854-30033-6-git-send-email-chunfeng.yun@mediatek.com>

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

Hi Chunfeng,

I love your patch! Yet something to improve:

[auto build test ERROR on linus/master]
[cannot apply to v5.3-rc7 next-20190902]
[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/add-support-USB-for-MT8183/20190901-163637
config: arm64-allmodconfig (attached as .config)
compiler: aarch64-linux-gcc (GCC) 7.4.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.4.0 make.cross ARCH=arm64 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

>> arch/arm64/boot/dts/mediatek/mt8183-evb.dts:11:10: fatal error: mt6358.dtsi: No such file or directory
      11 | #include "mt6358.dtsi"
         |          ^~~~~~~~~~~~~
   compilation terminated.

vim +11 arch/arm64/boot/dts/mediatek/mt8183-evb.dts

  > 11	#include "mt6358.dtsi"
    12	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 66348 bytes --]

[-- Attachment #3: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 1/1] mm/pgtable/debug: Add test validating architecture page table helpers
From: kbuild test robot @ 2019-09-03 11:13 UTC (permalink / raw)
  To: Anshuman Khandual
  Cc: Mark Rutland, linux-ia64, linux-sh, Tetsuo Handa, James Hogan,
	Heiko Carstens, Michal Hocko, linux-mm, Paul Mackerras,
	sparclinux, Thomas Gleixner, linux-s390, Michael Ellerman, x86,
	Russell King - ARM Linux, Matthew Wilcox, Steven Price,
	Jason Gunthorpe, linux-arm-kernel, linux-snps-arc, Kees Cook,
	Anshuman Khandual, Masahiro Yamada, Mark Brown, Dan Williams,
	Vlastimil Babka, Sri Krishna chowdary, Ard Biesheuvel,
	Greg Kroah-Hartman, Dave Hansen, linux-mips, Ralf Baechle,
	linux-kernel, Peter Zijlstra, Mike Rapoport, Paul Burton,
	kbuild-all, Vineet Gupta, Martin Schwidefsky, Andrew Morton,
	linuxppc-dev, David S. Miller
In-Reply-To: <1567497706-8649-2-git-send-email-anshuman.khandual@arm.com>

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

Hi Anshuman,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[cannot apply to v5.3-rc7 next-20190902]
[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/Anshuman-Khandual/mm-debug-Add-tests-for-architecture-exported-page-table-helpers/20190903-162959
config: m68k-allmodconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 7.4.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.4.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from arch/m68k/include/asm/bug.h:32:0,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:12,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/m68k/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:78,
                    from arch/m68k/include/asm/irqflags.h:6,
                    from include/linux/irqflags.h:16,
                    from arch/m68k/include/asm/atomic.h:6,
                    from include/linux/atomic.h:7,
                    from include/linux/mm_types_task.h:13,
                    from include/linux/mm_types.h:5,
                    from include/linux/hugetlb.h:5,
                    from mm/arch_pgtable_test.c:14:
   mm/arch_pgtable_test.c: In function 'pmd_clear_tests':
>> arch/m68k/include/asm/page.h:31:22: error: lvalue required as unary '&' operand
    #define pmd_val(x) ((&x)->pmd[0])
                         ^
   include/asm-generic/bug.h:124:25: note: in definition of macro 'WARN_ON'
     int __ret_warn_on = !!(condition);    \
                            ^~~~~~~~~
>> arch/m68k/include/asm/motorola_pgtable.h:138:26: note: in expansion of macro 'pmd_val'
    #define pmd_none(pmd)  (!pmd_val(pmd))
                             ^~~~~~~
>> mm/arch_pgtable_test.c:233:11: note: in expansion of macro 'pmd_none'
     WARN_ON(!pmd_none(READ_ONCE(*pmdp)));
              ^~~~~~~~
   mm/arch_pgtable_test.c: In function 'pmd_populate_tests':
>> arch/m68k/include/asm/page.h:31:22: error: lvalue required as unary '&' operand
    #define pmd_val(x) ((&x)->pmd[0])
                         ^
   include/asm-generic/bug.h:124:25: note: in definition of macro 'WARN_ON'
     int __ret_warn_on = !!(condition);    \
                            ^~~~~~~~~
   arch/m68k/include/asm/motorola_pgtable.h:139:25: note: in expansion of macro 'pmd_val'
    #define pmd_bad(pmd)  ((pmd_val(pmd) & _DESCTYPE_MASK) != _PAGE_TABLE)
                            ^~~~~~~
>> mm/arch_pgtable_test.c:245:10: note: in expansion of macro 'pmd_bad'
     WARN_ON(pmd_bad(READ_ONCE(*pmdp)));
             ^~~~~~~

vim +/pmd_none +233 mm/arch_pgtable_test.c

   228	
   229	static void pmd_clear_tests(pmd_t *pmdp)
   230	{
   231		memset(pmdp, RANDOM_NZVALUE, sizeof(pmd_t));
   232		pmd_clear(pmdp);
 > 233		WARN_ON(!pmd_none(READ_ONCE(*pmdp)));
   234	}
   235	
   236	static void pmd_populate_tests(struct mm_struct *mm, pmd_t *pmdp,
   237				       pgtable_t pgtable)
   238	{
   239		/*
   240		 * This entry points to next level page table page.
   241		 * Hence this must not qualify as pmd_bad().
   242		 */
   243		pmd_clear(pmdp);
   244		pmd_populate(mm, pmdp, pgtable);
 > 245		WARN_ON(pmd_bad(READ_ONCE(*pmdp)));
   246	}
   247	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 50918 bytes --]

[-- Attachment #3: Type: text/plain, Size: 176 bytes --]

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* RE: [PATCH] ACPI: support for NXP i2c controller
From: Meenakshi Aggarwal @ 2019-09-03 11:08 UTC (permalink / raw)
  To: Oleksij Rempel, Rafael J. Wysocki, Chuanhua Han, Wolfram Sang,
	Andy Shevchenko, Biwen Li
  Cc: Udit Kumar, Sascha Hauer, Rafael J. Wysocki,
	Linux Kernel Mailing List, Leo Li, ACPI Devel Maling List,
	linux-i2c, Shawn Guo, Linux ARM, Len Brown
In-Reply-To: <31e7c1bb-d153-5feb-0b86-946caca5206c@pengutronix.de>

+biwen

> -----Original Message-----
> From: Oleksij Rempel <o.rempel@pengutronix.de>
> Sent: Tuesday, September 3, 2019 4:20 PM
> To: Rafael J. Wysocki <rafael@kernel.org>; Chuanhua Han
> <chuanhua.han@nxp.com>; Wolfram Sang <wsa@the-dreams.de>; Andy
> Shevchenko <andy.shevchenko@gmail.com>
> Cc: Rafael J. Wysocki <rjw@rjwysocki.net>; Len Brown <lenb@kernel.org>;
> Shawn Guo <shawnguo@kernel.org>; Sascha Hauer <s.hauer@pengutronix.de>;
> ACPI Devel Maling List <linux-acpi@vger.kernel.org>; Linux Kernel Mailing List
> <linux-kernel@vger.kernel.org>; linux-i2c <linux-i2c@vger.kernel.org>; Linux
> ARM <linux-arm-kernel@lists.infradead.org>; Leo Li <leoyang.li@nxp.com>;
> Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>; Udit Kumar
> <udit.kumar@nxp.com>
> Subject: Re: [PATCH] ACPI: support for NXP i2c controller
> 
> One more question,
> 
> On 02.09.19 22:56, Rafael J. Wysocki wrote:
> > On Thu, Jul 11, 2019 at 12:35 PM Chuanhua Han <chuanhua.han@nxp.com>
> wrote:
> >>
> >> Enable NXP i2c controller to boot with ACPI
> >>
> >> Signed-off-by: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
> >> Signed-off-by: Udit Kumar <udit.kumar@nxp.com>
> >> Signed-off-by: Chuanhua Han <chuanhua.han@nxp.com>
> >
> > Wolfram, any objections to this from the i2c side?
> >
> >> ---
> >>   drivers/acpi/acpi_apd.c      |  6 ++++++
> >>   drivers/i2c/busses/i2c-imx.c | 15 +++++++++++++++
> >>   2 files changed, 21 insertions(+)
> >>
> >> diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index
> >> ff47317..cf8566c 100644
> >> --- a/drivers/acpi/acpi_apd.c
> >> +++ b/drivers/acpi/acpi_apd.c
> >> @@ -165,6 +165,11 @@ static const struct apd_device_desc
> thunderx2_i2c_desc = {
> >>          .fixed_clk_rate = 125000000,
> >>   };
> >>
> >> +static const struct apd_device_desc nxp_i2c_desc = {
> >> +       .setup = acpi_apd_setup,
> >> +       .fixed_clk_rate = 350000000,
> >> +};
> 
> I'm not ACPI expert, so need here some help for understanding. Here is ACPI
> table for
> NXP0001 id (found on the internet):
> +  Device(I2C0) {
> +    Name(_HID, "NXP0001")
> +    Name(_UID, 0)
> +    Name(_CRS, ResourceTemplate() {
> +      Memory32Fixed(ReadWrite, I2C0_BASE, I2C_LEN)
> +      Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { I2C0_IT }
> +    }) // end of _CRS for i2c0 device
> +    Name (_DSD, Package () {
> +      ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
> +      Package () {
> +         Package () {"clock-frequency", DEFAULT_PLAT_FREQ}, //This is
> + device specific
> data, Need to see how to pass clk stuff
> +      }
> +    })
> 
> Should kernel some how get proper clock-frequency from the ACPI? Or we still
> need to use hard coded .fixed_clk_rate in the kernel?
> 
> 
> >>   static const struct apd_device_desc hip08_spi_desc = {
> >>          .setup = acpi_apd_setup,
> >>          .fixed_clk_rate = 250000000,
> >> @@ -238,6 +243,7 @@ static const struct acpi_device_id
> acpi_apd_device_ids[] = {
> >>          { "HISI02A1", APD_ADDR(hip07_i2c_desc) },
> >>          { "HISI02A2", APD_ADDR(hip08_i2c_desc) },
> >>          { "HISI0173", APD_ADDR(hip08_spi_desc) },
> >> +       { "NXP0001", APD_ADDR(nxp_i2c_desc) },
> >>   #endif
> >>          { }
> >>   };
> >> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> >> index b1b8b93..99f9b96 100644
> >> --- a/drivers/i2c/busses/i2c-imx.c
> >> +++ b/drivers/i2c/busses/i2c-imx.c
> >> @@ -44,6 +44,7 @@
> >>   #include <linux/pm_runtime.h>
> >>   #include <linux/sched.h>
> >>   #include <linux/slab.h>
> >> +#include <linux/acpi.h>
> >>
> >>   /* This will be the driver name the kernel reports */
> >>   #define DRIVER_NAME "imx-i2c"
> >> @@ -255,6 +256,12 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
> >>   };
> >>   MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
> >>
> >> +static const struct acpi_device_id i2c_imx_acpi_ids[] = {
> >> +       {"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata},
> >> +       { }
> >> +};
> >> +MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids);
> >> +
> >>   static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx)
> >>   {
> >>          return i2c_imx->hwdata->devtype == IMX1_I2C;
> >> @@ -1052,6 +1059,9 @@ static int i2c_imx_probe(struct platform_device
> *pdev)
> >>   {
> >>          const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
> >>                                                             &pdev->dev);
> >> +       const struct acpi_device_id *acpi_id =
> >> +                       acpi_match_device(i2c_imx_acpi_ids,
> >> +                                         &pdev->dev);
> >>          struct imx_i2c_struct *i2c_imx;
> >>          struct resource *res;
> >>          struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
> >> @@ -1079,6 +1089,9 @@ static int i2c_imx_probe(struct platform_device
> *pdev)
> >>
> >>          if (of_id)
> >>                  i2c_imx->hwdata = of_id->data;
> >> +       else if (acpi_id)
> >> +               i2c_imx->hwdata = (struct imx_i2c_hwdata *)
> >> +                               acpi_id->driver_data;
> >>          else
> >>                  i2c_imx->hwdata = (struct imx_i2c_hwdata *)
> >>                                  platform_get_device_id(pdev)->driver_data;
> >> @@ -1091,6 +1104,7 @@ static int i2c_imx_probe(struct platform_device
> *pdev)
> >>          i2c_imx->adapter.nr             = pdev->id;
> >>          i2c_imx->adapter.dev.of_node    = pdev->dev.of_node;
> >>          i2c_imx->base                   = base;
> >> +       ACPI_COMPANION_SET(&i2c_imx->adapter.dev,
> ACPI_COMPANION(&pdev->dev));
> >>
> >>          /* Get I2C clock */
> >>          i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
> >> @@ -1253,6 +1267,7 @@ static struct platform_driver i2c_imx_driver = {
> >>                  .name = DRIVER_NAME,
> >>                  .pm = &i2c_imx_pm_ops,
> >>                  .of_match_table = i2c_imx_dt_ids,
> >> +               .acpi_match_table = ACPI_PTR(i2c_imx_acpi_ids),
> >>          },
> >>          .id_table = imx_i2c_devtype,
> >>   };
> >> --
> >> 2.9.5
> >>
> >
> 
> Kind regards,
> Oleksij Rempel
> 
> --
> Pengutronix e.K.                           |                             |
> Industrial Linux Solutions                 |
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fwww.pen
> gutronix.de%2F&amp;data=02%7C01%7Cmeenakshi.aggarwal%40nxp.com%7C
> 53801e593fbc47606f1d08d7305c8024%7C686ea1d3bc2b4c6fa92cd99c5c30163
> 5%7C0%7C0%7C637031046148090453&amp;sdata=PkS19Tph5n4gMcAJaG3sKs
> ROkOm%2BhoUykGRdYy0PUOc%3D&amp;reserved=0  |
> Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
> Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [RFC 5/9] dt-bindings: arm: samsung: Convert Exynos PMU bindings to json-schema
From: Krzysztof Kozlowski @ 2019-09-03 11:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Alessandro Zummo, Alexandre Belloni,
	Lars-Peter Clausen, Arnd Bergmann, devicetree,
	open list:IIO SUBSYSTEM AND DRIVERS, Marek Szyprowski,
	linux-kernel@vger.kernel.org, Tomasz Figa, linux-samsung-soc,
	moderated list:ARM/FREESCALE IMX / MXC ARM ARCHITECTURE,
	Peter Meerwald-Stadler, Hartmut Knaack, Olof Johansson,
	open list:REAL TIME CLOCK (RTC) SUBSYSTEM, notify,
	Jonathan Cameron, Paweł Chmiel
In-Reply-To: <CAL_JsqKdsABWK9Og_f38T9zf3SCFFdhU8WOJ4uJjREantoYvYQ@mail.gmail.com>

On Tue, 3 Sep 2019 at 10:25, Rob Herring <robh+dt@kernel.org> wrote:
>
> On Tue, Sep 3, 2019 at 8:58 AM Krzysztof Kozlowski <krzk@kernel.org> wrote:
> >
> > On Mon, 26 Aug 2019 at 13:54, Rob Herring <robh+dt@kernel.org> wrote:
> > >
> > > On Fri, Aug 23, 2019 at 9:54 AM Krzysztof Kozlowski <krzk@kernel.org> wrote:
> > > >
> > > > Convert Samsung Exynos Power Management Unit (PMU) bindings to DT schema
> > > > format using json-schema.
> > > >
> > > > Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
> > > > ---
> > > >  .../devicetree/bindings/arm/samsung/pmu.txt   | 72 --------------
> > > >  .../devicetree/bindings/arm/samsung/pmu.yaml  | 93 +++++++++++++++++++
> > > >  2 files changed, 93 insertions(+), 72 deletions(-)
> > > >  delete mode 100644 Documentation/devicetree/bindings/arm/samsung/pmu.txt
> > > >  create mode 100644 Documentation/devicetree/bindings/arm/samsung/pmu.yaml
> > >
> > >
> > > > diff --git a/Documentation/devicetree/bindings/arm/samsung/pmu.yaml b/Documentation/devicetree/bindings/arm/samsung/pmu.yaml
> > > > new file mode 100644
> > > > index 000000000000..818c6f3488ef
> > > > --- /dev/null
> > > > +++ b/Documentation/devicetree/bindings/arm/samsung/pmu.yaml
> > > > @@ -0,0 +1,93 @@
> > > > +# SPDX-License-Identifier: GPL-2.0
> > > > +%YAML 1.2
> > > > +---
> > > > +$id: http://devicetree.org/schemas/arm/samsung/pmu.yaml#
> > > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > > +
> > > > +title: Samsung Exynos SoC series Power Management Unit (PMU)
> > > > +
> > > > +maintainers:
> > > > +  - Krzysztof Kozlowski <krzk@kernel.org>
> > > > +
> > > > +properties:
> > > > +  compatible:
> > > > +    items:
> > > > +      - enum:
> > > > +          - samsung,exynos3250-pmu
> > > > +          - samsung,exynos4210-pmu
> > > > +          - samsung,exynos4412-pmu
> > > > +          - samsung,exynos5250-pmu
> > > > +          - samsung,exynos5260-pmu
> > > > +          - samsung,exynos5410-pmu
> > > > +          - samsung,exynos5420-pmu
> > > > +          - samsung,exynos5433-pmu
> > > > +          - samsung,exynos7-pmu
> > > > +      - const: syscon
> > > > +
> > > > +  reg:
> > > > +    maxItems: 1
> > > > +
> > > > +  '#clock-cells':
> > > > +    const: 1
> > > > +
> > > > +  clock-names:
> > > > +    description:
> > > > +      list of clock names for particular CLKOUT mux inputs
> > > > +    # TODO: what is the maximum number of elements (mux inputs)?
> > > > +    minItems: 1
> > > > +    maxItems: 32
> > > > +    items:
> > > > +      - enum:
> > >
> > > This isn't correct as you are only defining possible names for the
> > > first item. Drop the '-' (making items a schema instead of a list) and
> > > then it applies to all. However, doing that will cause a meta-schema
> > > error which I need to fix to allow. Or if there's a small set of
> > > possibilities of number of inputs, you can list them under a 'oneOf'
> > > list.
> >
> > Mhmm, I cannot test it or I have an error in the schema. if I
> > understand correctly, this would be:
> >
> >   clock-names:
> >     description:
> >       List of clock names for particular CLKOUT mux inputs
> >     minItems: 1
> >     maxItems: 16
> >     items:
> >       clkout0
> >       clkout1
> >       clkout2
> >       clkout3
> >       clkout4
> >       clkout5
> >       clkout6
> >       clkout7
> >       clkout8
> >       clkout9
> >       clkout10
> >       clkout11
> >       clkout12
> >       clkout13
> >       clkout14
> >       clkout15
> >       clkout16
> >
> > Now it produces the error "ignoring, error in schema 'items'" but
> > maybe it is expected with current meta-schema?
>
> 'make dt_binding_check' will give more detailed errors.
>
> Are the inputs always contiguous 0-N? If so, you want:
>
> items:
>   - const: clkout0
>   - const: clkout1
>   - const: clkout2
>   ...
>
> If you want to express any number and order of strings is valid, then you need:
>
> items:
>   enum:
>     - clkout0
>     - clkout1
>     - clkout2
>
> Doing that is discouraged for bindings though. Currently, it will
> generate an error from the meta-schema, but we could change that.

It's the second case. The inputs are not contiguous. Examples:

system-controller {
    compatible = "samsung,exynos3250-pmu", "syscon";
    clock-names = "clkout8";
    clocks = <&cmu CLK_FIN_PLL>;
}

system-controller {
    compatible = "samsung,exynos4412-pmu", "syscon";
    clock-names = "clkout0", "clkout1", "clkout2", "clkout3",
                  "clkout4", "clkout8", "clkout9";
    clocks = <&clock CLK_OUT_DMC>, <&clock CLK_OUT_TOP>,
             <&clock CLK_OUT_LEFTBUS>, <&clock CLK_OUT_RIGHTBUS>,
             <&clock CLK_OUT_CPU>, <&clock CLK_XXTI>, <&clock CLK_XUSBXTI>;
}

The bindings never required any specific ordering. Also the driver
just go through all indices and parses them.

Your second syntax fails:
Documentation/devicetree/bindings/arm/samsung/pmu.yaml:
properties:clock-names:items: {'enum': ['clkout0', 'clkout1',
'clkout2', 'clkout3', 'clkout4', 'clkout5', 'clkout6', 'clkout7',
'clkout8', 'clkout9', 'clkout10', 'clkout11', 'clkout12', 'clkout13',
'clkout14', 'clkout15', 'clkout16']} is not of type 'array'

Best regards,
Krzysztof

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v2 2/2] drm/bridge: add it6505 driver
From: allen @ 2019-09-03 10:51 UTC (permalink / raw)
  Cc: Archit Taneja, Jitao Shi, Jau-Chih Tseng, Yilun Lin, Allen Chen,
	David Airlie, open list, open list:DRM DRIVERS, Andrzej Hajda,
	moderated list:ARM/Mediatek SoC support, Laurent Pinchart,
	Pi-Hsun Shih, Matthias Brugger,
	moderated list:ARM/Mediatek SoC support
In-Reply-To: <1567507915-9844-1-git-send-email-allen.chen@ite.com.tw>

From: Allen Chen <allen.chen@ite.com.tw>

This adds support for the iTE IT6505.
This device can convert DPI signal to DP output.

Signed-off-by: Jitao Shi <jitao.shi@mediatek.com>
Signed-off-by: Yilun Lin <yllin@google.com>
Signed-off-by: Allen Chen <allen.chen@ite.com.tw>
---
 drivers/gpu/drm/bridge/Kconfig      |    7 +
 drivers/gpu/drm/bridge/Makefile     |    1 +
 drivers/gpu/drm/bridge/ite-it6505.c | 2531 +++++++++++++++++++++++++++++++++++
 3 files changed, 2539 insertions(+)
 create mode 100644 drivers/gpu/drm/bridge/ite-it6505.c

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index b6e5621..8843288 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -43,6 +43,13 @@ config DRM_DUMB_VGA_DAC
 	  Support for non-programmable RGB to VGA DAC bridges, such as ADI
 	  ADV7123, TI THS8134 and THS8135 or passive resistor ladder DACs.
 
+config DRM_ITE_IT6505
+	tristate "ITE IT6505 DP bridge"
+	depends on OF
+	select DRM_KMS_HELPER
+	help
+	  ITE IT6505 DP bridge chip driver.
+
 config DRM_LVDS_ENCODER
 	tristate "Transparent parallel to LVDS encoder support"
 	depends on OF
diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
index 06cbdcc..77d60b3 100644
--- a/drivers/gpu/drm/bridge/Makefile
+++ b/drivers/gpu/drm/bridge/Makefile
@@ -2,6 +2,7 @@
 obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
 obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
 obj-$(CONFIG_DRM_DUMB_VGA_DAC) += dumb-vga-dac.o
+obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
 obj-$(CONFIG_DRM_LVDS_ENCODER) += lvds-encoder.o
 obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
 obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
new file mode 100644
index 0000000..5031200
--- /dev/null
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -0,0 +1,2531 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/fs.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <crypto/hash.h>
+#include <crypto/sha.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+
+#define AX 0
+#define BX 1
+#define AUDSEL I2S
+#define AUDTYPE LPCM
+#define AUDFS AUD48K
+#define AUDCH 2
+/* 0: Standard I2S;1: 32bit I2S */
+#define I2SINPUTFMT 1
+/* 0: Left-justified;1: Right-justified */
+#define I2SJUSTIFIED 0
+/* 0: Data delay 1T correspond to WS;1: No data delay correspond to WS */
+#define I2SDATADELAY 0
+/* 0: is left channel;1: is right channel */
+#define I2SWSCHANNEL 0
+/* 0: MSB shift first;1: LSB shift first */
+#define I2SDATASEQ 0
+
+#define LANESWAP 0
+#define LANE 4
+#define _HBR 1
+#define ENSSC 1
+#define FLAGTRAINDOWN 150
+#define POLLINGKSVLIST 400
+#define TRAINFAILCNT 5
+#define TRAINFAILHPD 3
+#define AUX_WAIT_TIMEOUT_MS 15
+#define PCLK_DELAY 1
+#define PCLK_INV 0
+#define EDIDRETRYTIME 5
+#define SHOWVIDEOTIMING 2
+#define PWROFFRETRYTIME 5
+#define MAXPCLK 80000
+#define DEFAULTHDCP 1
+#define DEFAULTAUDIO 0
+#define DEFAULTPWRONOFF 1
+#define DEFAULTDRVHOLD 0
+#define DEFAULTPWRON 0
+#define AUX_FIFO_MAX_SIZE 0x10
+
+/* AX or BX */
+#define CHIP_VERSION BX
+
+/*
+ * 0: for bitland
+ * 1: for google kukui p2, huaqin
+ */
+#define AFE_SETTING 1
+
+u8 afe_setting_table[2][3] = {
+	{0, 0, 0},
+	{0x93, 0x2a, 0x85}
+};
+
+enum sys_status {
+	SYS_UNPLUG = 0,
+	SYS_HPD,
+	SYS_AUTOTRAIN,
+	SYS_WAIT,
+	SYS_TRAINFAIL,
+	SYS_ReHDCP,
+	SYS_NOROP,
+	SYS_Unknown,
+};
+
+enum it6505_aud_sel {
+	I2S = 0,
+	SPDIF,
+};
+
+enum it6505_aud_fs {
+	AUD24K = 0x6,
+	AUD32K = 0x3,
+	AUD48K = 0x2,
+	AUD96K = 0xA,
+	AUD192K = 0xE,
+	AUD44P1K = 0x0,
+	AUD88P2K = 0x8,
+	AUD176P4K = 0xC,
+};
+
+enum it6505_aud_type {
+	LPCM = 0,
+	NLPCM,
+	DSS,
+	HBR,
+};
+
+enum aud_word_length {
+	AUD16BIT = 0,
+	AUD18BIT,
+	AUD20BIT,
+	AUD24BIT,
+};
+
+/* Audio Sample Word Length: AUD16BIT, AUD18BIT, AUD20BIT, AUD24BIT */
+#define AUDWORDLENGTH AUD24BIT
+
+struct it6505_platform_data {
+	struct regulator *pwr18;
+	struct regulator *ovdd;
+	struct gpio_desc *gpiod_hpd;
+	struct gpio_desc *gpiod_reset;
+};
+
+struct it6505 {
+	struct drm_dp_aux aux;
+	struct drm_bridge bridge;
+	struct i2c_client *client;
+	struct edid *edid;
+	struct drm_connector connector;
+	struct drm_dp_link link;
+	struct it6505_platform_data pdata;
+	struct mutex lock;
+	struct mutex mode_lock;
+	struct regmap *regmap;
+	struct drm_display_mode vid_info;
+
+	struct notifier_block event_nb;
+	struct extcon_dev *extcon;
+	struct work_struct extcon_wq;
+	enum sys_status status;
+	bool hbr;
+	u8 en_ssc;
+	bool laneswap_disabled;
+	bool laneswap;
+
+	enum it6505_aud_sel aud_sel;
+	enum it6505_aud_fs aud_fs;
+	enum it6505_aud_type aud_type;
+	u8 aud_ch;
+	u8 i2s_input_fmt;
+	u8 i2s_justified;
+	u8 i2s_data_delay;
+	u8 i2s_ws_channel;
+	u8 i2s_data_seq;
+	u8 vidstable_done;
+	enum aud_word_length audwordlength;
+	unsigned int en_hdcp;
+	unsigned int en_pwronoff;
+	unsigned int en_audio;
+	u8 cntfsm;
+	u8 train_fail_hpd;
+	bool cp_capable;
+	bool cp_done;
+	u8 downstream_repeater;
+	u8 shainput[64];
+	u8 av[5][4];
+	u8 bv[5][4];
+	bool powered;
+	/* it6505 driver hold option */
+	unsigned int drv_hold;
+};
+
+static const struct regmap_range it6505_bridge_volatile_ranges[] = {
+	{ .range_min = 0, .range_max = 0xFF },
+};
+
+static const struct regmap_access_table it6505_bridge_volatile_table = {
+	.yes_ranges = it6505_bridge_volatile_ranges,
+	.n_yes_ranges = ARRAY_SIZE(it6505_bridge_volatile_ranges),
+};
+
+static const struct regmap_config it6505_bridge_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.volatile_table = &it6505_bridge_volatile_table,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int dptxrd(struct it6505 *it6505, unsigned int reg_addr,
+		  unsigned int *value)
+{
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_read(it6505->regmap, reg_addr, value);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "read failed reg[0x%x] err: %d", reg_addr,
+			      err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void it6505_dump(struct it6505 *it6505)
+{
+	unsigned int temp[16], i, j;
+	u8 value[16];
+	struct device *dev = &it6505->client->dev;
+
+	for (i = 0; i <= 0xff; i += 16) {
+		for (j = 0; j < 16; j++) {
+			dptxrd(it6505, i + j, temp + j);
+			value[j] = temp[j];
+		}
+		DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x] = %16ph", i, value);
+	}
+}
+
+static int dptxwr(struct it6505 *it6505, unsigned int reg_addr,
+		  unsigned int reg_val)
+{
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_write(it6505->regmap, reg_addr, reg_val);
+
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "write failed reg[0x%x] = 0x%x err = %d",
+			      reg_addr, reg_val, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int dptxset(struct it6505 *it6505, unsigned int reg, unsigned int mask,
+		   unsigned int value)
+{
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = regmap_update_bits(it6505->regmap, reg, mask, value);
+	if (err < 0) {
+		DRM_DEV_ERROR(
+			dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d",
+			reg, value, mask, err);
+		return err;
+	}
+
+	return 0;
+}
+
+static void dptx_debug_print(struct it6505 *it6505, unsigned int reg,
+			     const char *prefix)
+{
+	unsigned int val;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	if (likely(!(drm_debug & DRM_UT_DRIVER)))
+		return;
+
+	err = dptxrd(it6505, reg, &val);
+	if (err < 0)
+		DRM_DEV_DEBUG_DRIVER(dev, "%s reg%02x read error", prefix, reg);
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "%s reg%02x = 0x%02x", prefix, reg,
+				     val);
+}
+
+static inline struct it6505 *connector_to_it6505(struct drm_connector *c)
+{
+	return container_of(c, struct it6505, connector);
+}
+
+static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct it6505, bridge);
+}
+
+static void it6505_init_fsm(struct it6505 *it6505)
+{
+	it6505->aud_sel = AUDSEL;
+	it6505->aud_fs = AUDFS;
+	it6505->aud_ch = AUDCH;
+	it6505->aud_type = AUDTYPE;
+	it6505->i2s_input_fmt = I2SINPUTFMT;
+	it6505->i2s_justified = I2SJUSTIFIED;
+	it6505->i2s_data_delay = I2SDATADELAY;
+	it6505->i2s_ws_channel = I2SWSCHANNEL;
+	it6505->i2s_data_seq = I2SDATASEQ;
+	it6505->audwordlength = AUDWORDLENGTH;
+	it6505->en_audio = DEFAULTAUDIO;
+	it6505->en_hdcp = DEFAULTHDCP;
+
+	it6505->hbr = _HBR;
+	it6505->en_ssc = ENSSC;
+	it6505->laneswap = LANESWAP;
+	it6505->vidstable_done = 0;
+}
+
+static void it6505_termination_on(struct it6505 *it6505)
+{
+#if (CHIP_VERSION == BX)
+	dptxset(it6505, 0x5D, 0x80, 0x00);
+	dptxset(it6505, 0x5E, 0x02, 0x02);
+#endif
+}
+
+static void it6505_termination_off(struct it6505 *it6505)
+{
+#if (CHIP_VERSION == BX)
+	dptxset(it6505, 0x5D, 0x80, 0x80);
+	dptxset(it6505, 0x5E, 0x02, 0x00);
+	dptxset(it6505, 0x5C, 0xF0, 0x00);
+#endif
+}
+
+static bool dptx_getsinkhpd(struct it6505 *it6505)
+{
+	unsigned int value;
+	int ret;
+
+	ret = dptxrd(it6505, 0x0D, &value);
+
+	if (ret < 0)
+		return false;
+
+	return (value & BIT(1)) == BIT(1);
+}
+
+static void dptx_chgbank(struct it6505 *it6505, unsigned int bank_id)
+{
+	dptxset(it6505, 0x0F, 0x01, bank_id & BIT(0));
+}
+
+static void it6505_int_mask_on(struct it6505 *it6505)
+{
+	dptxwr(it6505, 0x09, 0x1F);
+	dptxwr(it6505, 0x0A, 0x07);
+	dptxwr(it6505, 0x0B, 0x90);
+}
+
+static void it6505_int_mask_off(struct it6505 *it6505)
+{
+	dptxwr(it6505, 0x09, 0x00);
+	dptxwr(it6505, 0x0A, 0x00);
+	dptxwr(it6505, 0x0B, 0x00);
+}
+
+static void dptx_init(struct it6505 *it6505)
+{
+	dptxwr(it6505, 0x05, 0x3B);
+	usleep_range(1000, 2000);
+	dptxwr(it6505, 0x05, 0x1F);
+	usleep_range(1000, 1500);
+}
+
+static void it6505_init_and_mask_on(struct it6505 *it6505)
+{
+	dptx_init(it6505);
+	it6505_int_mask_on(it6505);
+}
+
+static int it6505_poweron(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	int err = 0;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "it6505 already powered on");
+		return 0;
+	}
+
+	DRM_DEV_DEBUG_DRIVER(dev, "it6505 start to power on");
+
+	err = regulator_enable(pdata->pwr18);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s to enable pwr18 regulator",
+			     err ? "Failed" : "Succeeded");
+	if (err)
+		return err;
+	/* time interval between IVDD and OVDD at least be 1ms */
+	usleep_range(1000, 2000);
+	err = regulator_enable(pdata->ovdd);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s to enable ovdd regulator",
+			     err ? "Failed" : "Succeeded");
+	if (err) {
+		regulator_disable(pdata->pwr18);
+		return err;
+	}
+	/* time interval between OVDD and SYSRSTN at least be 10ms */
+	usleep_range(10000, 20000);
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	usleep_range(1000, 2000);
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
+	usleep_range(10000, 20000);
+
+	it6505_init_and_mask_on(it6505);
+	it6505->powered = true;
+	return 0;
+}
+
+static int it6505_poweroff(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	int err = 0;
+	struct device *dev = &it6505->client->dev;
+
+	if (!it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "power had been already off");
+		return 0;
+	}
+	gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
+	err = regulator_disable(pdata->pwr18);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s to disable pwr18 regulator",
+			     err ? "Failed" : "Succeeded");
+	if (err)
+		return err;
+	err = regulator_disable(pdata->ovdd);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s to disable ovdd regulator",
+			     err ? "Failed" : "Succeeded");
+	if (err)
+		return err;
+
+	kfree(it6505->edid);
+	it6505->edid = NULL;
+	it6505->powered = false;
+	return 0;
+}
+
+static void show_vid_info(struct it6505 *it6505)
+{
+	int hsync_pol, vsync_pol, interlaced;
+	int htotal, hdes, hdew, hfph, hsyncw;
+	int vtotal, vdes, vdew, vfph, vsyncw;
+	int rddata, rddata1, i;
+	int pclk, sum;
+
+	usleep_range(10000, 15000);
+	dptx_chgbank(it6505, 0);
+	dptxrd(it6505, 0xa0, &rddata);
+	hsync_pol = rddata & BIT(0);
+	vsync_pol = (rddata & BIT(2)) >> 2;
+	interlaced = (rddata & BIT(4)) >> 4;
+
+	dptxrd(it6505, 0xa1, &rddata);
+	dptxrd(it6505, 0xa2, &rddata1);
+	htotal = ((rddata1 & 0x1F) << 8) + rddata;
+
+	dptxrd(it6505, 0xa3, &rddata);
+	dptxrd(it6505, 0xa4, &rddata1);
+
+	hdes = ((rddata1 & 0x1F) << 8) + rddata;
+
+	dptxrd(it6505, 0xa5, &rddata);
+	dptxrd(it6505, 0xa6, &rddata1);
+
+	hdew = ((rddata1 & 0x1F) << 8) + rddata;
+
+	dptxrd(it6505, 0xa7, &rddata);
+	dptxrd(it6505, 0xa8, &rddata1);
+
+	hfph = ((rddata1 & 0x1F) << 8) + rddata;
+
+	dptxrd(it6505, 0xa9, &rddata);
+	dptxrd(it6505, 0xaa, &rddata1);
+
+	hsyncw = ((rddata1 & 0x1F) << 8) + rddata;
+
+	dptxrd(it6505, 0xab, &rddata);
+	dptxrd(it6505, 0xac, &rddata1);
+	vtotal = ((rddata1 & 0x0F) << 8) + rddata;
+
+	dptxrd(it6505, 0xad, &rddata);
+	dptxrd(it6505, 0xae, &rddata1);
+	vdes = ((rddata1 & 0x0F) << 8) + rddata;
+
+	dptxrd(it6505, 0xaf, &rddata);
+	dptxrd(it6505, 0xb0, &rddata1);
+	vdew = ((rddata1 & 0x0F) << 8) + rddata;
+
+	dptxrd(it6505, 0xb1, &rddata);
+	dptxrd(it6505, 0xb2, &rddata1);
+	vfph = ((rddata1 & 0x0F) << 8) + rddata;
+
+	dptxrd(it6505, 0xb3, &rddata);
+	dptxrd(it6505, 0xb4, &rddata1);
+	vsyncw = ((rddata1 & 0x0F) << 8) + rddata;
+
+	sum = 0;
+	for (i = 0; i < 100; i++) {
+		dptxset(it6505, 0x12, 0x80, 0x80);
+		usleep_range(10000, 15000);
+		dptxset(it6505, 0x12, 0x80, 0x00);
+
+		dptxrd(it6505, 0x13, &rddata);
+		dptxrd(it6505, 0x14, &rddata1);
+		rddata = ((rddata1 & 0x0F) << 8) + rddata;
+
+		sum += rddata;
+	}
+
+	sum /= 100;
+	pclk = 13500 * 2048 / sum;
+	it6505->vid_info.clock = pclk;
+	it6505->vid_info.hdisplay = hdew;
+	it6505->vid_info.hsync_start = hdew + hfph;
+	it6505->vid_info.hsync_end = hdew + hfph + hsyncw;
+	it6505->vid_info.htotal = htotal;
+	it6505->vid_info.vdisplay = vdew;
+	it6505->vid_info.vsync_start = vdew + vfph;
+	it6505->vid_info.vsync_end = vdew + vfph + vsyncw;
+	it6505->vid_info.vtotal = vtotal;
+	it6505->vid_info.vrefresh = pclk / htotal / vtotal;
+
+	DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, DRM_MODE_FMT,
+			     DRM_MODE_ARG(&it6505->vid_info));
+}
+
+static void show_aud_mcnt(struct it6505 *it6505)
+{
+	unsigned int audn, regde, regdf, rege0, vclk, aclk, audmcal, audmcnt;
+	struct device *dev = &it6505->client->dev;
+
+	dptxrd(it6505, 0xde, &regde);
+	dptxrd(it6505, 0xdf, &regdf);
+	dptxrd(it6505, 0xe0, &rege0);
+	audn = regde + (regdf << 8) + (rege0 << 16);
+	if (it6505->hbr)
+		vclk = 2700000;
+	else
+		vclk = 1620000;
+	switch (it6505->aud_fs) {
+	case AUD32K:
+		aclk = 320;
+		break;
+	case AUD48K:
+		aclk = 480;
+		break;
+	case AUD96K:
+		aclk = 960;
+		break;
+	case AUD192K:
+		aclk = 1920;
+		break;
+	case AUD44P1K:
+		aclk = 441;
+	case AUD88P2K:
+		aclk = 882;
+		break;
+	case AUD176P4K:
+		aclk = 1764;
+		break;
+	default:
+		break;
+	}
+	audmcal = audn * aclk / vclk * 512;
+	dptxrd(it6505, 0xe4, &regde);
+	dptxrd(it6505, 0xe5, &regdf);
+	dptxrd(it6505, 0xe6, &rege0);
+	audmcnt = (rege0 << 16) + (regdf << 8) + regde;
+	DRM_DEV_DEBUG_DRIVER(dev, "audio N:0x%06x", audn);
+	DRM_DEV_DEBUG_DRIVER(dev, "audio Mcal:0x%06x", audmcal);
+	DRM_DEV_DEBUG_DRIVER(dev, "audio Mcnt:0x%06x", audmcnt);
+}
+
+static const char *const state_string[] = {
+	[SYS_UNPLUG] = "SYS_UNPLUG",
+	[SYS_HPD] = "SYS_HPD",
+	[SYS_AUTOTRAIN] = "SYS_AUTOTRAIN",
+	[SYS_WAIT] = "SYS_WAIT",
+	[SYS_TRAINFAIL] = "SYS_TRAINFAIL",
+	[SYS_ReHDCP] = "SYS_ReHDCP",
+	[SYS_NOROP] = "SYS_NOROP",
+	[SYS_Unknown] = "SYS_Unknown",
+};
+
+static void dptx_sys_chg(struct it6505 *it6505, enum sys_status newstate)
+{
+	int i = 0;
+	struct device *dev = &it6505->client->dev;
+
+	if (newstate != SYS_UNPLUG) {
+		if (!dptx_getsinkhpd(it6505))
+			newstate = SYS_UNPLUG;
+	}
+	if (it6505->status == newstate)
+		return;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "sys_state change: %s -> %s",
+			     state_string[it6505->status],
+			     state_string[newstate]);
+	it6505->status = newstate;
+
+	switch (it6505->status) {
+	case SYS_UNPLUG:
+		kfree(it6505->edid);
+		it6505->edid = NULL;
+		DRM_DEV_DEBUG_DRIVER(dev, "Free it6505 EDID memory");
+		it6505_init_and_mask_on(it6505);
+		it6505_termination_off(it6505);
+		break;
+	case SYS_HPD:
+		it6505_termination_on(it6505);
+		break;
+	case SYS_AUTOTRAIN:
+		break;
+	case SYS_WAIT:
+		break;
+	case SYS_ReHDCP:
+		break;
+	case SYS_NOROP:
+		for (i = 0; i < SHOWVIDEOTIMING; i++)
+			show_vid_info(it6505);
+		break;
+	case SYS_TRAINFAIL:
+		/* it6505 goes to idle */
+		break;
+	default:
+		break;
+	}
+}
+
+static bool it6505_aux_op_finished(struct it6505 *it6505)
+{
+	unsigned int value;
+	int err;
+
+	err = regmap_read(it6505->regmap, 0x2b, &value);
+	if (err < 0)
+		return false;
+
+	return (value & BIT(5)) == 0;
+}
+
+static int dptx_auxwait(struct it6505 *it6505)
+{
+	unsigned int status;
+	unsigned long timeout;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
+
+	while (!it6505_aux_op_finished(it6505)) {
+		if (time_after(jiffies, timeout)) {
+			DRM_DEV_ERROR(dev, "Timed out waiting AUX to finish");
+			return -ETIMEDOUT;
+		}
+		usleep_range(1000, 2000);
+	}
+
+	err = dptxrd(it6505, 0x9f, &status);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to read AUX channel: %d", err);
+		return err;
+	}
+
+	if (status & 0x03) {
+		DRM_DEV_ERROR(dev, "Failed to wait for AUX (status: 0x%x)",
+			      status);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+enum aux_cmd_type {
+	CMD_AUX_NATIVE_READ = 0x0,
+	CMD_AUX_NATIVE_WRITE = 0x5,
+	CMD_AUX_I2C_EDID_READ = 0xB,
+};
+
+enum aux_cmd_reply {
+	REPLY_ACK,
+	REPLY_NACK,
+	REPLY_DEFER,
+};
+
+static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
+				      enum aux_cmd_type cmd,
+				      unsigned int address, u8 *buffer,
+				      size_t size, enum aux_cmd_reply *reply)
+{
+	int ret;
+	int i;
+	int status, val;
+
+	if (cmd == CMD_AUX_I2C_EDID_READ) {
+		/* DP AUX EDID FIFO has maximum length of 16 bytes. */
+		size = min_t(size_t, size, AUX_FIFO_MAX_SIZE);
+		/* Enable AUX FIFO read back and clear FIFO */
+		dptxwr(it6505, 0x23, 0xC3);
+		dptxwr(it6505, 0x23, 0xC2);
+	} else {
+		/* The DP AUX transmit buffer has 4 bytes. */
+		size = min_t(size_t, size, 4);
+		dptxwr(it6505, 0x23, 0x42);
+	}
+
+	/* Start Address[7:0] */
+	dptxwr(it6505, 0x24, (address >> 0) & 0xFF);
+	/* Start Address[15:8] */
+	dptxwr(it6505, 0x25, (address >> 8) & 0xFF);
+	/* WriteNum[3:0]+StartAdr[19:16] */
+	dptxwr(it6505, 0x26, ((address >> 16) & 0x0F) | ((size - 1) << 4));
+
+	if (cmd == CMD_AUX_NATIVE_WRITE)
+		regmap_bulk_write(it6505->regmap, 0x27, buffer, size);
+
+	/* Aux Fire */
+	dptxwr(it6505, 0x2B, cmd);
+
+	ret = dptx_auxwait(it6505);
+	if (ret < 0)
+		return ret;
+
+	ret = dptxrd(it6505, 0x9F, &status);
+	if (ret < 0)
+		return ret;
+
+	switch ((status >> 6) & 0x3) {
+	case 0:
+		*reply = REPLY_ACK;
+		break;
+	case 1:
+		*reply = REPLY_NACK;
+		return 0;
+	case 2:
+		*reply = REPLY_DEFER;
+		return 0;
+	case 3:
+		return -ETIMEDOUT;
+	}
+
+	if (cmd == CMD_AUX_NATIVE_WRITE)
+		goto out;
+
+	if (cmd == CMD_AUX_I2C_EDID_READ) {
+		for (i = 0; i < size; i++) {
+			ret = dptxrd(it6505, 0x2F, &val);
+			if (ret < 0)
+				return ret;
+			buffer[i] = val;
+		}
+	} else {
+		for (i = 0; i < size; i++) {
+			ret = dptxrd(it6505, 0x2C + i, &val);
+			if (ret < 0)
+				return ret;
+			buffer[size - 1 - i] = val;
+		}
+	}
+
+out:
+	dptxwr(it6505, 0x23, 0x40);
+	return size;
+}
+
+static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct it6505 *it6505 = container_of(aux, struct it6505, aux);
+	u8 cmd;
+	bool is_i2c = !(msg->request & DP_AUX_NATIVE_WRITE);
+	int ret;
+	enum aux_cmd_reply reply;
+
+	/* IT6505 doesn't support arbitrary I2C read / write. */
+	if (is_i2c)
+		return -EINVAL;
+
+	switch (msg->request) {
+	case DP_AUX_NATIVE_READ:
+		cmd = CMD_AUX_NATIVE_READ;
+		break;
+	case DP_AUX_NATIVE_WRITE:
+		cmd = CMD_AUX_NATIVE_WRITE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = it6505_aux_do_transfer(it6505, cmd, msg->address, msg->buffer,
+				     msg->size, &reply);
+	if (ret < 0)
+		return ret;
+
+	switch (reply) {
+	case REPLY_ACK:
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+		break;
+	case REPLY_NACK:
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+		break;
+	case REPLY_DEFER:
+		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+		break;
+	}
+
+	return ret;
+}
+
+static int dptx_get_edidblock(void *data, u8 *buf, unsigned int blockno,
+			      size_t len)
+{
+	struct it6505 *it6505 = data;
+	int offset, ret;
+	struct device *dev = &it6505->client->dev;
+	enum aux_cmd_reply reply;
+
+	dptxset(it6505, 0x05, 0x08, 0x08);
+	dptxset(it6505, 0x05, 0x08, 0x00);
+	DRM_DEV_DEBUG_DRIVER(dev, "blocknum = %d", blockno);
+
+	for (offset = 0; offset < EDID_LENGTH; offset += 8) {
+		ret = it6505_aux_do_transfer(it6505, CMD_AUX_I2C_EDID_READ,
+					     blockno * EDID_LENGTH + offset,
+					     buf + offset, 8, &reply);
+		if (ret < 0)
+			return ret;
+		if (reply != REPLY_ACK)
+			return -EIO;
+
+		DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x]: %8ph", offset,
+				     buf + offset);
+	}
+
+	return 0;
+}
+
+static int it6505_get_modes(struct drm_connector *connector)
+{
+	struct it6505 *it6505 = connector_to_it6505(connector);
+	int err, num_modes = 0;
+	struct device *dev = &it6505->client->dev;
+
+	it6505->train_fail_hpd = TRAINFAILHPD;
+	if (it6505->edid)
+		return drm_add_edid_modes(connector, it6505->edid);
+	mutex_lock(&it6505->mode_lock);
+
+	it6505_dump(it6505);
+	dptx_debug_print(it6505, 0x9F, "aux status");
+	it6505->edid =
+		drm_do_get_edid(&it6505->connector, dptx_get_edidblock, it6505);
+	if (!it6505->edid) {
+		DRM_DEV_ERROR(dev, "Failed to read EDID");
+		goto unlock;
+	}
+
+	err = drm_connector_update_edid_property(connector, it6505->edid);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to update EDID property: %d", err);
+		goto unlock;
+	}
+
+	num_modes = drm_add_edid_modes(connector, it6505->edid);
+
+unlock:
+	mutex_unlock(&it6505->mode_lock);
+
+	return num_modes;
+}
+
+static const struct drm_connector_helper_funcs it6505_connector_helper_funcs = {
+	.get_modes = it6505_get_modes,
+};
+
+static enum drm_connector_status it6505_detect(struct drm_connector *connector,
+					       bool force)
+{
+	struct it6505 *it6505 = connector_to_it6505(connector);
+
+	if (gpiod_get_value(it6505->pdata.gpiod_hpd))
+		return connector_status_disconnected;
+
+	return connector_status_connected;
+}
+
+static const struct drm_connector_funcs it6505_connector_funcs = {
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.detect = it6505_detect,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static int it6505_extcon_notifier(struct notifier_block *self,
+				  unsigned long event, void *ptr)
+{
+	struct it6505 *it6505 = container_of(self, struct it6505, event_nb);
+
+	schedule_work(&it6505->extcon_wq);
+	return NOTIFY_DONE;
+}
+
+static void it6505_extcon_work(struct work_struct *work)
+{
+	struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq);
+	int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP);
+	unsigned int pwroffretry = 0;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->drv_hold)
+		return;
+	mutex_lock(&it6505->lock);
+	DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state);
+	if (state > 0) {
+		DRM_DEV_DEBUG_DRIVER(dev, "start to power on");
+		msleep(1000);
+		it6505_poweron(it6505);
+		if (!dptx_getsinkhpd(it6505))
+			it6505_termination_off(it6505);
+	} else {
+		if (it6505->en_pwronoff) {
+			DRM_DEV_DEBUG_DRIVER(dev, "start to power off");
+			while (it6505_poweroff(it6505) &&
+			       pwroffretry++ < PWROFFRETRYTIME) {
+				DRM_DEV_DEBUG_DRIVER(dev,
+						     "power off fail %d times",
+						     pwroffretry);
+			}
+			drm_helper_hpd_irq_event(it6505->connector.dev);
+			DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!");
+		}
+	}
+	mutex_unlock(&it6505->lock);
+}
+
+static int it6505_use_notifier_module(struct it6505 *it6505)
+{
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	it6505->event_nb.notifier_call = it6505_extcon_notifier;
+	INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
+	ret = devm_extcon_register_notifier(&it6505->client->dev,
+					    it6505->extcon, EXTCON_DISP_DP,
+					    &it6505->event_nb);
+	if (ret) {
+		DRM_DEV_ERROR(dev, "failed to register notifier for DP");
+		return ret;
+	}
+	return 0;
+}
+
+static int it6505_bridge_attach(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+	struct device *dev;
+	int err;
+
+	dev = &it6505->client->dev;
+	if (!bridge->encoder) {
+		DRM_DEV_ERROR(dev, "Parent encoder object not found");
+		return -ENODEV;
+	}
+
+	err = drm_connector_init(bridge->dev, &it6505->connector,
+				 &it6505_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to initialize connector: %d", err);
+		return err;
+	}
+
+	drm_connector_helper_add(&it6505->connector,
+				 &it6505_connector_helper_funcs);
+
+	it6505->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+	err = drm_connector_attach_encoder(&it6505->connector, bridge->encoder);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to link up connector to encoder: %d",
+			      err);
+		return err;
+	}
+
+	err = drm_connector_register(&it6505->connector);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to register connector: %d", err);
+		return err;
+	}
+
+	err = it6505_use_notifier_module(it6505);
+	if (err < 0) {
+		drm_connector_unregister(&it6505->connector);
+		return err;
+	}
+	schedule_work(&it6505->extcon_wq);
+
+	return 0;
+}
+
+static void it6505_bridge_detach(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	devm_extcon_unregister_notifier(&it6505->client->dev, it6505->extcon,
+					EXTCON_DISP_DP, &it6505->event_nb);
+	flush_work(&it6505->extcon_wq);
+	drm_connector_unregister(&it6505->connector);
+}
+
+static enum drm_mode_status
+it6505_bridge_mode_valid(struct drm_bridge *bridge,
+			 const struct drm_display_mode *mode)
+{
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		return MODE_NO_INTERLACE;
+
+	/* Max 1200p at 5.4 Ghz, one lane */
+	if (mode->clock > MAXPCLK)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static int it6505_send_video_infoframe(struct it6505 *it6505,
+				       struct hdmi_avi_infoframe *frame)
+{
+	u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer));
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to pack AVI infoframe: %d\n", err);
+		return err;
+	}
+
+	err = dptxset(it6505, 0xe8, 0x01, 0x00);
+	if (err)
+		return err;
+
+	err = regmap_bulk_write(it6505->regmap, 0xe9,
+				buffer + HDMI_INFOFRAME_HEADER_SIZE,
+				frame->length);
+	if (err)
+		return err;
+
+	err = dptxset(it6505, 0xe8, 0x01, 0x01);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static void it6505_bridge_mode_set(struct drm_bridge *bridge,
+				   struct drm_display_mode *mode,
+				   struct drm_display_mode *adjusted_mode)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+	struct hdmi_avi_infoframe frame;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	mutex_lock(&it6505->mode_lock);
+	err = drm_hdmi_avi_infoframe_from_display_mode(&frame, adjusted_mode,
+						       false);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to setup AVI infoframe: %d", err);
+		goto unlock;
+	}
+	it6505->vid_info.base.id = adjusted_mode->base.id;
+	strlcpy(it6505->vid_info.name, adjusted_mode->name,
+		DRM_DISPLAY_MODE_LEN);
+	it6505->vid_info.type = adjusted_mode->type;
+	it6505->vid_info.flags = adjusted_mode->flags;
+	err = it6505_send_video_infoframe(it6505, &frame);
+	if (err)
+		DRM_DEV_ERROR(dev, "Failed to send AVI infoframe: %d", err);
+
+unlock:
+	mutex_unlock(&it6505->mode_lock);
+}
+
+static void dptx_set_aud_fmt(struct it6505 *it6505)
+{
+	unsigned int audsrc;
+	/* I2S MODE */
+	dptxwr(it6505, 0xB9,
+	       (it6505->audwordlength << 5) | (it6505->i2s_data_seq << 4) |
+		       (it6505->i2s_ws_channel << 3) |
+		       (it6505->i2s_data_delay << 2) |
+		       (it6505->i2s_justified << 1) | it6505->i2s_input_fmt);
+	if (it6505->aud_sel == SPDIF) {
+		dptxwr(it6505, 0xBA, 0x00);
+		/* 0x30 = 128*FS */
+		dptxset(it6505, 0x11, 0xF0, 0x30);
+	} else {
+		dptxwr(it6505, 0xBA, 0xe4);
+	}
+
+	dptxwr(it6505, 0xBB, 0x20);
+	dptxwr(it6505, 0xBC, 0x00);
+	audsrc = 1;
+
+	if (it6505->aud_ch > 2)
+		audsrc |= 2;
+
+	if (it6505->aud_ch > 4)
+		audsrc |= 4;
+
+	if (it6505->aud_ch > 6)
+		audsrc |= 8;
+
+	audsrc |= it6505->aud_sel << 4;
+
+	dptxwr(it6505, 0xB8, audsrc);
+}
+
+static void dptx_set_aud_chsts(struct it6505 *it6505)
+{
+	enum it6505_aud_fs audfs = it6505->aud_fs;
+
+	/* Channel Status */
+	dptxwr(it6505, 0xBF, it6505->aud_type << 1);
+	dptxwr(it6505, 0xC0, 0x00);
+	dptxwr(it6505, 0xC1, 0x00);
+	dptxwr(it6505, 0xC2, audfs);
+	switch (it6505->audwordlength) {
+	case AUD16BIT:
+		dptxwr(it6505, 0xC3, (~audfs << 4) + 0x02);
+		break;
+
+	case AUD18BIT:
+		dptxwr(it6505, 0xC3, (~audfs << 4) + 0x04);
+		break;
+
+	case AUD20BIT:
+		dptxwr(it6505, 0xC3, (~audfs << 4) + 0x03);
+		break;
+
+	case AUD24BIT:
+		dptxwr(it6505, 0xC3, (~audfs << 4) + 0x0B);
+		break;
+	}
+}
+
+static void dptx_set_audio_infoframe(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+
+	dptxwr(it6505, 0xf7, it6505->aud_ch - 1);
+
+	switch (it6505->aud_ch) {
+	case 2:
+		dptxwr(it6505, 0xF9, 0x00);
+		break;
+	case 3:
+		dptxwr(it6505, 0xF9, 0x01);
+		break;
+	case 4:
+		dptxwr(it6505, 0xF9, 0x03);
+		break;
+	case 5:
+		dptxwr(it6505, 0xF9, 0x07);
+		break;
+	case 6:
+		dptxwr(it6505, 0xF9, 0x0B);
+		break;
+	case 7:
+		dptxwr(it6505, 0xF9, 0x0F);
+		break;
+	case 8:
+		dptxwr(it6505, 0xF9, 0x1F);
+		break;
+	default:
+		DRM_DEV_ERROR(dev, "audio channel number error: %u",
+			      it6505->aud_ch);
+	}
+	/* Enable Audio InfoFrame */
+	dptxset(it6505, 0xE8, 0x22, 0x22);
+}
+
+static void it6505_set_audio(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+	unsigned int regbe;
+
+	/* Audio Clock Domain Reset */
+	dptxset(it6505, 0x05, 0x02, 0x02);
+	/* Audio mute */
+	dptxset(it6505, 0xD3, 0x20, 0x20);
+	/* Release Audio Clock Domain Reset */
+	dptxset(it6505, 0x05, 0x02, 0x00);
+
+	dptxrd(it6505, 0xBE, &regbe);
+	if (regbe == 0xFF)
+		DRM_DEV_DEBUG_DRIVER(dev, "no audio input");
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "audio input fs: %d.%d kHz",
+				     6750 / regbe, 67500 % regbe);
+	dptx_set_aud_fmt(it6505);
+	dptx_set_aud_chsts(it6505);
+	dptx_set_audio_infoframe(it6505);
+
+	/* Enable Enhanced Audio TimeStmp Mode */
+	dptxset(it6505, 0xD4, 0x04, 0x04);
+	/* Disable Full Audio Packet */
+	dptxset(it6505, 0xBB, 0x10, 0x00);
+
+	dptxwr(it6505, 0xDE, 0x00);
+	dptxwr(it6505, 0xDF, 0x80);
+	dptxwr(it6505, 0xE0, 0x00);
+	dptxset(it6505, 0xB8, 0x80, 0x80);
+	dptxset(it6505, 0xB8, 0x80, 0x00);
+	dptxset(it6505, 0xD3, 0x20, 0x00);
+}
+
+/***************************************************************************
+ * DPCD Read and EDID
+ ***************************************************************************/
+
+static unsigned int dptx_dpcdrd(struct it6505 *it6505, unsigned long offset)
+{
+	u8 value;
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	ret = drm_dp_dpcd_readb(&it6505->aux, offset, &value);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "DPCD read failed [0x%x] ret: %d", offset,
+			      ret);
+		return 0;
+	}
+	return value;
+}
+
+static int dptx_dpcdwr(struct it6505 *it6505, unsigned long offset,
+		       unsigned long datain)
+{
+	int ret;
+	struct device *dev = &it6505->client->dev;
+
+	ret = drm_dp_dpcd_writeb(&it6505->aux, offset, datain);
+	if (ret < 0) {
+		DRM_DEV_ERROR(dev, "DPCD write failed [0x%x] ret: %d", offset,
+			      ret);
+		return ret;
+	}
+	return 0;
+}
+
+static int it6505_get_dpcd(struct it6505 *it6505, int offset, u8 *dpcd, int num)
+{
+	int i, ret;
+	struct device *dev = &it6505->client->dev;
+
+	for (i = 0; i < num; i += 4) {
+		ret = drm_dp_dpcd_read(&it6505->aux, offset + i, dpcd + i,
+				       min(num - i, 4));
+		if (ret < 0)
+			return ret;
+	}
+
+	DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x%x] = %*ph", offset, num, dpcd);
+	return 0;
+}
+
+static void it6505_parse_dpcd(struct it6505 *it6505)
+{
+	u8 dpcd[DP_RECEIVER_CAP_SIZE];
+	struct device *dev = &it6505->client->dev;
+	int bcaps;
+	struct drm_dp_link *link = &it6505->link;
+
+	drm_dp_link_probe(&it6505->aux, link);
+	it6505_get_dpcd(it6505, DP_DPCD_REV, dpcd, ARRAY_SIZE(dpcd));
+
+	DRM_DEV_DEBUG_DRIVER(dev, "#########DPCD Rev.: %d.%d###########",
+			     link->revision >> 4, link->revision & 0x0F);
+
+	switch (link->rate) {
+	case 162000:
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Link Rate: 1.62Gbps per lane");
+		if (it6505->hbr) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Not support HBR Mode, will train LBR");
+			it6505->hbr = false;
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Training LBR");
+		}
+		break;
+
+	case 270000:
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Link Rate: 2.7Gbps per lane");
+		if (!it6505->hbr) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Support HBR Mode, will train LBR");
+			it6505->hbr = false;
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Training HBR");
+		}
+		break;
+
+	case 540000:
+		/* TODO(pihsun): Check if this is correct. HBR here? */
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Link Rate: 2.7Gbps per lane");
+		break;
+
+	default:
+		DRM_DEV_ERROR(dev, "Unknown Maximum Link Rate: %u",
+			      link->rate);
+		break;
+	}
+
+	if (link->num_lanes == 1 || link->num_lanes == 2 ||
+	    link->num_lanes == 4) {
+		DRM_DEV_DEBUG_DRIVER(
+			dev, "Lane Count: %u lane", link->num_lanes);
+		if (link->num_lanes > LANE)
+			link->num_lanes = LANE;
+		DRM_DEV_DEBUG_DRIVER(dev, "Training %u lane", link->num_lanes);
+	} else {
+		DRM_DEV_ERROR(dev, "Lane Count Error: %u", link->num_lanes);
+	}
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		DRM_DEV_DEBUG_DRIVER(dev, "Support Enhanced Framing");
+	else
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Can not support Enhanced Framing Mode");
+
+	if (dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5) {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Down-Spread: 0.5, support SSC!");
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "Maximum Down-Spread: 0, No support SSC!");
+		it6505->en_ssc = 0;
+	}
+
+	if (link->revision >= 0x11 &&
+	    dpcd[DP_MAX_DOWNSPREAD] & DP_NO_AUX_HANDSHAKE_LINK_TRAINING)
+		DRM_DEV_DEBUG_DRIVER(dev, "Support No AUX Training");
+	else
+		DRM_DEV_DEBUG_DRIVER(dev, "Can not support No AUX Training");
+
+	bcaps = dptx_dpcdrd(it6505, DP_AUX_HDCP_BCAPS);
+	if (bcaps & DP_BCAPS_HDCP_CAPABLE) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Sink support HDCP!");
+		it6505->cp_capable = true;
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Sink not support HDCP!");
+		it6505->cp_capable = false;
+		it6505->en_hdcp = 0;
+	}
+
+	if (bcaps & DP_BCAPS_REPEATER_PRESENT) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Downstream is repeater!!");
+		it6505->downstream_repeater = true;
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Downstream is receiver!!");
+		it6505->downstream_repeater = false;
+	}
+}
+
+static void it6505_enable_hdcp(struct it6505 *it6505)
+{
+	u8 bksvs[5], c;
+	struct device *dev = &it6505->client->dev;
+
+	/* Disable CP_Desired */
+	dptxset(it6505, 0x38, 0x0B, 0x00);
+	dptxset(it6505, 0x05, 0x10, 0x10);
+
+	usleep_range(1000, 1500);
+	c = dptx_dpcdrd(it6505, DP_AUX_HDCP_BCAPS);
+	DRM_DEV_DEBUG_DRIVER(dev, "DPCD[0x68028]: 0x%x\n", c);
+	if (!c)
+		return;
+
+	dptxset(it6505, 0x05, 0x10, 0x00);
+	/* Disable CP_Desired */
+	dptxset(it6505, 0x38, 0x01, 0x00);
+	/* Use R0' 100ms waiting */
+	dptxset(it6505, 0x38, 0x08, 0x00);
+	/* clear the repeater List Chk Done and fail bit */
+	dptxset(it6505, 0x39, 0x30, 0x00);
+
+	it6505_get_dpcd(it6505, DP_AUX_HDCP_BKSV, bksvs, ARRAY_SIZE(bksvs));
+
+	DRM_DEV_DEBUG_DRIVER(dev, "Sink BKSV = %5ph", bksvs);
+
+	/* Select An Generator */
+	dptxset(it6505, 0x3A, 0x01, 0x01);
+	/* Enable An Generator */
+	dptxset(it6505, 0x3A, 0x02, 0x02);
+	/* delay1ms(10);*/
+	usleep_range(10000, 15000);
+	/* Stop An Generator */
+	dptxset(it6505, 0x3A, 0x02, 0x00);
+
+	dptxset(it6505, 0x38, 0x01, 0x01);
+	dptxset(it6505, 0x39, 0x01, 0x01);
+}
+
+static void it6505_lanespeed_setup(struct it6505 *it6505)
+{
+	if (!it6505->hbr) {
+		dptxset(it6505, 0x16, 0x01, 0x01);
+		dptxset(it6505, 0x5C, 0x02, 0x00);
+	} else {
+		dptxset(it6505, 0x16, 0x01, 0x00);
+		dptxset(it6505, 0x5C, 0x02, 0x02);
+	}
+}
+
+static void it6505_lane_swap(struct it6505 *it6505)
+{
+	int err;
+	union extcon_property_value property;
+	struct device *dev = &it6505->client->dev;
+
+	if (!it6505->laneswap_disabled) {
+		err = extcon_get_property(it6505->extcon, EXTCON_DISP_DP,
+					  EXTCON_PROP_USB_TYPEC_POLARITY,
+					  &property);
+		if (err) {
+			DRM_DEV_ERROR(dev, "get property fail!");
+			return;
+		}
+		it6505->laneswap = property.intval;
+	}
+
+	dptxset(it6505, 0x16, 0x08, it6505->laneswap ? 0x08 : 0x00);
+	dptxset(it6505, 0x16, 0x06, (it6505->link.num_lanes - 1) << 1);
+	DRM_DEV_DEBUG_DRIVER(dev, "it6505->laneswap = 0x%x", it6505->laneswap);
+
+	if (it6505->laneswap) {
+		switch (it6505->link.num_lanes) {
+		case 1:
+			dptxset(it6505, 0x5C, 0xF1, 0x81);
+			break;
+		case 2:
+			dptxset(it6505, 0x5C, 0xF1, 0xC1);
+			break;
+		default:
+			dptxset(it6505, 0x5C, 0xF1, 0xF1);
+			break;
+		}
+	} else {
+		switch (it6505->link.num_lanes) {
+		case 1:
+			dptxset(it6505, 0x5C, 0xF1, 0x11);
+			break;
+		case 2:
+			dptxset(it6505, 0x5C, 0xF1, 0x31);
+			break;
+		default:
+			dptxset(it6505, 0x5C, 0xF1, 0xF1);
+			break;
+		}
+	}
+}
+
+static void it6505_lane_config(struct it6505 *it6505)
+{
+	it6505_lanespeed_setup(it6505);
+	it6505_lane_swap(it6505);
+}
+
+static void it6505_set_ssc(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+
+	dptxset(it6505, 0x16, 0x10, it6505->en_ssc << 4);
+	if (it6505->en_ssc) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Enable 27M 4463 PPM SSC");
+		dptx_chgbank(it6505, 1);
+		dptxwr(it6505, 0x88, 0x9e);
+		dptxwr(it6505, 0x89, 0x1c);
+		dptxwr(it6505, 0x8A, 0x42);
+		dptx_chgbank(it6505, 0);
+		dptxwr(it6505, 0x58, 0x07);
+		dptxwr(it6505, 0x59, 0x29);
+		dptxwr(it6505, 0x5A, 0x03);
+		/* Stamp Interrupt Step */
+		dptxset(it6505, 0xD4, 0x30, 0x10);
+		dptx_dpcdwr(it6505, DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
+	} else {
+		dptx_dpcdwr(it6505, DP_DOWNSPREAD_CTRL, 0x00);
+		dptxset(it6505, 0xD4, 0x30, 0x00);
+	}
+}
+
+static void pclk_phase(struct it6505 *it6505)
+{
+	dptxset(it6505, 0x10, 0x03, PCLK_DELAY);
+	dptxset(it6505, 0x12, 0x10, PCLK_INV << 4);
+}
+
+static void afe_driving_setting(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+	unsigned int afe_setting;
+
+	afe_setting = AFE_SETTING;
+	if (afe_setting >= ARRAY_SIZE(afe_setting_table)) {
+		DRM_DEV_ERROR(dev, "afe setting value error and use default");
+		afe_setting = 0;
+	}
+	if (afe_setting) {
+		dptx_chgbank(it6505, 1);
+		dptxwr(it6505, 0x7E, afe_setting_table[afe_setting][0]);
+		dptxwr(it6505, 0x7F, afe_setting_table[afe_setting][1]);
+		dptxwr(it6505, 0x81, afe_setting_table[afe_setting][2]);
+		dptx_chgbank(it6505, 0);
+	}
+}
+
+static void dptx_output(struct it6505 *it6505)
+{
+	/* change bank 0 */
+	dptx_chgbank(it6505, 0);
+	dptxwr(it6505, 0x64, 0x10);
+	dptxwr(it6505, 0x65, 0x80);
+	dptxwr(it6505, 0x66, 0x10);
+	dptxwr(it6505, 0x67, 0x4F);
+	dptxwr(it6505, 0x68, 0x09);
+	dptxwr(it6505, 0x69, 0xBA);
+	dptxwr(it6505, 0x6A, 0x3B);
+	dptxwr(it6505, 0x6B, 0x4B);
+	dptxwr(it6505, 0x6C, 0x3E);
+	dptxwr(it6505, 0x6D, 0x4F);
+	dptxwr(it6505, 0x6E, 0x09);
+	dptxwr(it6505, 0x6F, 0x56);
+	dptxwr(it6505, 0x70, 0x0E);
+	dptxwr(it6505, 0x71, 0x00);
+	dptxwr(it6505, 0x72, 0x00);
+	dptxwr(it6505, 0x73, 0x4F);
+	dptxwr(it6505, 0x74, 0x09);
+	dptxwr(it6505, 0x75, 0x00);
+	dptxwr(it6505, 0x76, 0x00);
+	dptxwr(it6505, 0x77, 0xE7);
+	dptxwr(it6505, 0x78, 0x10);
+	dptxwr(it6505, 0xE8, 0x00);
+	dptxset(it6505, 0xCE, 0x70, 0x60);
+	dptxwr(it6505, 0xCA, 0x4D);
+	dptxwr(it6505, 0xC9, 0xF5);
+	dptxwr(it6505, 0x5C, 0x02);
+
+	drm_dp_link_power_up(&it6505->aux, &it6505->link);
+	dptxset(it6505, 0x59, 0x01, 0x01);
+	dptxset(it6505, 0x5A, 0x05, 0x01);
+	dptxwr(it6505, 0x12, 0x01);
+	dptxwr(it6505, 0xCB, 0x17);
+	dptxwr(it6505, 0x11, 0x09);
+	dptxwr(it6505, 0x20, 0x28);
+	dptxset(it6505, 0x23, 0x30, 0x00);
+	dptxset(it6505, 0x3A, 0x04, 0x04);
+	dptxset(it6505, 0x15, 0x01, 0x01);
+	dptxwr(it6505, 0x0C, 0x08);
+
+	dptxset(it6505, 0x5F, 0x20, 0x00);
+	it6505_lane_config(it6505);
+
+	it6505_set_ssc(it6505);
+
+	if (it6505->link.capabilities & DP_LINK_CAP_ENHANCED_FRAMING) {
+		dptxwr(it6505, 0xD3, 0x33);
+		dptx_dpcdwr(it6505, DP_LANE_COUNT_SET,
+			    DP_LANE_COUNT_ENHANCED_FRAME_EN);
+	} else {
+		dptxwr(it6505, 0xD3, 0x32);
+	}
+
+	dptxset(it6505, 0x15, 0x02, 0x02);
+	dptxset(it6505, 0x15, 0x02, 0x00);
+	dptxset(it6505, 0x05, 0x03, 0x02);
+	dptxset(it6505, 0x05, 0x03, 0x00);
+
+	/* reg60[2] = InDDR */
+	dptxwr(it6505, 0x60, 0x44);
+	/* M444B24 format */
+	dptxwr(it6505, 0x62, 0x01);
+	/* select RGB Bypass CSC */
+	dptxwr(it6505, 0x63, 0x00);
+
+	pclk_phase(it6505);
+	dptxset(it6505, 0x61, 0x01, 0x01);
+	dptxwr(it6505, 0x06, 0xFF);
+	dptxwr(it6505, 0x07, 0xFF);
+	dptxwr(it6505, 0x08, 0xFF);
+
+	dptxset(it6505, 0xd3, 0x30, 0x00);
+	dptxset(it6505, 0xd4, 0x41, 0x41);
+	dptxset(it6505, 0xe8, 0x11, 0x11);
+
+	afe_driving_setting(it6505);
+	dptxwr(it6505, 0x17, 0x04);
+	dptxwr(it6505, 0x17, 0x01);
+}
+
+static void dptx_process_sys_wait(struct it6505 *it6505)
+{
+	int reg0e;
+	struct device *dev = &it6505->client->dev;
+
+	dptxrd(it6505, 0x0E, &reg0e);
+	DRM_DEV_DEBUG_DRIVER(dev, "SYS_WAIT state reg0e=0x%02x", reg0e);
+
+	if (reg0e & BIT(4)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Auto Link Training Success...");
+		DRM_DEV_DEBUG_DRIVER(dev, "Link State: 0x%x", reg0e & 0x1F);
+		if (it6505->en_audio) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
+			it6505_set_audio(it6505);
+		}
+		DRM_DEV_DEBUG_DRIVER(dev, "it6505->VidStable_Done = %02x",
+				     it6505->vidstable_done);
+		if (it6505->cp_capable) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP");
+			if (it6505->en_hdcp) {
+				DRM_DEV_DEBUG_DRIVER(dev, "Enable HDCP");
+				dptx_sys_chg(it6505, SYS_ReHDCP);
+			} else {
+				DRM_DEV_DEBUG_DRIVER(dev, "Disable HDCP");
+				dptx_sys_chg(it6505, SYS_NOROP);
+			}
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Not support HDCP");
+			dptx_sys_chg(it6505, SYS_NOROP);
+		}
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Auto Link Training fail step %d",
+				     it6505->cntfsm);
+		dptx_debug_print(it6505, 0x0D, "system status");
+		if (it6505->cntfsm > 0) {
+			it6505->cntfsm--;
+			dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Auto Training Fail %d times",
+					     TRAINFAILCNT);
+			DRM_DEV_DEBUG_DRIVER(dev, "Sys change to SYS_HPD");
+			dptx_dpcdwr(it6505, DP_TRAINING_PATTERN_SET, 0x00);
+			if (it6505->train_fail_hpd > 0) {
+				it6505->train_fail_hpd--;
+				dptx_sys_chg(it6505, SYS_HPD);
+			} else {
+				dptx_sys_chg(it6505, SYS_TRAINFAIL);
+			}
+		}
+	}
+}
+
+static void dptx_sys_fsm(struct it6505 *it6505)
+{
+	unsigned int i;
+	int reg0e;
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->status != SYS_UNPLUG && !dptx_getsinkhpd(it6505))
+		dptx_sys_chg(it6505, SYS_UNPLUG);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "state: %s", state_string[it6505->status]);
+	switch (it6505->status) {
+	case SYS_UNPLUG:
+		drm_helper_hpd_irq_event(it6505->connector.dev);
+		break;
+
+	case SYS_HPD:
+		dptx_debug_print(it6505, 0x9F, "aux status");
+		drm_helper_hpd_irq_event(it6505->connector.dev);
+		it6505_init_fsm(it6505);
+		/* GETDPCD */
+		it6505_parse_dpcd(it6505);
+
+		/*
+		 * training fail TRAINFAILCNT times,
+		 * then change to HPD to restart
+		 */
+		it6505->cntfsm = TRAINFAILCNT;
+		DRM_DEV_DEBUG_DRIVER(dev, "will Train %s %d lanes",
+				     it6505->hbr ? "HBR" : "LBR",
+				     it6505->link.num_lanes);
+		dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		break;
+
+	case SYS_AUTOTRAIN:
+		dptx_output(it6505);
+
+		/*
+		 * waiting for training down flag
+		 * because we don't know
+		 * how long this step will be completed
+		 * so use step 1ms to wait
+		 */
+		for (i = 0; i < FLAGTRAINDOWN; i++) {
+			usleep_range(1000, 2000);
+			dptxrd(it6505, 0x0E, &reg0e);
+			if (reg0e & BIT(4))
+				break;
+		}
+
+		dptx_sys_chg(it6505, SYS_WAIT);
+		break;
+
+	case SYS_WAIT:
+		dptx_process_sys_wait(it6505);
+		break;
+
+	case SYS_ReHDCP:
+		msleep(2400);
+		dptx_debug_print(it6505, 0x3B, "ar0_low");
+		dptx_debug_print(it6505, 0x3C, "ar0_high");
+		dptx_debug_print(it6505, 0x45, "br0_low");
+		dptx_debug_print(it6505, 0x46, "br0_high");
+		it6505_enable_hdcp(it6505);
+		DRM_DEV_DEBUG_DRIVER(dev, "SYS_ReHDCP end");
+		break;
+
+	case SYS_NOROP:
+		break;
+
+	case SYS_TRAINFAIL:
+		break;
+
+	default:
+		DRM_DEV_ERROR(dev, "sys_state change to unknown_state: %d",
+			      it6505->status);
+		break;
+	}
+}
+
+static int sha1_digest(struct it6505 *it6505, u8 *sha1_input, unsigned int size,
+		       u8 *output_av)
+{
+	struct shash_desc *desc;
+	struct crypto_shash *tfm;
+	int err;
+	struct device *dev = &it6505->client->dev;
+
+	tfm = crypto_alloc_shash("sha1", 0, 0);
+	if (IS_ERR(tfm)) {
+		DRM_DEV_ERROR(dev, "crypto_alloc_shash sha1 failed");
+		return PTR_ERR(tfm);
+	}
+	desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+	if (!desc) {
+		crypto_free_shash(tfm);
+		return -ENOMEM;
+	}
+
+	desc->tfm = tfm;
+	err = crypto_shash_digest(desc, sha1_input, size, output_av);
+	if (err)
+		DRM_DEV_ERROR(dev, "crypto_shash_digest sha1 failed");
+
+	crypto_free_shash(tfm);
+	kfree(desc);
+	return err;
+}
+
+static int it6505_makeup_sha1_input(struct it6505 *it6505)
+{
+	int msgcnt = 0, i;
+	unsigned int value;
+	u8 am0[8], binfo[2];
+	struct device *dev = &it6505->client->dev;
+
+	dptxset(it6505, 0x3A, 0x20, 0x20);
+	for (i = 0; i < 8; i++) {
+		dptxrd(it6505, 0x4C + i, &value);
+		am0[i] = value;
+	}
+	DRM_DEV_DEBUG_DRIVER(dev, "read am0 = %8ph", am0);
+	dptxset(it6505, 0x3A, 0x20, 0x00);
+
+	it6505_get_dpcd(it6505, DP_AUX_HDCP_BINFO, binfo, ARRAY_SIZE(binfo));
+	DRM_DEV_DEBUG_DRIVER(dev, "Attached devices: %02x", binfo[0] & 0x7F);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "%s 127 devices are attached",
+			     (binfo[0] & BIT(7)) ? "over" : "under");
+	DRM_DEV_DEBUG_DRIVER(dev, "depth, attached levels: %02x",
+			     binfo[1] & 0x07);
+	DRM_DEV_DEBUG_DRIVER(dev, "%s than seven levels cascaded",
+			     (binfo[1] & BIT(3)) ? "more" : "less");
+
+	for (i = 0; i < (binfo[0] & 0x7F); i++) {
+		it6505_get_dpcd(it6505, DP_AUX_HDCP_KSV_FIFO + (i % 3) * 5,
+				it6505->shainput + msgcnt, 5);
+		msgcnt += 5;
+
+		DRM_DEV_DEBUG_DRIVER(dev, "KSV List %d device : %5ph", i,
+				     it6505->shainput + i * 5);
+	}
+	it6505->shainput[msgcnt++] = binfo[0];
+	it6505->shainput[msgcnt++] = binfo[1];
+	for (i = 0; i < 8; i++)
+		it6505->shainput[msgcnt++] = am0[i];
+
+	DRM_DEV_DEBUG_DRIVER(dev, "SHA Message Count = %d", msgcnt);
+	return msgcnt;
+}
+
+static void it6505_check_sha1_result(struct it6505 *it6505)
+{
+	unsigned int i, shapass;
+	struct device *dev = &it6505->client->dev;
+
+	DRM_DEV_DEBUG_DRIVER(dev, "SHA calculate complete");
+	for (i = 0; i < 5; i++)
+		DRM_DEV_DEBUG_DRIVER(dev, "av %d: %4ph", i, it6505->av[i]);
+
+	shapass = 1;
+	for (i = 0; i < 5; i++) {
+		it6505_get_dpcd(it6505, DP_AUX_HDCP_V_PRIME(i), it6505->bv[i],
+				4);
+		DRM_DEV_DEBUG_DRIVER(dev, "bv %d: %4ph", i, it6505->bv[i]);
+		if ((it6505->bv[i][3] != it6505->av[i][0]) ||
+		    (it6505->bv[i][2] != it6505->av[i][1]) ||
+		    (it6505->bv[i][1] != it6505->av[i][2]) ||
+		    (it6505->bv[i][0] != it6505->av[i][3])) {
+			shapass = 0;
+		}
+	}
+	if (shapass) {
+		DRM_DEV_DEBUG_DRIVER(dev, "SHA check result pass!");
+		dptxset(it6505, 0x39, BIT(4), BIT(4));
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "SHA check result fail");
+		dptxset(it6505, 0x39, BIT(5), BIT(5));
+	}
+}
+
+static void to_fsm_status(struct it6505 *it6505, enum sys_status status)
+{
+	while (it6505->status != status && it6505->status != SYS_TRAINFAIL) {
+		dptx_sys_fsm(it6505);
+		if (it6505->status == SYS_UNPLUG)
+			return;
+	}
+}
+
+static void go_on_fsm(struct it6505 *it6505);
+
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+	return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static void hpd_irq(struct it6505 *it6505)
+{
+	u8 dpcd_sink_count, dpcd_irq_vector;
+	u8 link_status[DP_LINK_STATUS_SIZE];
+	unsigned int bstatus;
+	struct device *dev = &it6505->client->dev;
+
+	dpcd_sink_count = dptx_dpcdrd(it6505, DP_SINK_COUNT);
+	dpcd_irq_vector = dptx_dpcdrd(it6505, DP_DEVICE_SERVICE_IRQ_VECTOR);
+	drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count = 0x%x", dpcd_sink_count);
+	DRM_DEV_DEBUG_DRIVER(dev, "dpcd_irq_vector = 0x%x", dpcd_irq_vector);
+	DRM_DEV_DEBUG_DRIVER(dev, "link_status = %*ph", ARRAY_SIZE(link_status),
+			     link_status);
+
+	if (dpcd_irq_vector & DP_CP_IRQ) {
+		bstatus = dptx_dpcdrd(it6505, DP_AUX_HDCP_BSTATUS);
+		dptxset(it6505, 0x39, 0x02, 0x02);
+		DRM_DEV_DEBUG_DRIVER(dev, "Receive CP_IRQ, bstatus = 0x%02x",
+				     bstatus);
+		dptx_debug_print(it6505, 0x55, "");
+
+		if (bstatus & DP_BSTATUS_READY)
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP KSV list ready");
+
+		if (bstatus & DP_BSTATUS_LINK_FAILURE) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "Link Integrity Fail, restart HDCP");
+			return;
+		}
+		if (bstatus & DP_BSTATUS_R0_PRIME_READY)
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP R0' ready");
+	}
+
+	if (dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
+	    DP_LINK_STATUS_UPDATED) {
+		if (!drm_dp_channel_eq_ok(link_status,
+					  it6505->link.num_lanes)) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Link Re-Training");
+			dptxset(it6505, 0xD3, 0x30, 0x30);
+			dptxset(it6505, 0xE8, 0x33, 0x00);
+			msleep(500);
+			dptx_sys_chg(it6505, SYS_HPD);
+			go_on_fsm(it6505);
+		}
+	}
+}
+
+static void go_on_fsm(struct it6505 *it6505)
+{
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->cp_capable) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP, cp_capable: %d",
+				     it6505->cp_capable);
+		if (it6505->en_hdcp) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Enable HDCP");
+			to_fsm_status(it6505, SYS_ReHDCP);
+			dptx_sys_fsm(it6505);
+
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Disable HDCP");
+			to_fsm_status(it6505, SYS_NOROP);
+		}
+	} else {
+		DRM_DEV_DEBUG_DRIVER(dev, "Not support HDCP");
+		to_fsm_status(it6505, SYS_NOROP);
+	}
+}
+
+static void it6505_check_reg06(struct it6505 *it6505, unsigned int reg06)
+{
+	unsigned int rddata;
+	struct device *dev = &it6505->client->dev;
+
+	if (reg06 & BIT(0)) {
+		/* hpd pin status change */
+		DRM_DEV_DEBUG_DRIVER(dev, "HPD Change Interrupt");
+		if (dptx_getsinkhpd(it6505)) {
+			dptx_sys_chg(it6505, SYS_HPD);
+			dptx_sys_fsm(it6505);
+			dptxrd(it6505, 0x0D, &rddata);
+			if (rddata & BIT(2)) {
+				go_on_fsm(it6505);
+			} else {
+				dptxwr(it6505, 0x05, 0x00);
+				dptxset(it6505, 0x61, 0x02, 0x02);
+				dptxset(it6505, 0x61, 0x02, 0x00);
+			}
+		} else {
+			dptx_sys_chg(it6505, SYS_UNPLUG);
+			dptx_sys_fsm(it6505);
+			return;
+		}
+	}
+
+	if (it6505->cp_capable) {
+		if (reg06 & BIT(4)) {
+			DRM_DEV_DEBUG_DRIVER(dev,
+					     "HDCP encryption Done Interrupt");
+			it6505->cp_done = 1;
+			dptx_sys_chg(it6505, SYS_NOROP);
+		}
+
+		if (reg06 & BIT(3)) {
+			DRM_DEV_DEBUG_DRIVER(
+				dev, "HDCP encryption Fail Interrupt, retry");
+			it6505->cp_done = 0;
+			it6505_init_and_mask_on(it6505);
+		}
+	}
+
+	if (reg06 & BIT(1)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "HPD IRQ Interrupt");
+		hpd_irq(it6505);
+	}
+
+	if (reg06 & BIT(2)) {
+		dptxrd(it6505, 0x0D, &rddata);
+
+		if (rddata & BIT(2)) {
+			DRM_DEV_DEBUG_DRIVER(dev, "Video Stable On Interrupt");
+			it6505->vidstable_done = 1;
+			dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+			go_on_fsm(it6505);
+		} else {
+			DRM_DEV_DEBUG_DRIVER(dev, "Video Stable Off Interrupt");
+			it6505->vidstable_done = 0;
+		}
+	}
+}
+
+static void it6505_check_reg07(struct it6505 *it6505, unsigned int reg07)
+{
+	struct device *dev = &it6505->client->dev;
+	unsigned int len, i;
+	unsigned int bstatus;
+
+	if (it6505->status == SYS_UNPLUG)
+		return;
+	if (reg07 & BIT(0))
+		DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt");
+
+	if (reg07 & BIT(1)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "HDCP event Interrupt");
+		for (i = 0; i < POLLINGKSVLIST; i++) {
+			bstatus = dptx_dpcdrd(it6505, DP_AUX_HDCP_BSTATUS);
+			usleep_range(2000, 3000);
+			if (bstatus & DP_BSTATUS_READY)
+				break;
+		}
+
+		/*
+		 * Read Bstatus to determine what happened
+		 */
+		DRM_DEV_DEBUG_DRIVER(dev, "Bstatus: 0x%02x", bstatus);
+		dptx_debug_print(it6505, 0x3B, "ar0_low");
+		dptx_debug_print(it6505, 0x3C, "ar0_high");
+		dptx_debug_print(it6505, 0x45, "br0_low");
+		dptx_debug_print(it6505, 0x46, "br0_high");
+
+		if (bstatus & DP_BSTATUS_READY) {
+			DRM_DEV_DEBUG_DRIVER(dev, "HDCP KSV list ready");
+			len = it6505_makeup_sha1_input(it6505);
+			sha1_digest(it6505, it6505->shainput, len,
+				    (u8 *)it6505->av);
+			it6505_check_sha1_result(it6505);
+		}
+	}
+	if (it6505->en_audio && (reg07 & BIT(2))) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio FIFO OverFlow Interrupt");
+		dptx_debug_print(it6505, 0xBE, "");
+		dptxset(it6505, 0xD3, 0x20, 0x20);
+		dptxset(it6505, 0xE8, 0x22, 0x00);
+		dptxset(it6505, 0xB8, 0x80, 0x80);
+		dptxset(it6505, 0xB8, 0x80, 0x00);
+		it6505_set_audio(it6505);
+	}
+	if (it6505->en_audio && (reg07 & BIT(5)))
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio infoframe packet done");
+}
+
+static void it6505_check_reg08(struct it6505 *it6505, unsigned int reg08)
+{
+	struct device *dev = &it6505->client->dev;
+
+	if (it6505->status == SYS_UNPLUG)
+		return;
+	if (it6505->en_audio && (reg08 & BIT(1)))
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio TimeStamp Packet Done!");
+
+	if (it6505->en_audio && (reg08 & BIT(3))) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Audio M Error Interrupt ...");
+		show_aud_mcnt(it6505);
+	}
+	if (reg08 & BIT(4)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "Link Training Fail Interrupt");
+		/* restart training */
+		dptx_sys_chg(it6505, SYS_AUTOTRAIN);
+		go_on_fsm(it6505);
+	}
+
+	if (reg08 & BIT(7)) {
+		DRM_DEV_DEBUG_DRIVER(dev, "IO Latch FIFO OverFlow Interrupt");
+		dptxset(it6505, 0x61, 0x02, 0x02);
+		dptxset(it6505, 0x61, 0x02, 0x00);
+	}
+}
+
+static void it6505_dptx_irq(struct it6505 *it6505)
+{
+	unsigned int reg06, reg07, reg08, reg0d;
+	struct device *dev = &it6505->client->dev;
+
+	dptxrd(it6505, 0x06, &reg06);
+	dptxrd(it6505, 0x07, &reg07);
+	dptxrd(it6505, 0x08, &reg08);
+	dptxrd(it6505, 0x0D, &reg0d);
+
+	dptxwr(it6505, 0x06, reg06);
+	dptxwr(it6505, 0x07, reg07);
+	dptxwr(it6505, 0x08, reg08);
+
+	DRM_DEV_DEBUG_DRIVER(dev, "reg06 = 0x%02x", reg06);
+	DRM_DEV_DEBUG_DRIVER(dev, "reg07 = 0x%02x", reg07);
+	DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", reg08);
+	DRM_DEV_DEBUG_DRIVER(dev, "reg0d = 0x%02x", reg0d);
+
+	if (reg06 != 0)
+		it6505_check_reg06(it6505, reg06);
+
+	if (reg07 != 0)
+		it6505_check_reg07(it6505, reg07);
+
+	if (reg08 != 0)
+		it6505_check_reg08(it6505, reg08);
+}
+
+static void it6505_bridge_enable(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	if (!it6505->drv_hold) {
+		it6505_int_mask_on(it6505);
+		dptx_sys_chg(it6505, SYS_HPD);
+	}
+}
+
+static void it6505_bridge_disable(struct drm_bridge *bridge)
+{
+	struct it6505 *it6505 = bridge_to_it6505(bridge);
+
+	dptx_sys_chg(it6505, SYS_UNPLUG);
+}
+
+static const struct drm_bridge_funcs it6505_bridge_funcs = {
+	.attach = it6505_bridge_attach,
+	.detach = it6505_bridge_detach,
+	.mode_valid = it6505_bridge_mode_valid,
+	.mode_set = it6505_bridge_mode_set,
+	.enable = it6505_bridge_enable,
+	.disable = it6505_bridge_disable,
+};
+
+static void it6505_clear_int(struct it6505 *it6505)
+{
+	dptxwr(it6505, 0x06, 0xFF);
+	dptxwr(it6505, 0x07, 0xFF);
+	dptxwr(it6505, 0x08, 0xFF);
+}
+
+static irqreturn_t it6505_intp_threaded_handler(int unused, void *data)
+{
+	struct it6505 *it6505 = data;
+	struct device *dev = &it6505->client->dev;
+
+	msleep(150);
+	mutex_lock(&it6505->lock);
+
+	if (it6505->drv_hold == 0 && it6505->powered) {
+		DRM_DEV_DEBUG_DRIVER(dev, "into it6505_dptx_irq");
+		it6505_dptx_irq(it6505);
+	}
+
+	mutex_unlock(&it6505->lock);
+	return IRQ_HANDLED;
+}
+
+static int it6505_init_pdata(struct it6505 *it6505)
+{
+	struct it6505_platform_data *pdata = &it6505->pdata;
+	struct device *dev = &it6505->client->dev;
+
+	/* 1.0V digital core power regulator  */
+	pdata->pwr18 = devm_regulator_get(dev, "pwr18");
+	if (IS_ERR(pdata->pwr18)) {
+		DRM_DEV_ERROR(dev, "pwr18 regulator not found");
+		return PTR_ERR(pdata->pwr18);
+	}
+
+	pdata->ovdd = devm_regulator_get(dev, "ovdd");
+	if (IS_ERR(pdata->ovdd)) {
+		DRM_DEV_ERROR(dev, "ovdd regulator not found");
+		return PTR_ERR(pdata->ovdd);
+	}
+
+	/* GPIO for HPD */
+	pdata->gpiod_hpd = devm_gpiod_get(dev, "hpd", GPIOD_IN);
+	if (IS_ERR(pdata->gpiod_hpd))
+		return PTR_ERR(pdata->gpiod_hpd);
+
+	/* GPIO for chip reset */
+	pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
+
+	return PTR_ERR_OR_ZERO(pdata->gpiod_reset);
+}
+
+static ssize_t drv_hold_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->drv_hold);
+}
+
+static ssize_t drv_hold_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	if (kstrtoint(buf, 10, &it6505->drv_hold) < 0)
+		return -EINVAL;
+
+	if (it6505->drv_hold) {
+		it6505_int_mask_off(it6505);
+	} else {
+		it6505_clear_int(it6505);
+		it6505_int_mask_on(it6505);
+	}
+	return count;
+}
+
+static ssize_t print_timing_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	struct drm_display_mode *vid = &it6505->vid_info;
+	char *str = buf, *end = buf + PAGE_SIZE;
+
+	str += scnprintf(str, end - str, "---video timing---\n");
+	str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n", vid->clock / 1000,
+			 vid->clock % 1000);
+	str += scnprintf(str, end - str, "HTotal:%d\n", vid->htotal);
+	str += scnprintf(str, end - str, "HActive:%d\n", vid->hdisplay);
+	str += scnprintf(str, end - str, "HFrontPorch:%d\n",
+			 vid->hsync_start - vid->hdisplay);
+	str += scnprintf(str, end - str, "HSyncWidth:%d\n",
+			 vid->hsync_end - vid->hsync_start);
+	str += scnprintf(str, end - str, "HBackPorch:%d\n",
+			 vid->htotal - vid->hsync_end);
+	str += scnprintf(str, end - str, "VTotal:%d\n", vid->vtotal);
+	str += scnprintf(str, end - str, "VActive:%d\n", vid->vdisplay);
+	str += scnprintf(str, end - str, "VFrontPorch:%d\n",
+			 vid->vsync_start - vid->vdisplay);
+	str += scnprintf(str, end - str, "VSyncWidth:%d\n",
+			 vid->vsync_end - vid->vsync_start);
+	str += scnprintf(str, end - str, "VBackPorch:%d\n",
+			 vid->vtotal - vid->vsync_end);
+
+	return str - buf;
+}
+
+static ssize_t sha_debug_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	int i = 0;
+	char *str = buf, *end = buf + PAGE_SIZE;
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	str += scnprintf(str, end - str, "sha input:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->shainput); i += 16)
+		str += scnprintf(str, end - str, "%16ph\n",
+				 it6505->shainput + i);
+
+	str += scnprintf(str, end - str, "av:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->av); i++)
+		str += scnprintf(str, end - str, "%4ph\n", it6505->av[i]);
+
+	str += scnprintf(str, end - str, "bv:\n");
+	for (i = 0; i < ARRAY_SIZE(it6505->bv); i++)
+		str += scnprintf(str, end - str, "%4ph\n", it6505->bv[i]);
+
+	return end - str;
+}
+
+static ssize_t en_hdcp_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->en_hdcp);
+}
+
+static ssize_t en_hdcp_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	unsigned int reg3f, hdcp;
+
+	if (kstrtoint(buf, 10, &it6505->en_hdcp) < 0)
+		return -EINVAL;
+
+	if (!it6505->powered || it6505->status == SYS_UNPLUG) {
+		DRM_DEV_DEBUG_DRIVER(dev,
+				     "power down or unplug, can not fire HDCP");
+		return -EINVAL;
+	}
+
+	if (it6505->en_hdcp) {
+		if (it6505->cp_capable) {
+			dptx_sys_chg(it6505, SYS_ReHDCP);
+			dptx_sys_fsm(it6505);
+		} else {
+			DRM_DEV_ERROR(dev, "sink not support HDCP");
+			it6505->cp_done = 0;
+		}
+	} else {
+		dptxset(it6505, 0x05, 0x10, 0x10);
+		dptxset(it6505, 0x05, 0x10, 0x00);
+		dptxrd(it6505, 0x3F, &reg3f);
+		hdcp = (reg3f & BIT(7)) >> 7;
+		DRM_DEV_DEBUG_DRIVER(dev, "%s to disable hdcp",
+				     hdcp ? "failed" : "succeeded");
+		it6505->cp_done = hdcp;
+	}
+	return count;
+}
+
+static ssize_t en_pwronoff_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->en_pwronoff);
+}
+
+static ssize_t en_pwronoff_store(struct device *dev,
+				 struct device_attribute *attr, const char *buf,
+				 size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	if (kstrtoint(buf, 10, &it6505->en_pwronoff) < 0)
+		return -EINVAL;
+	return count;
+}
+
+static ssize_t en_audio_show(struct device *dev, struct device_attribute *attr,
+			     char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->en_audio);
+}
+
+static ssize_t en_audio_store(struct device *dev, struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	if (kstrtoint(buf, 10, &it6505->en_audio) < 0)
+		return -EINVAL;
+	if (!it6505->powered || it6505->status == SYS_UNPLUG) {
+		DRM_DEV_DEBUG_DRIVER(
+			dev, "power down or unplug, can not output audio");
+		return -EINVAL;
+	}
+
+	if (it6505->en_audio) {
+		it6505_set_audio(it6505);
+	} else {
+		dptxset(it6505, 0x05, 0x02, 0x02);
+		dptxset(it6505, 0x05, 0x02, 0x00);
+	}
+	DRM_DEV_DEBUG_DRIVER(dev, "%s audio",
+			     it6505->en_audio ? "Enable" : "Disable");
+	return count;
+}
+
+static ssize_t force_pwronoff_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+	int pwr;
+
+	if (kstrtoint(buf, 10, &pwr) < 0)
+		return -EINVAL;
+	if (pwr)
+		it6505_poweron(it6505);
+	else
+		it6505_poweroff(it6505);
+	return count;
+}
+
+static ssize_t pwr_status_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct it6505 *it6505 = dev_get_drvdata(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->powered);
+}
+
+static DEVICE_ATTR_RO(print_timing);
+static DEVICE_ATTR_RO(pwr_status);
+static DEVICE_ATTR_RO(sha_debug);
+static DEVICE_ATTR_WO(force_pwronoff);
+static DEVICE_ATTR_RW(drv_hold);
+static DEVICE_ATTR_RW(en_hdcp);
+static DEVICE_ATTR_RW(en_pwronoff);
+static DEVICE_ATTR_RW(en_audio);
+
+static const struct attribute *it6505_attrs[] = {
+	&dev_attr_drv_hold.attr,
+	&dev_attr_print_timing.attr,
+	&dev_attr_sha_debug.attr,
+	&dev_attr_en_hdcp.attr,
+	&dev_attr_en_pwronoff.attr,
+	&dev_attr_en_audio.attr,
+	&dev_attr_force_pwronoff.attr,
+	&dev_attr_pwr_status.attr,
+	NULL,
+};
+
+static int it6505_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct it6505 *it6505;
+	struct it6505_platform_data *pdata;
+	struct device *dev = &client->dev;
+	struct extcon_dev *extcon;
+	int err, intp_irq;
+
+	it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL);
+	if (!it6505)
+		return -ENOMEM;
+
+	mutex_init(&it6505->lock);
+	mutex_init(&it6505->mode_lock);
+
+	pdata = &it6505->pdata;
+
+	it6505->bridge.of_node = client->dev.of_node;
+	it6505->client = client;
+	i2c_set_clientdata(client, it6505);
+
+	/* get extcon device from DTS */
+	extcon = extcon_get_edev_by_phandle(dev, 0);
+	if (PTR_ERR(extcon) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (IS_ERR(extcon)) {
+		DRM_DEV_ERROR(dev, "can not get extcon device!");
+		return -EINVAL;
+	}
+
+	it6505->extcon = extcon;
+
+	it6505->laneswap_disabled =
+		device_property_read_bool(dev, "no-laneswap");
+
+	err = it6505_init_pdata(it6505);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to initialize pdata: %d", err);
+		return err;
+	}
+
+	it6505->regmap =
+		devm_regmap_init_i2c(client, &it6505_bridge_regmap_config);
+	if (IS_ERR(it6505->regmap)) {
+		DRM_DEV_ERROR(dev, "regmap i2c init failed");
+		return PTR_ERR(it6505->regmap);
+	}
+
+	intp_irq = client->irq;
+
+	if (!intp_irq) {
+		DRM_DEV_ERROR(dev, "Failed to get CABLE_DET and INTP IRQ");
+		return -ENODEV;
+	}
+
+	err = devm_request_threaded_irq(&client->dev, intp_irq, NULL,
+					it6505_intp_threaded_handler,
+					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+					"it6505-intp", it6505);
+	if (err) {
+		DRM_DEV_ERROR(dev, "Failed to request INTP threaded IRQ: %d",
+			      err);
+		return err;
+	}
+
+	/* Register aux channel */
+	it6505->aux.name = "DP-AUX";
+	it6505->aux.dev = dev;
+	it6505->aux.transfer = it6505_aux_transfer;
+	err = drm_dp_aux_register(&it6505->aux);
+	if (err < 0) {
+		DRM_DEV_ERROR(dev, "Failed to register aux: %d", err);
+		return err;
+	}
+
+	it6505->en_pwronoff = DEFAULTPWRONOFF;
+	it6505->drv_hold = DEFAULTDRVHOLD;
+	it6505->en_hdcp = DEFAULTHDCP;
+	it6505->en_audio = DEFAULTAUDIO;
+	it6505->powered = 0;
+
+	if (DEFAULTPWRON)
+		it6505_poweron(it6505);
+	err = sysfs_create_files(&client->dev.kobj, it6505_attrs);
+	if (err) {
+		drm_dp_aux_unregister(&it6505->aux);
+		return err;
+	}
+
+	it6505->bridge.funcs = &it6505_bridge_funcs;
+	drm_bridge_add(&it6505->bridge);
+	return 0;
+}
+
+static int it6505_remove(struct i2c_client *client)
+{
+	struct it6505 *it6505 = i2c_get_clientdata(client);
+
+	drm_bridge_remove(&it6505->bridge);
+	sysfs_remove_files(&client->dev.kobj, it6505_attrs);
+	kfree(it6505->edid);
+	it6505->edid = NULL;
+	drm_connector_unregister(&it6505->connector);
+	drm_dp_aux_unregister(&it6505->aux);
+	return 0;
+}
+
+static const struct i2c_device_id it6505_id[] = {
+	{ "it6505", 0 },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(i2c, it6505_id);
+
+static const struct of_device_id it6505_of_match[] = {
+	{ .compatible = "ite,it6505" },
+	{ }
+};
+
+struct i2c_driver it6505_i2c_driver = {
+	.driver = {
+		.name = "it6505_dptx",
+		.owner = THIS_MODULE,
+		.of_match_table = it6505_of_match,
+	},
+	.probe = it6505_i2c_probe,
+	.remove = it6505_remove,
+	.id_table = it6505_id,
+};
+
+module_i2c_driver(it6505_i2c_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_DESCRIPTION("IT6505 DisplayPort Transmitter driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* Re: [PATCH] ACPI: support for NXP i2c controller
From: Oleksij Rempel @ 2019-09-03 10:50 UTC (permalink / raw)
  To: Rafael J. Wysocki, Chuanhua Han, Wolfram Sang, Andy Shevchenko
  Cc: Udit Kumar, Sascha Hauer, Rafael J. Wysocki,
	Linux Kernel Mailing List, Leo Li, ACPI Devel Maling List,
	Meenakshi Aggarwal, linux-i2c, Shawn Guo, Linux ARM, Len Brown
In-Reply-To: <CAJZ5v0hY2sL+XfN_4v07_hjvoxgCAt+Q89+wNg5Pky6XKP-mqA@mail.gmail.com>

One more question,

On 02.09.19 22:56, Rafael J. Wysocki wrote:
> On Thu, Jul 11, 2019 at 12:35 PM Chuanhua Han <chuanhua.han@nxp.com> wrote:
>>
>> Enable NXP i2c controller to boot with ACPI
>>
>> Signed-off-by: Meenakshi Aggarwal <meenakshi.aggarwal@nxp.com>
>> Signed-off-by: Udit Kumar <udit.kumar@nxp.com>
>> Signed-off-by: Chuanhua Han <chuanhua.han@nxp.com>
> 
> Wolfram, any objections to this from the i2c side?
> 
>> ---
>>   drivers/acpi/acpi_apd.c      |  6 ++++++
>>   drivers/i2c/busses/i2c-imx.c | 15 +++++++++++++++
>>   2 files changed, 21 insertions(+)
>>
>> diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
>> index ff47317..cf8566c 100644
>> --- a/drivers/acpi/acpi_apd.c
>> +++ b/drivers/acpi/acpi_apd.c
>> @@ -165,6 +165,11 @@ static const struct apd_device_desc thunderx2_i2c_desc = {
>>          .fixed_clk_rate = 125000000,
>>   };
>>
>> +static const struct apd_device_desc nxp_i2c_desc = {
>> +       .setup = acpi_apd_setup,
>> +       .fixed_clk_rate = 350000000,
>> +};

I'm not ACPI expert, so need here some help for understanding. Here is ACPI table for 
NXP0001 id (found on the internet):
+  Device(I2C0) {
+    Name(_HID, "NXP0001")
+    Name(_UID, 0)
+    Name(_CRS, ResourceTemplate() {
+      Memory32Fixed(ReadWrite, I2C0_BASE, I2C_LEN)
+      Interrupt(ResourceConsumer, Level, ActiveHigh, Shared) { I2C0_IT }
+    }) // end of _CRS for i2c0 device
+    Name (_DSD, Package () {
+      ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+      Package () {
+         Package () {"clock-frequency", DEFAULT_PLAT_FREQ}, //This is device specific 
data, Need to see how to pass clk stuff
+      }
+    })

Should kernel some how get proper clock-frequency from the ACPI? Or we still need to use 
hard coded .fixed_clk_rate in the kernel?


>>   static const struct apd_device_desc hip08_spi_desc = {
>>          .setup = acpi_apd_setup,
>>          .fixed_clk_rate = 250000000,
>> @@ -238,6 +243,7 @@ static const struct acpi_device_id acpi_apd_device_ids[] = {
>>          { "HISI02A1", APD_ADDR(hip07_i2c_desc) },
>>          { "HISI02A2", APD_ADDR(hip08_i2c_desc) },
>>          { "HISI0173", APD_ADDR(hip08_spi_desc) },
>> +       { "NXP0001", APD_ADDR(nxp_i2c_desc) },
>>   #endif
>>          { }
>>   };
>> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
>> index b1b8b93..99f9b96 100644
>> --- a/drivers/i2c/busses/i2c-imx.c
>> +++ b/drivers/i2c/busses/i2c-imx.c
>> @@ -44,6 +44,7 @@
>>   #include <linux/pm_runtime.h>
>>   #include <linux/sched.h>
>>   #include <linux/slab.h>
>> +#include <linux/acpi.h>
>>
>>   /* This will be the driver name the kernel reports */
>>   #define DRIVER_NAME "imx-i2c"
>> @@ -255,6 +256,12 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
>>   };
>>   MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
>>
>> +static const struct acpi_device_id i2c_imx_acpi_ids[] = {
>> +       {"NXP0001", .driver_data = (kernel_ulong_t)&vf610_i2c_hwdata},
>> +       { }
>> +};
>> +MODULE_DEVICE_TABLE(acpi, i2c_imx_acpi_ids);
>> +
>>   static inline int is_imx1_i2c(struct imx_i2c_struct *i2c_imx)
>>   {
>>          return i2c_imx->hwdata->devtype == IMX1_I2C;
>> @@ -1052,6 +1059,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
>>   {
>>          const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
>>                                                             &pdev->dev);
>> +       const struct acpi_device_id *acpi_id =
>> +                       acpi_match_device(i2c_imx_acpi_ids,
>> +                                         &pdev->dev);
>>          struct imx_i2c_struct *i2c_imx;
>>          struct resource *res;
>>          struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
>> @@ -1079,6 +1089,9 @@ static int i2c_imx_probe(struct platform_device *pdev)
>>
>>          if (of_id)
>>                  i2c_imx->hwdata = of_id->data;
>> +       else if (acpi_id)
>> +               i2c_imx->hwdata = (struct imx_i2c_hwdata *)
>> +                               acpi_id->driver_data;
>>          else
>>                  i2c_imx->hwdata = (struct imx_i2c_hwdata *)
>>                                  platform_get_device_id(pdev)->driver_data;
>> @@ -1091,6 +1104,7 @@ static int i2c_imx_probe(struct platform_device *pdev)
>>          i2c_imx->adapter.nr             = pdev->id;
>>          i2c_imx->adapter.dev.of_node    = pdev->dev.of_node;
>>          i2c_imx->base                   = base;
>> +       ACPI_COMPANION_SET(&i2c_imx->adapter.dev, ACPI_COMPANION(&pdev->dev));
>>
>>          /* Get I2C clock */
>>          i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
>> @@ -1253,6 +1267,7 @@ static struct platform_driver i2c_imx_driver = {
>>                  .name = DRIVER_NAME,
>>                  .pm = &i2c_imx_pm_ops,
>>                  .of_match_table = i2c_imx_dt_ids,
>> +               .acpi_match_table = ACPI_PTR(i2c_imx_acpi_ids),
>>          },
>>          .id_table = imx_i2c_devtype,
>>   };
>> --
>> 2.9.5
>>
> 

Kind regards,
Oleksij Rempel

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH 4/5] dt-bindings: dma: ti-edma: Add option for reserved channel ranges
From: Peter Ujfalusi @ 2019-09-03 10:19 UTC (permalink / raw)
  To: Rob Herring
  Cc: devicetree, linux-kernel, vkoul, dmaengine, dan.j.williams,
	linux-omap, linux-arm-kernel
In-Reply-To: <a4c5688b-cbeb-5059-5351-11d9ae1b25d5@ti.com>

Hi Rob,

On 30/08/2019 8.37, Peter Ujfalusi wrote:
> Rob,
> 
> On 30/08/2019 1.47, Rob Herring wrote:
>> On Fri, Aug 23, 2019 at 03:56:17PM +0300, Peter Ujfalusi wrote:
>>> Similarly to paRAM slots, channels can be used by other cores.
>>>
>>> Add optional property to configure the reserved channel ranges.
>>>
>>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>>> ---
>>>  Documentation/devicetree/bindings/dma/ti-edma.txt | 5 +++++
>>>  1 file changed, 5 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/dma/ti-edma.txt b/Documentation/devicetree/bindings/dma/ti-edma.txt
>>> index 4bbc94d829c8..1198682ada99 100644
>>> --- a/Documentation/devicetree/bindings/dma/ti-edma.txt
>>> +++ b/Documentation/devicetree/bindings/dma/ti-edma.txt
>>> @@ -42,6 +42,9 @@ Optional properties:
>>>  - ti,edma-reserved-slot-ranges: PaRAM slot ranges which should not be used by
>>>  		the driver, they are allocated to be used by for example the
>>>  		DSP. See example.
>>> +- ti,edma-reserved-chan-ranges: channel ranges which should not be used by
>>> +		the driver, they are allocated to be used by for example the
>>> +		DSP. See example.
>>
>> Based on the other thread, I think extending dma-channel-mask to a 
>> uint32-array makes sense here.
> 
> Yes, that is the reason I have asked on that and I'm in progress of
> converting the edma driver to use the dma-channel-mask.
> Just need to do some shuffling in the driver to get the mask in a form
> usable by the driver.
> 
> I'll send an updated series early next week.

How should the dma-channel-mask uint31-array should be documented and used?

Basically some EDMA have 32, some 64 channels. This is fine.
Let's say I want to mask out channel 0-4 and 24-27

This would look like in case of EDMA with 32 channels:
&edma {
	/* channel 0-4 and 24-27 is not to be used */
	dma-channel-mask = <0xf0fffff0>;
};

How this should look like in case when I have 64 channels?
&edma {
	/* channel 0-4 and 24-27 is not to be used */
	dma-channel-mask = <0xf0fffff0>, <0xffffffff>;
};

When I read the u32s then
chan_mask[0] is for channel 0-31 (LSB is channel 0)
chan_maks[1] is for channel 32-63 (LSB is channel 32)

Or:
&edma {
	/* channel 0-4 and 24-27 is not to be used */
	dma-channel-mask = <0xffffffff>, <0xf0fffff0>;
};

chan_maks[0] is for channel 32-63 (LSB is channel 32)
chan_mask[1] is for channel 0-31 (LSB is channel 0)

Do you have pointer on already established notion on how to document and
handle this?

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH V3 2/5] input: keyboard: imx_sc: Add i.MX system controller key support
From: Oleksij Rempel @ 2019-09-03 10:05 UTC (permalink / raw)
  To: Anson Huang, robh+dt, mark.rutland, shawnguo, s.hauer, kernel,
	festevam, catalin.marinas, will, dmitry.torokhov, aisheng.dong,
	ulf.hansson, fugang.duan, peng.fan, daniel.baluta,
	leonard.crestez, mripard, olof, arnd, jagan, bjorn.andersson,
	dinguyen, marcin.juszkiewicz, stefan, gregkh, andriy.shevchenko,
	yuehaibing, tglx, ronald, m.felsch, ping.bai, devicetree,
	linux-kernel, linux-arm-kernel, linux-input
  Cc: Linux-imx
In-Reply-To: <1567546600-21566-2-git-send-email-Anson.Huang@nxp.com>



On 03.09.19 23:36, Anson Huang wrote:
> i.MX8QXP is an ARMv8 SoC which has a Cortex-M4 system controller
> inside, the system controller is in charge of controlling power,
> clock and scu key etc..
> 
> Adds i.MX system controller key driver support, Linux kernel has
> to communicate with system controller via MU (message unit) IPC
> to get scu key's status.
> 
> Signed-off-by: Anson Huang <Anson.Huang@nxp.com>

Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>

> ---
> Changes since V2:
> 	- use private platform data instead of global data;
> 	- use "key" instead of "pwrkey";
> 	- fix some data format.
> ---
>   drivers/input/keyboard/Kconfig      |   7 ++
>   drivers/input/keyboard/Makefile     |   1 +
>   drivers/input/keyboard/imx_sc_key.c | 178 ++++++++++++++++++++++++++++++++++++
>   3 files changed, 186 insertions(+)
>   create mode 100644 drivers/input/keyboard/imx_sc_key.c
> 
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index 2e6d288..607acf2 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -469,6 +469,13 @@ config KEYBOARD_IMX
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called imx_keypad.
>   
> +config KEYBOARD_IMX_SC_KEY
> +	tristate "IMX SCU Key Driver"
> +	depends on IMX_SCU
> +	help
> +	  This is the system controller key driver for NXP i.MX SoCs with
> +	  system controller inside.
> +
>   config KEYBOARD_NEWTON
>   	tristate "Newton keyboard"
>   	select SERIO
> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> index 9510325..f5b1752 100644
> --- a/drivers/input/keyboard/Makefile
> +++ b/drivers/input/keyboard/Makefile
> @@ -29,6 +29,7 @@ obj-$(CONFIG_KEYBOARD_HIL)		+= hil_kbd.o
>   obj-$(CONFIG_KEYBOARD_HIL_OLD)		+= hilkbd.o
>   obj-$(CONFIG_KEYBOARD_IPAQ_MICRO)	+= ipaq-micro-keys.o
>   obj-$(CONFIG_KEYBOARD_IMX)		+= imx_keypad.o
> +obj-$(CONFIG_KEYBOARD_IMX_SC_KEY)	+= imx_sc_key.o
>   obj-$(CONFIG_KEYBOARD_HP6XX)		+= jornada680_kbd.o
>   obj-$(CONFIG_KEYBOARD_HP7XX)		+= jornada720_kbd.o
>   obj-$(CONFIG_KEYBOARD_LKKBD)		+= lkkbd.o
> diff --git a/drivers/input/keyboard/imx_sc_key.c b/drivers/input/keyboard/imx_sc_key.c
> new file mode 100644
> index 0000000..e69479b
> --- /dev/null
> +++ b/drivers/input/keyboard/imx_sc_key.c
> @@ -0,0 +1,178 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright 2019 NXP.
> + */
> +
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/firmware/imx/sci.h>
> +#include <linux/init.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/jiffies.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +
> +#define DEBOUNCE_TIME	100
> +#define REPEAT_INTERVAL	60
> +
> +#define SC_IRQ_BUTTON		1
> +#define SC_IRQ_GROUP_WAKE	3
> +#define IMX_SC_MISC_FUNC_GET_BUTTON_STATUS	18
> +
> +struct imx_key_drv_data {
> +	int keycode;
> +	bool keystate;  /* 1: pressed, 0: release */
> +	bool delay_check;
> +	struct delayed_work check_work;
> +	struct input_dev *input;
> +	struct imx_sc_ipc *key_ipc_handle;
> +	struct notifier_block key_notifier;
> +};
> +
> +struct imx_sc_msg_key {
> +	struct imx_sc_rpc_msg hdr;
> +	u8 state;
> +};
> +
> +static int imx_sc_key_notify(struct notifier_block *nb,
> +			     unsigned long event, void *group)
> +{
> +	struct imx_key_drv_data *priv =
> +				 container_of(nb,
> +					      struct imx_key_drv_data,
> +					      key_notifier);
> +
> +	if ((event & SC_IRQ_BUTTON) && (*(u8 *)group == SC_IRQ_GROUP_WAKE)
> +	    && !priv->delay_check) {
> +		priv->delay_check = 1;
> +		schedule_delayed_work(&priv->check_work,
> +				      msecs_to_jiffies(REPEAT_INTERVAL));
> +	}
> +
> +	return 0;
> +}
> +
> +static void imx_sc_check_for_events(struct work_struct *work)
> +{
> +	struct imx_key_drv_data *priv =
> +				 container_of(work,
> +					      struct imx_key_drv_data,
> +					      check_work.work);
> +	struct input_dev *input = priv->input;
> +	struct imx_sc_msg_key msg;
> +	struct imx_sc_rpc_msg *hdr = &msg.hdr;
> +	bool state;
> +	int ret;
> +
> +	hdr->ver = IMX_SC_RPC_VERSION;
> +	hdr->svc = IMX_SC_RPC_SVC_MISC;
> +	hdr->func = IMX_SC_MISC_FUNC_GET_BUTTON_STATUS;
> +	hdr->size = 1;
> +
> +	ret = imx_scu_call_rpc(priv->key_ipc_handle, &msg, true);
> +	if (ret) {
> +		dev_err(&input->dev, "read imx sc key failed, ret %d\n", ret);
> +		return;
> +	}
> +
> +	state = (bool)msg.state;
> +
> +	if (!state && !priv->keystate)
> +		state = true;
> +
> +	if (state ^ priv->keystate) {
> +		pm_wakeup_event(input->dev.parent, 0);
> +		priv->keystate = state;
> +		input_event(input, EV_KEY, priv->keycode, state);
> +		input_sync(input);
> +		if (!state)
> +			priv->delay_check = 0;
> +		pm_relax(priv->input->dev.parent);
> +	}
> +
> +	if (state)
> +		schedule_delayed_work(&priv->check_work,
> +				      msecs_to_jiffies(DEBOUNCE_TIME));
> +}
> +
> +static int imx_sc_key_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	static struct imx_key_drv_data *priv;
> +	struct input_dev *input;
> +	int ret;
> +
> +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	ret = imx_scu_get_handle(&priv->key_ipc_handle);
> +	if (ret)
> +		return ret;
> +
> +	if (of_property_read_u32(np, "linux,keycode", &priv->keycode)) {
> +		dev_err(&pdev->dev, "missing KEY_POWER in DT\n");
> +		return -EINVAL;
> +	}
> +
> +	INIT_DELAYED_WORK(&priv->check_work, imx_sc_check_for_events);
> +
> +	input = devm_input_allocate_device(&pdev->dev);
> +	if (!input) {
> +		dev_err(&pdev->dev, "failed to allocate the input device\n");
> +		return -ENOMEM;
> +	}
> +
> +	input->name = pdev->name;
> +	input->phys = "imx-sc-key/input0";
> +	input->id.bustype = BUS_HOST;
> +
> +	input_set_capability(input, EV_KEY, priv->keycode);
> +
> +	ret = input_register_device(input);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register input device\n");
> +		return ret;
> +	}
> +
> +	priv->input = input;
> +	platform_set_drvdata(pdev, priv);
> +
> +	ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, true);
> +	if (ret) {
> +		dev_warn(&pdev->dev, "enable scu group irq failed\n");
> +		return ret;
> +	}
> +
> +	priv->key_notifier.notifier_call = imx_sc_key_notify;
> +	ret = imx_scu_irq_register_notifier(&priv->key_notifier);
> +	if (ret) {
> +		imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, false);
> +		dev_warn(&pdev->dev, "register scu notifier failed\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id imx_sc_key_ids[] = {
> +	{ .compatible = "fsl,imx-sc-key" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, imx_sc_key_ids);
> +
> +static struct platform_driver imx_sc_key_driver = {
> +	.driver = {
> +		.name = "imx-sc-key",
> +		.of_match_table = imx_sc_key_ids,
> +	},
> +	.probe = imx_sc_key_probe,
> +};
> +module_platform_driver(imx_sc_key_driver);
> +
> +MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
> +MODULE_DESCRIPTION("i.MX System Controller Key Driver");
> +MODULE_LICENSE("GPL v2");
> 

Kind regards,
Oleksij Rempel

-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH] drm: bridge/dw_hdmi: add audio sample channel status setting
From: Neil Armstrong @ 2019-09-03  9:53 UTC (permalink / raw)
  To: Cheng-Yi Chiang, linux-kernel
  Cc: alsa-devel, tzungbi, zhengxing, kuninori.morimoto.gx,
	linux-rockchip, airlied, sam, jeffy.chen, dianders, dri-devel,
	cain.cai, a.hajda, eddie.cai, Laurent.pinchart, daniel,
	Yakir Yang, enric.balletbo, dgreid, kuankuan.y, linux-arm-kernel
In-Reply-To: <20190903055103.134764-1-cychiang@chromium.org>

Hi,

On 03/09/2019 07:51, Cheng-Yi Chiang wrote:
> From: Yakir Yang <ykk@rock-chips.com>
> 
> When transmitting IEC60985 linear PCM audio, we configure the
> Audio Sample Channel Status information of all the channel
> status bits in the IEC60958 frame.
> Refer to 60958-3 page 10 for frequency, original frequency, and
> wordlength setting.
> 
> This fix the issue that audio does not come out on some monitors
> (e.g. LG 22CV241)
> 
> Signed-off-by: Yakir Yang <ykk@rock-chips.com>
> Signed-off-by: Cheng-Yi Chiang <cychiang@chromium.org>
> ---
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 59 +++++++++++++++++++++++
>  drivers/gpu/drm/bridge/synopsys/dw-hdmi.h | 20 ++++++++
>  2 files changed, 79 insertions(+)
> 
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> index bd65d0479683..34d46e25d610 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
> @@ -582,6 +582,63 @@ static unsigned int hdmi_compute_n(unsigned int freq, unsigned long pixel_clk)
>  	return n;
>  }
>  
> +static void hdmi_set_schnl(struct dw_hdmi *hdmi)
> +{
> +	u8 aud_schnl_samplerate;
> +	u8 aud_schnl_8;
> +
> +	/* These registers are on RK3288 using version 2.0a. */
> +	if (hdmi->version != 0x200a)
> +		return;

Are these limited to the 2.0a version *in* RK3288, or 2.0a version on all
SoCs ?

> +
> +	switch (hdmi->sample_rate) {
> +	case 32000:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_32K;
> +		break;
> +	case 44100:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_44K1;
> +		break;
> +	case 48000:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_48K;
> +		break;
> +	case 88200:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_88K2;
> +		break;
> +	case 96000:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_96K;
> +		break;
> +	case 176400:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_176K4;
> +		break;
> +	case 192000:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_192K;
> +		break;
> +	case 768000:
> +		aud_schnl_samplerate = HDMI_FC_AUDSCHNLS7_SMPRATE_768K;
> +		break;
> +	default:
> +		dev_warn(hdmi->dev, "Unsupported audio sample rate (%u)\n",
> +			 hdmi->sample_rate);
> +		return;
> +	}
> +
> +	/* set channel status register */
> +	hdmi_modb(hdmi, aud_schnl_samplerate, HDMI_FC_AUDSCHNLS7_SMPRATE_MASK,
> +		  HDMI_FC_AUDSCHNLS7);
> +
> +	/*
> +	 * Set original frequency to be the same as frequency.
> +	 * Use one-complement value as stated in IEC60958-3 page 13.
> +	 */
> +	aud_schnl_8 = (~aud_schnl_samplerate) <<
> +			HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET;
> +
> +	/* This means word length is 16 bit. Refer to IEC60958-3 page 12. */
> +	aud_schnl_8 |= 2 << HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET;
> +
> +	hdmi_writeb(hdmi, aud_schnl_8, HDMI_FC_AUDSCHNLS8);
> +}
> +
>  static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
>  	unsigned long pixel_clk, unsigned int sample_rate)
>  {
> @@ -620,6 +677,8 @@ static void hdmi_set_clk_regenerator(struct dw_hdmi *hdmi,
>  	hdmi->audio_cts = cts;
>  	hdmi_set_cts_n(hdmi, cts, hdmi->audio_enable ? n : 0);
>  	spin_unlock_irq(&hdmi->audio_lock);
> +
> +	hdmi_set_schnl(hdmi);
>  }
>  
>  static void hdmi_init_clk_regenerator(struct dw_hdmi *hdmi)
> diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> index 6988f12d89d9..619ebc1c8354 100644
> --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h
> @@ -158,6 +158,17 @@
>  #define HDMI_FC_SPDDEVICEINF                    0x1062
>  #define HDMI_FC_AUDSCONF                        0x1063
>  #define HDMI_FC_AUDSSTAT                        0x1064
> +#define HDMI_FC_AUDSV                           0x1065
> +#define HDMI_FC_AUDSU                           0x1066
> +#define HDMI_FC_AUDSCHNLS0                      0x1067
> +#define HDMI_FC_AUDSCHNLS1                      0x1068
> +#define HDMI_FC_AUDSCHNLS2                      0x1069
> +#define HDMI_FC_AUDSCHNLS3                      0x106a
> +#define HDMI_FC_AUDSCHNLS4                      0x106b
> +#define HDMI_FC_AUDSCHNLS5                      0x106c
> +#define HDMI_FC_AUDSCHNLS6                      0x106d
> +#define HDMI_FC_AUDSCHNLS7                      0x106e
> +#define HDMI_FC_AUDSCHNLS8                      0x106f
>  #define HDMI_FC_DATACH0FILL                     0x1070
>  #define HDMI_FC_DATACH1FILL                     0x1071
>  #define HDMI_FC_DATACH2FILL                     0x1072
> @@ -706,6 +717,15 @@ enum {
>  /* HDMI_FC_AUDSCHNLS7 field values */
>  	HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
>  	HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_MASK = 0x0f,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_192K = 0xe,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_176K4 = 0xc,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_96K = 0xa,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_768K = 0x9,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_88K2 = 0x8,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_32K = 0x3,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_48K = 0x2,
> +	HDMI_FC_AUDSCHNLS7_SMPRATE_44K1 = 0x0,
>  
>  /* HDMI_FC_AUDSCHNLS8 field values */
>  	HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
> 


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH V7 3/3] arm64/mm: Enable memory hot remove
From: Anshuman Khandual @ 2019-09-03  9:45 UTC (permalink / raw)
  To: linux-mm, linux-kernel, linux-arm-kernel, akpm, catalin.marinas,
	will
  Cc: mark.rutland, mhocko, david, ira.weiny, steve.capper, mgorman,
	steven.price, broonie, cai, ard.biesheuvel, cpandya, arunks,
	dan.j.williams, Robin.Murphy, logang, valentin.schneider,
	suzuki.poulose, osalvador
In-Reply-To: <1567503958-25831-1-git-send-email-anshuman.khandual@arm.com>

The arch code for hot-remove must tear down portions of the linear map and
vmemmap corresponding to memory being removed. In both cases the page
tables mapping these regions must be freed, and when sparse vmemmap is in
use the memory backing the vmemmap must also be freed.

This patch adds a new remove_pagetable() helper which can be used to tear
down either region, and calls it from vmemmap_free() and
___remove_pgd_mapping(). The sparse_vmap argument determines whether the
backing memory will be freed.

remove_pagetable() makes two distinct passes over the kernel page table.
In the first pass it unmaps, invalidates applicable TLB cache and frees
backing memory if required (vmemmap) for each mapped leaf entry. In the
second pass it looks for empty page table sections whose page table page
can be unmapped, TLB invalidated and freed.

While freeing intermediate level page table pages bail out if any of its
entries are still valid. This can happen for partially filled kernel page
table either from a previously attempted failed memory hot add or while
removing an address range which does not span the entire page table page
range.

The vmemmap region may share levels of table with the vmalloc region.
There can be conflicts between hot remove freeing page table pages with
a concurrent vmalloc() walking the kernel page table. This conflict can
not just be solved by taking the init_mm ptl because of existing locking
scheme in vmalloc(). Hence unlike linear mapping, skip freeing page table
pages while tearing down vmemmap mapping when vmalloc and vmemmap ranges
overlap.

While here update arch_add_memory() to handle __add_pages() failures by
just unmapping recently added kernel linear mapping. Now enable memory hot
remove on arm64 platforms by default with ARCH_ENABLE_MEMORY_HOTREMOVE.

This implementation is overall inspired from kernel page table tear down
procedure on X86 architecture.

Acked-by: Steve Capper <steve.capper@arm.com>
Acked-by: David Hildenbrand <david@redhat.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
 arch/arm64/Kconfig              |   3 +
 arch/arm64/include/asm/memory.h |   1 +
 arch/arm64/mm/mmu.c             | 338 +++++++++++++++++++++++++++++++-
 3 files changed, 333 insertions(+), 9 deletions(-)

diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 6481964b6425..0079820af308 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -274,6 +274,9 @@ config ZONE_DMA32
 config ARCH_ENABLE_MEMORY_HOTPLUG
 	def_bool y
 
+config ARCH_ENABLE_MEMORY_HOTREMOVE
+	def_bool y
+
 config SMP
 	def_bool y
 
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
index b61b50bf68b1..615dcd08acfa 100644
--- a/arch/arm64/include/asm/memory.h
+++ b/arch/arm64/include/asm/memory.h
@@ -54,6 +54,7 @@
 #define MODULES_VADDR		(BPF_JIT_REGION_END)
 #define MODULES_VSIZE		(SZ_128M)
 #define VMEMMAP_START		(-VMEMMAP_SIZE - SZ_2M)
+#define VMEMMAP_END		(VMEMMAP_START + VMEMMAP_SIZE)
 #define PCI_IO_END		(VMEMMAP_START - SZ_2M)
 #define PCI_IO_START		(PCI_IO_END - PCI_IO_SIZE)
 #define FIXADDR_TOP		(PCI_IO_START - SZ_2M)
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index ee1bf416368d..980d761a7327 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -60,6 +60,14 @@ static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;
 
 static DEFINE_SPINLOCK(swapper_pgdir_lock);
 
+/*
+ * This represents if vmalloc and vmemmap address range overlap with
+ * each other on an intermediate level kernel page table entry which
+ * in turn helps in deciding whether empty kernel page table pages
+ * if any can be freed during memory hotplug operation.
+ */
+static bool vmalloc_vmemmap_overlap;
+
 void set_swapper_pgd(pgd_t *pgdp, pgd_t pgd)
 {
 	pgd_t *fixmap_pgdp;
@@ -723,6 +731,250 @@ int kern_addr_valid(unsigned long addr)
 
 	return pfn_valid(pte_pfn(pte));
 }
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+static void free_hotplug_page_range(struct page *page, size_t size)
+{
+	WARN_ON(!page || PageReserved(page));
+	free_pages((unsigned long)page_address(page), get_order(size));
+}
+
+static void free_hotplug_pgtable_page(struct page *page)
+{
+	free_hotplug_page_range(page, PAGE_SIZE);
+}
+
+static void free_pte_table(pmd_t *pmdp, unsigned long addr)
+{
+	struct page *page;
+	pte_t *ptep;
+	int i;
+
+	ptep = pte_offset_kernel(pmdp, 0UL);
+	for (i = 0; i < PTRS_PER_PTE; i++) {
+		if (!pte_none(READ_ONCE(ptep[i])))
+			return;
+	}
+
+	page = pmd_page(READ_ONCE(*pmdp));
+	pmd_clear(pmdp);
+	__flush_tlb_kernel_pgtable(addr);
+	free_hotplug_pgtable_page(page);
+}
+
+static void free_pmd_table(pud_t *pudp, unsigned long addr)
+{
+	struct page *page;
+	pmd_t *pmdp;
+	int i;
+
+	if (CONFIG_PGTABLE_LEVELS <= 2)
+		return;
+
+	pmdp = pmd_offset(pudp, 0UL);
+	for (i = 0; i < PTRS_PER_PMD; i++) {
+		if (!pmd_none(READ_ONCE(pmdp[i])))
+			return;
+	}
+
+	page = pud_page(READ_ONCE(*pudp));
+	pud_clear(pudp);
+	__flush_tlb_kernel_pgtable(addr);
+	free_hotplug_pgtable_page(page);
+}
+
+static void free_pud_table(pgd_t *pgdp, unsigned long addr)
+{
+	struct page *page;
+	pud_t *pudp;
+	int i;
+
+	if (CONFIG_PGTABLE_LEVELS <= 3)
+		return;
+
+	pudp = pud_offset(pgdp, 0UL);
+	for (i = 0; i < PTRS_PER_PUD; i++) {
+		if (!pud_none(READ_ONCE(pudp[i])))
+			return;
+	}
+
+	page = pgd_page(READ_ONCE(*pgdp));
+	pgd_clear(pgdp);
+	__flush_tlb_kernel_pgtable(addr);
+	free_hotplug_pgtable_page(page);
+}
+
+static void unmap_hotplug_pte_range(pmd_t *pmdp, unsigned long addr,
+				    unsigned long end, bool sparse_vmap)
+{
+	struct page *page;
+	pte_t *ptep, pte;
+
+	do {
+		ptep = pte_offset_kernel(pmdp, addr);
+		pte = READ_ONCE(*ptep);
+		if (pte_none(pte))
+			continue;
+
+		WARN_ON(!pte_present(pte));
+		page = sparse_vmap ? pte_page(pte) : NULL;
+		pte_clear(&init_mm, addr, ptep);
+		flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
+		if (sparse_vmap)
+			free_hotplug_page_range(page, PAGE_SIZE);
+	} while (addr += PAGE_SIZE, addr < end);
+}
+
+static void unmap_hotplug_pmd_range(pud_t *pudp, unsigned long addr,
+				    unsigned long end, bool sparse_vmap)
+{
+	unsigned long next;
+	struct page *page;
+	pmd_t *pmdp, pmd;
+
+	do {
+		next = pmd_addr_end(addr, end);
+		pmdp = pmd_offset(pudp, addr);
+		pmd = READ_ONCE(*pmdp);
+		if (pmd_none(pmd))
+			continue;
+
+		WARN_ON(!pmd_present(pmd));
+		if (pmd_sect(pmd)) {
+			page = sparse_vmap ? pmd_page(pmd) : NULL;
+			pmd_clear(pmdp);
+			flush_tlb_kernel_range(addr, next);
+			if (sparse_vmap)
+				free_hotplug_page_range(page, PMD_SIZE);
+			continue;
+		}
+		WARN_ON(!pmd_table(pmd));
+		unmap_hotplug_pte_range(pmdp, addr, next, sparse_vmap);
+	} while (addr = next, addr < end);
+}
+
+static void unmap_hotplug_pud_range(pgd_t *pgdp, unsigned long addr,
+				    unsigned long end, bool sparse_vmap)
+{
+	unsigned long next;
+	struct page *page;
+	pud_t *pudp, pud;
+
+	do {
+		next = pud_addr_end(addr, end);
+		pudp = pud_offset(pgdp, addr);
+		pud = READ_ONCE(*pudp);
+		if (pud_none(pud))
+			continue;
+
+		WARN_ON(!pud_present(pud));
+		if (pud_sect(pud)) {
+			page = sparse_vmap ? pud_page(pud) : NULL;
+			pud_clear(pudp);
+			flush_tlb_kernel_range(addr, next);
+			if (sparse_vmap)
+				free_hotplug_page_range(page, PUD_SIZE);
+			continue;
+		}
+		WARN_ON(!pud_table(pud));
+		unmap_hotplug_pmd_range(pudp, addr, next, sparse_vmap);
+	} while (addr = next, addr < end);
+}
+
+static void unmap_hotplug_range(unsigned long addr, unsigned long end,
+				bool sparse_vmap)
+{
+	unsigned long next;
+	pgd_t *pgdp, pgd;
+
+	do {
+		next = pgd_addr_end(addr, end);
+		pgdp = pgd_offset_k(addr);
+		pgd = READ_ONCE(*pgdp);
+		if (pgd_none(pgd))
+			continue;
+
+		WARN_ON(!pgd_present(pgd));
+		unmap_hotplug_pud_range(pgdp, addr, next, sparse_vmap);
+	} while (addr = next, addr < end);
+}
+
+static void free_empty_pte_table(pmd_t *pmdp, unsigned long addr,
+				 unsigned long end)
+{
+	pte_t *ptep, pte;
+
+	do {
+		ptep = pte_offset_kernel(pmdp, addr);
+		pte = READ_ONCE(*ptep);
+		WARN_ON(!pte_none(pte));
+	} while (addr += PAGE_SIZE, addr < end);
+}
+
+static void free_empty_pmd_table(pud_t *pudp, unsigned long addr,
+				 unsigned long end)
+{
+	unsigned long next;
+	pmd_t *pmdp, pmd;
+
+	do {
+		next = pmd_addr_end(addr, end);
+		pmdp = pmd_offset(pudp, addr);
+		pmd = READ_ONCE(*pmdp);
+		if (pmd_none(pmd))
+			continue;
+
+		WARN_ON(!pmd_present(pmd) || !pmd_table(pmd) || pmd_sect(pmd));
+		free_empty_pte_table(pmdp, addr, next);
+		free_pte_table(pmdp, addr);
+	} while (addr = next, addr < end);
+}
+
+static void free_empty_pud_table(pgd_t *pgdp, unsigned long addr,
+				 unsigned long end)
+{
+	unsigned long next;
+	pud_t *pudp, pud;
+
+	do {
+		next = pud_addr_end(addr, end);
+		pudp = pud_offset(pgdp, addr);
+		pud = READ_ONCE(*pudp);
+		if (pud_none(pud))
+			continue;
+
+		WARN_ON(!pud_present(pud) || !pud_table(pud) || pud_sect(pud));
+		free_empty_pmd_table(pudp, addr, next);
+		free_pmd_table(pudp, addr);
+	} while (addr = next, addr < end);
+}
+
+static void free_empty_tables(unsigned long addr, unsigned long end)
+{
+	unsigned long next;
+	pgd_t *pgdp, pgd;
+
+	do {
+		next = pgd_addr_end(addr, end);
+		pgdp = pgd_offset_k(addr);
+		pgd = READ_ONCE(*pgdp);
+		if (pgd_none(pgd))
+			continue;
+
+		WARN_ON(!pgd_present(pgd));
+		free_empty_pud_table(pgdp, addr, next);
+		free_pud_table(pgdp, addr);
+	} while (addr = next, addr < end);
+}
+
+static void remove_pagetable(unsigned long start, unsigned long end,
+			     bool sparse_vmap)
+{
+	unmap_hotplug_range(start, end, sparse_vmap);
+	free_empty_tables(start, end);
+}
+#endif
+
 #ifdef CONFIG_SPARSEMEM_VMEMMAP
 #if !ARM64_SWAPPER_USES_SECTION_MAPS
 int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
@@ -770,6 +1022,28 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
 void vmemmap_free(unsigned long start, unsigned long end,
 		struct vmem_altmap *altmap)
 {
+#ifdef CONFIG_MEMORY_HOTPLUG
+	/*
+	 * FIXME: We should have called remove_pagetable(start, end, true).
+	 * vmemmap and vmalloc virtual range might share intermediate kernel
+	 * page table entries. Removing vmemmap range page table pages here
+	 * can potentially conflict with a concurrent vmalloc() allocation.
+	 *
+	 * This is primarily because vmalloc() does not take init_mm ptl for
+	 * the entire page table walk and it's modification. Instead it just
+	 * takes the lock while allocating and installing page table pages
+	 * via [p4d|pud|pmd|pte]_alloc(). A concurrently vanishing page table
+	 * entry via memory hot remove can cause vmalloc() kernel page table
+	 * walk pointers to be invalid on the fly which can cause corruption
+	 * or worst, a crash.
+	 *
+	 * So free_empty_tables() gets called where vmalloc and vmemmap range
+	 * do not overlap at any intermediate level kernel page table entry.
+	 */
+	unmap_hotplug_range(start, end, true);
+	if (!vmalloc_vmemmap_overlap)
+		free_empty_tables(start, end);
+#endif
 }
 #endif	/* CONFIG_SPARSEMEM_VMEMMAP */
 
@@ -1048,10 +1322,18 @@ int p4d_free_pud_page(p4d_t *p4d, unsigned long addr)
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG
+static void __remove_pgd_mapping(pgd_t *pgdir, unsigned long start, u64 size)
+{
+	unsigned long end = start + size;
+
+	WARN_ON(pgdir != init_mm.pgd);
+	remove_pagetable(start, end, false);
+}
+
 int arch_add_memory(int nid, u64 start, u64 size,
 			struct mhp_restrictions *restrictions)
 {
-	int flags = 0;
+	int ret, flags = 0;
 
 	if (rodata_full || debug_pagealloc_enabled())
 		flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
@@ -1059,9 +1341,14 @@ int arch_add_memory(int nid, u64 start, u64 size,
 	__create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
 			     size, PAGE_KERNEL, __pgd_pgtable_alloc, flags);
 
-	return __add_pages(nid, start >> PAGE_SHIFT, size >> PAGE_SHIFT,
+	ret = __add_pages(nid, start >> PAGE_SHIFT, size >> PAGE_SHIFT,
 			   restrictions);
+	if (ret)
+		__remove_pgd_mapping(swapper_pg_dir,
+				     __phys_to_virt(start), size);
+	return ret;
 }
+
 void arch_remove_memory(int nid, u64 start, u64 size,
 			struct vmem_altmap *altmap)
 {
@@ -1069,14 +1356,47 @@ void arch_remove_memory(int nid, u64 start, u64 size,
 	unsigned long nr_pages = size >> PAGE_SHIFT;
 	struct zone *zone;
 
-	/*
-	 * FIXME: Cleanup page tables (also in arch_add_memory() in case
-	 * adding fails). Until then, this function should only be used
-	 * during memory hotplug (adding memory), not for memory
-	 * unplug. ARCH_ENABLE_MEMORY_HOTREMOVE must not be
-	 * unlocked yet.
-	 */
 	zone = page_zone(pfn_to_page(start_pfn));
 	__remove_pages(zone, start_pfn, nr_pages, altmap);
+	__remove_pgd_mapping(swapper_pg_dir, __phys_to_virt(start), size);
+}
+
+static int __init find_vmalloc_vmemmap_overlap(void)
+{
+	unsigned long gap_start, gap_end;
+
+	/*
+	 * vmalloc and vmemmap address ranges should be linearly
+	 * increasing and non-overlapping before the gap between
+	 * them can be measured.
+	 */
+	BUILD_BUG_ON(VMALLOC_END <= VMALLOC_START);
+	BUILD_BUG_ON(VMEMMAP_END <= VMEMMAP_START);
+	BUILD_BUG_ON((VMEMMAP_START >= VMALLOC_START)
+			&& (VMEMMAP_START <= VMALLOC_END));
+	BUILD_BUG_ON((VMEMMAP_END >= VMALLOC_START)
+			&& (VMEMMAP_END <= VMALLOC_END));
+
+	/*
+	 * vmalloc and vmemmap can only be non-overlapping disjoint
+	 * ranges. So [gap_start..gap_end] is determined which will
+	 * represent the gap between the above mentioned ranges.
+	 */
+	if (VMEMMAP_START > VMALLOC_END) {
+		gap_start = VMALLOC_END;
+		gap_end = VMEMMAP_START;
+	} else {
+		gap_start = VMEMMAP_END;
+		gap_end = VMALLOC_START;
+	}
+
+	/*
+	 * Race condition during memory hot-remove exists when the
+	 * gap edges share same PGD entry in kernel page table.
+	 */
+	if ((gap_start & PGDIR_MASK) == (gap_end & PGDIR_MASK))
+		vmalloc_vmemmap_overlap = true;
+	return 0;
 }
+early_initcall(find_vmalloc_vmemmap_overlap);
 #endif
-- 
2.20.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH V7 2/3] arm64/mm: Hold memory hotplug lock while walking for kernel page table dump
From: Anshuman Khandual @ 2019-09-03  9:45 UTC (permalink / raw)
  To: linux-mm, linux-kernel, linux-arm-kernel, akpm, catalin.marinas,
	will
  Cc: mark.rutland, mhocko, david, ira.weiny, steve.capper, mgorman,
	steven.price, broonie, cai, ard.biesheuvel, cpandya, arunks,
	dan.j.williams, Robin.Murphy, logang, valentin.schneider,
	suzuki.poulose, osalvador
In-Reply-To: <1567503958-25831-1-git-send-email-anshuman.khandual@arm.com>

The arm64 page table dump code can race with concurrent modification of the
kernel page tables. When a leaf entries are modified concurrently, the dump
code may log stale or inconsistent information for a VA range, but this is
otherwise not harmful.

When intermediate levels of table are freed, the dump code will continue to
use memory which has been freed and potentially reallocated for another
purpose. In such cases, the dump code may dereference bogus addresses,
leading to a number of potential problems.

Intermediate levels of table may by freed during memory hot-remove,
which will be enabled by a subsequent patch. To avoid racing with
this, take the memory hotplug lock when walking the kernel page table.

Acked-by: David Hildenbrand <david@redhat.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
 arch/arm64/mm/ptdump_debugfs.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/mm/ptdump_debugfs.c b/arch/arm64/mm/ptdump_debugfs.c
index 064163f25592..b5eebc8c4924 100644
--- a/arch/arm64/mm/ptdump_debugfs.c
+++ b/arch/arm64/mm/ptdump_debugfs.c
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/debugfs.h>
+#include <linux/memory_hotplug.h>
 #include <linux/seq_file.h>
 
 #include <asm/ptdump.h>
@@ -7,7 +8,10 @@
 static int ptdump_show(struct seq_file *m, void *v)
 {
 	struct ptdump_info *info = m->private;
+
+	get_online_mems();
 	ptdump_walk_pgd(m, info);
+	put_online_mems();
 	return 0;
 }
 DEFINE_SHOW_ATTRIBUTE(ptdump);
-- 
2.20.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH V7 1/3] mm/hotplug: Reorder memblock_[free|remove]() calls in try_remove_memory()
From: Anshuman Khandual @ 2019-09-03  9:45 UTC (permalink / raw)
  To: linux-mm, linux-kernel, linux-arm-kernel, akpm, catalin.marinas,
	will
  Cc: mark.rutland, mhocko, david, ira.weiny, steve.capper, mgorman,
	steven.price, broonie, cai, ard.biesheuvel, cpandya, arunks,
	dan.j.williams, Robin.Murphy, logang, valentin.schneider,
	suzuki.poulose, osalvador
In-Reply-To: <1567503958-25831-1-git-send-email-anshuman.khandual@arm.com>

Memory hot remove uses get_nid_for_pfn() while tearing down linked sysfs
entries between memory block and node. It first checks pfn validity with
pfn_valid_within() before fetching nid. With CONFIG_HOLES_IN_ZONE config
(arm64 has this enabled) pfn_valid_within() calls pfn_valid().

pfn_valid() is an arch implementation on arm64 (CONFIG_HAVE_ARCH_PFN_VALID)
which scans all mapped memblock regions with memblock_is_map_memory(). This
creates a problem in memory hot remove path which has already removed given
memory range from memory block with memblock_[remove|free] before arriving
at unregister_mem_sect_under_nodes(). Hence get_nid_for_pfn() returns -1
skipping subsequent sysfs_remove_link() calls leaving node <-> memory block
sysfs entries as is. Subsequent memory add operation hits BUG_ON() because
of existing sysfs entries.

[   62.007176] NUMA: Unknown node for memory at 0x680000000, assuming node 0
[   62.052517] ------------[ cut here ]------------
[   62.053211] kernel BUG at mm/memory_hotplug.c:1143!
[   62.053868] Internal error: Oops - BUG: 0 [#1] PREEMPT SMP
[   62.054589] Modules linked in:
[   62.054999] CPU: 19 PID: 3275 Comm: bash Not tainted 5.1.0-rc2-00004-g28cea40b2683 #41
[   62.056274] Hardware name: linux,dummy-virt (DT)
[   62.057166] pstate: 40400005 (nZcv daif +PAN -UAO)
[   62.058083] pc : add_memory_resource+0x1cc/0x1d8
[   62.058961] lr : add_memory_resource+0x10c/0x1d8
[   62.059842] sp : ffff0000168b3ce0
[   62.060477] x29: ffff0000168b3ce0 x28: ffff8005db546c00
[   62.061501] x27: 0000000000000000 x26: 0000000000000000
[   62.062509] x25: ffff0000111ef000 x24: ffff0000111ef5d0
[   62.063520] x23: 0000000000000000 x22: 00000006bfffffff
[   62.064540] x21: 00000000ffffffef x20: 00000000006c0000
[   62.065558] x19: 0000000000680000 x18: 0000000000000024
[   62.066566] x17: 0000000000000000 x16: 0000000000000000
[   62.067579] x15: ffffffffffffffff x14: ffff8005e412e890
[   62.068588] x13: ffff8005d6b105d8 x12: 0000000000000000
[   62.069610] x11: ffff8005d6b10490 x10: 0000000000000040
[   62.070615] x9 : ffff8005e412e898 x8 : ffff8005e412e890
[   62.071631] x7 : ffff8005d6b105d8 x6 : ffff8005db546c00
[   62.072640] x5 : 0000000000000001 x4 : 0000000000000002
[   62.073654] x3 : ffff8005d7049480 x2 : 0000000000000002
[   62.074666] x1 : 0000000000000003 x0 : 00000000ffffffef
[   62.075685] Process bash (pid: 3275, stack limit = 0x00000000d754280f)
[   62.076930] Call trace:
[   62.077411]  add_memory_resource+0x1cc/0x1d8
[   62.078227]  __add_memory+0x70/0xa8
[   62.078901]  probe_store+0xa4/0xc8
[   62.079561]  dev_attr_store+0x18/0x28
[   62.080270]  sysfs_kf_write+0x40/0x58
[   62.080992]  kernfs_fop_write+0xcc/0x1d8
[   62.081744]  __vfs_write+0x18/0x40
[   62.082400]  vfs_write+0xa4/0x1b0
[   62.083037]  ksys_write+0x5c/0xc0
[   62.083681]  __arm64_sys_write+0x18/0x20
[   62.084432]  el0_svc_handler+0x88/0x100
[   62.085177]  el0_svc+0x8/0xc

Re-ordering memblock_[free|remove]() with arch_remove_memory() solves the
problem on arm64 as pfn_valid() behaves correctly and returns positive
as memblock for the address range still exists. arch_remove_memory()
removes applicable memory sections from zone with __remove_pages() and
tears down kernel linear mapping. Removing memblock regions afterwards
is safe because there is no other memblock (bootmem) allocator user that
late. So nobody is going to allocate from the removed range just to blow
up later. Also nobody should be using the bootmem allocated range else
we wouldn't allow to remove it. So reordering is indeed safe.

Reviewed-by: David Hildenbrand <david@redhat.com>
Reviewed-by: Oscar Salvador <osalvador@suse.de>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Acked-by: Michal Hocko <mhocko@suse.com>
Signed-off-by: Anshuman Khandual <anshuman.khandual@arm.com>
---
 mm/memory_hotplug.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index c73f09913165..355c466e0621 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1770,13 +1770,13 @@ static int __ref try_remove_memory(int nid, u64 start, u64 size)
 
 	/* remove memmap entry */
 	firmware_map_remove(start, start + size, "System RAM");
-	memblock_free(start, size);
-	memblock_remove(start, size);
 
 	/* remove memory block devices before removing memory */
 	remove_memory_block_devices(start, size);
 
 	arch_remove_memory(nid, start, size, NULL);
+	memblock_free(start, size);
+	memblock_remove(start, size);
 	__release_memory_resource(start, size);
 
 	try_offline_node(nid);
-- 
2.20.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH V7 0/3] arm64/mm: Enable memory hot remove
From: Anshuman Khandual @ 2019-09-03  9:45 UTC (permalink / raw)
  To: linux-mm, linux-kernel, linux-arm-kernel, akpm, catalin.marinas,
	will
  Cc: mark.rutland, mhocko, david, ira.weiny, steve.capper, mgorman,
	steven.price, broonie, cai, ard.biesheuvel, cpandya, arunks,
	dan.j.williams, Robin.Murphy, logang, valentin.schneider,
	suzuki.poulose, osalvador

This series enables memory hot remove on arm64 after fixing a memblock
removal ordering problem in generic try_remove_memory() and a possible
arm64 platform specific kernel page table race condition. This series
is based on the following current arm64 tree.

git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git (for-next/core)

Concurrent vmalloc() and hot-remove conflict:

As pointed out earlier on the v5 thread [2] there can be potential conflict
between concurrent vmalloc() and memory hot-remove operation. The problem here
is caused by inadequate locking in vmalloc() which protects installation of a
page table page but not the walk or the leaf entry modification.

Lets not free page table pages if any for vmemmap mappings after unmapping
it's virtual range if vmalloc and vmemap range overlaps. The only downside
here is that some page table pages might remain empty and unused until next
memory hot-add operation for the same memory range. But as mentioned earlier
those will be minimal.

Only the following two configurations have vmalloc and vmemmap overlap where
free_empty_tables() function is not getting called.

1. 4K page size with 48 bit VA space
2. 16K page size with 48 bit VA space

Testing:

Memory hot remove has been tested on arm64 for 4K, 16K, 64K page config
options with all possible CONFIG_ARM64_VA_BITS and CONFIG_PGTABLE_LEVELS
combinations. It is only build tested on non-arm64 platforms.

Changes in V7:

- vmalloc_vmemmap_overlap gets evaluated early during boot for a given config
- free_empty_tables() gets conditionally called based on vmalloc_vmemmap_overlap

Changes in V6: (https://lkml.org/lkml/2019/7/15/36)

- Implemented most of the suggestions from Mark Rutland
- Added <linux/memory_hotplug.h> in ptdump
- remove_pagetable() now has two distinct passes over the kernel page table
- First pass unmap_hotplug_range() removes leaf level entries at all level
- Second pass free_empty_tables() removes empty page table pages
- Kernel page table lock has been dropped completely
- vmemmap_free() does not call freee_empty_tables() to avoid conflict with vmalloc()
- All address range scanning are converted to do {} while() loop
- Added 'unsigned long end' in __remove_pgd_mapping()
- Callers need not provide starting pointer argument to free_[pte|pmd|pud]_table() 
- Drop the starting pointer argument from free_[pte|pmd|pud]_table() functions
- Fetching pxxp[i] in free_[pte|pmd|pud]_table() is wrapped around in READ_ONCE()
- free_[pte|pmd|pud]_table() now computes starting pointer inside the function
- Fixed TLB handling while freeing huge page section mappings at PMD or PUD level
- Added WARN_ON(!page) in free_hotplug_page_range()
- Added WARN_ON(![pm|pud]_table(pud|pmd)) when there is no section mapping

- [PATCH 1/3] mm/hotplug: Reorder memblock_[free|remove]() calls in try_remove_memory()
- Request earlier for separate merger (https://patchwork.kernel.org/patch/10986599/)
- s/__remove_memory/try_remove_memory in the subject line
- s/arch_remove_memory/memblock_[free|remove] in the subject line
- A small change in the commit message as re-order happens now for memblock remove
  functions not for arch_remove_memory()

Changes in V5: (https://lkml.org/lkml/2019/5/29/218)

- Have some agreement [1] over using memory_hotplug_lock for arm64 ptdump
- Change 7ba36eccb3f8 ("arm64/mm: Inhibit huge-vmap with ptdump") already merged
- Dropped the above patch from this series
- Fixed indentation problem in arch_[add|remove]_memory() as per David
- Collected all new Acked-by tags
 
Changes in V4: (https://lkml.org/lkml/2019/5/20/19)

- Implemented most of the suggestions from Mark Rutland
- Interchanged patch [PATCH 2/4] <---> [PATCH 3/4] and updated commit message
- Moved CONFIG_PGTABLE_LEVELS inside free_[pud|pmd]_table()
- Used READ_ONCE() in missing instances while accessing page table entries
- s/p???_present()/p???_none() for checking valid kernel page table entries
- WARN_ON() when an entry is !p???_none() and !p???_present() at the same time
- Updated memory hot-remove commit message with additional details as suggested
- Rebased the series on 5.2-rc1 with hotplug changes from David and Michal Hocko
- Collected all new Acked-by tags

Changes in V3: (https://lkml.org/lkml/2019/5/14/197)
 
- Implemented most of the suggestions from Mark Rutland for remove_pagetable()
- Fixed applicable PGTABLE_LEVEL wrappers around pgtable page freeing functions
- Replaced 'direct' with 'sparse_vmap' in remove_pagetable() with inverted polarity
- Changed pointer names ('p' at end) and removed tmp from iterations
- Perform intermediate TLB invalidation while clearing pgtable entries
- Dropped flush_tlb_kernel_range() in remove_pagetable()
- Added flush_tlb_kernel_range() in remove_pte_table() instead
- Renamed page freeing functions for pgtable page and mapped pages
- Used page range size instead of order while freeing mapped or pgtable pages
- Removed all PageReserved() handling while freeing mapped or pgtable pages
- Replaced XXX_index() with XXX_offset() while walking the kernel page table
- Used READ_ONCE() while fetching individual pgtable entries
- Taken overall init_mm.page_table_lock instead of just while changing an entry
- Dropped previously added [pmd|pud]_index() which are not required anymore
- Added a new patch to protect kernel page table race condition for ptdump
- Added a new patch from Mark Rutland to prevent huge-vmap with ptdump

Changes in V2: (https://lkml.org/lkml/2019/4/14/5)

- Added all received review and ack tags
- Split the series from ZONE_DEVICE enablement for better review
- Moved memblock re-order patch to the front as per Robin Murphy
- Updated commit message on memblock re-order patch per Michal Hocko
- Dropped [pmd|pud]_large() definitions
- Used existing [pmd|pud]_sect() instead of earlier [pmd|pud]_large()
- Removed __meminit and __ref tags as per Oscar Salvador
- Dropped unnecessary 'ret' init in arch_add_memory() per Robin Murphy
- Skipped calling into pgtable_page_dtor() for linear mapping page table
  pages and updated all relevant functions

Changes in V1: (https://lkml.org/lkml/2019/4/3/28)

References:

[1] https://lkml.org/lkml/2019/5/28/584
[2] https://lkml.org/lkml/2019/6/11/709

Anshuman Khandual (3):
  mm/hotplug: Reorder memblock_[free|remove]() calls in try_remove_memory()
  arm64/mm: Hold memory hotplug lock while walking for kernel page table dump
  arm64/mm: Enable memory hot remove

 arch/arm64/Kconfig              |   3 +
 arch/arm64/include/asm/memory.h |   1 +
 arch/arm64/mm/mmu.c             | 338 +++++++++++++++++++++++++++++++-
 arch/arm64/mm/ptdump_debugfs.c  |   4 +
 mm/memory_hotplug.c             |   4 +-
 5 files changed, 339 insertions(+), 11 deletions(-)

-- 
2.20.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* Re: [PATCH v5 01/11] kselftest: arm64: add skeleton Makefile
From: Cristian Marussi @ 2019-09-03  9:45 UTC (permalink / raw)
  To: Amit Kachhap, linux-kselftest@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org, shuah@kernel.org
  Cc: andreyknvl@google.com, Dave P Martin
In-Reply-To: <cce97298-7a27-c470-6fc5-873b4447ecc9@arm.com>

Hi Amit

On 03/09/2019 10:26, Amit Kachhap wrote:
> 
> Hi Cristian,
> 
> On 9/2/19 4:59 PM, Cristian Marussi wrote:
>> Add a new arm64-specific empty subsystem amongst TARGETS of KSFT build
>> framework; keep these new arm64 KSFT testcases separated into distinct
>> subdirs inside tools/testing/selftests/arm64/ depending on the specific
>> subsystem targeted.
>>
>> Add into toplevel arm64 KSFT Makefile a mechanism to guess the effective
>> location of Kernel headers as installed by KSFT framework.
>>
>> Merge with
>>
>> commit 9ce1263033cd ("selftests, arm64: add a selftest for passing
>> 		     tagged pointers to kernel")
>>
>> while moving such KSFT tags tests inside their own subdirectory
>> (arm64/tags).
>>
>> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
>> ---
>> v4 --> v5
>> - rebased on arm64/for-next/core
>> - merged this patch with KSFT arm64 tags patch, while moving the latter
>>    into its own subdir
>> - moved kernel header includes search mechanism from KSFT arm64
>>    SIGNAL Makefile
> This approach breaks the compilation of individual test cases which need 
> to export includes individually.
> 
> make -C tools/testing/selftests/arm64/signal
> 
> ../../lib.mk:25: ../../../../scripts/subarch.include: No such file or 
> directory
> Makefile:25: warning: overriding recipe for target 'clean'
> ../../lib.mk:123: warning: ignoring old recipe for target 'clean'
> make: *** No rule to make target '../../../../scripts/subarch.include'. 
> Stop.
> 

Having removed standalone mode in signal I was not expecting to be able
to run distinct arm64 subsystems as before with:

$ make -C tools/testing/selftests/arm64/signal
(which was not a standard KSFT thing)

but only using standard targets way like:

$ make -C tools/testing/selftests TARGETS=arm64 

or 

$ make -C tools/testing/selftests TARGETS=arm64 \
		INSTALL_PATH=<your-installation-path> install

or

$ make TARGETS=arm64 kselftest

which runs all tests inside arm64 as a whole.
(I should have changed arm64 README accordingly)


> However tags test works well,
> make -C tools/testing/selftests/arm64/tags
> 
> aarch64-none-linux-gnu-gcc     tags_test.c  -o 
> /home/amikac01/work/MTE_WORK/linux-server/linux/tools/testing/selftests/arm64/tags/tags_test
> 

But I'll have a look why tags expose different behavior.

> 
> Thanks,
> Amit Daniel


Thanks

Cristian
> 
>> - export proper top_srcdir ENV for lib.mk
>> v3 --> v4
>> - comment reword
>> - simplified documentation in README
>> - dropped README about standalone
>> ---
>>   tools/testing/selftests/Makefile              |  1 +
>>   tools/testing/selftests/arm64/Makefile        | 70 +++++++++++++++++--
>>   tools/testing/selftests/arm64/README          | 20 ++++++
>>   tools/testing/selftests/arm64/tags/Makefile   | 10 +++
>>   .../arm64/{ => tags}/run_tags_test.sh         |  0
>>   .../selftests/arm64/{ => tags}/tags_test.c    |  0
>>   6 files changed, 95 insertions(+), 6 deletions(-)
>>   create mode 100644 tools/testing/selftests/arm64/README
>>   create mode 100644 tools/testing/selftests/arm64/tags/Makefile
>>   rename tools/testing/selftests/arm64/{ => tags}/run_tags_test.sh (100%)
>>   rename tools/testing/selftests/arm64/{ => tags}/tags_test.c (100%)
>>
>> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
>> index 25b43a8c2b15..1722dae9381a 100644
>> --- a/tools/testing/selftests/Makefile
>> +++ b/tools/testing/selftests/Makefile
>> @@ -1,5 +1,6 @@
>>   # SPDX-License-Identifier: GPL-2.0
>>   TARGETS = android
>> +TARGETS += arm64
>>   TARGETS += bpf
>>   TARGETS += breakpoints
>>   TARGETS += capabilities
>> diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile
>> index a61b2e743e99..5dbb0ffdfc9a 100644
>> --- a/tools/testing/selftests/arm64/Makefile
>> +++ b/tools/testing/selftests/arm64/Makefile
>> @@ -1,11 +1,69 @@
>>   # SPDX-License-Identifier: GPL-2.0
>> +# Copyright (C) 2019 ARM Limited
>>   
>> -# ARCH can be overridden by the user for cross compiling
>> -ARCH ?= $(shell uname -m 2>/dev/null || echo not)
>> +# When ARCH not overridden for crosscompiling, lookup machine
>> +ARCH ?= $(shell uname -m)
>> +ARCH := $(shell echo $(ARCH) | sed -e s/aarch64/arm64/)
>>   
>> -ifneq (,$(filter $(ARCH),aarch64 arm64))
>> -TEST_GEN_PROGS := tags_test
>> -TEST_PROGS := run_tags_test.sh
>> +ifeq ("x$(ARCH)", "xarm64")
>> +SUBDIRS := tags
>> +else
>> +SUBDIRS :=
>>   endif
>>   
>> -include ../lib.mk
>> +CFLAGS := -Wall -O2 -g
>> +
>> +# A proper top_srcdir is needed by KSFT(lib.mk)
>> +top_srcdir = ../../../../..
>> +
>> +# Additional include paths needed by kselftest.h and local headers
>> +CFLAGS += -I$(top_srcdir)/tools/testing/selftests/
>> +
>> +# Guessing where the Kernel headers could have been installed
>> +# depending on ENV config
>> +ifeq ($(KBUILD_OUTPUT),)
>> +khdr_dir = $(top_srcdir)/usr/include
>> +else
>> +# the KSFT preferred location when KBUILD_OUTPUT is set
>> +khdr_dir = $(KBUILD_OUTPUT)/kselftest/usr/include
>> +endif
>> +
>> +CFLAGS += -I$(khdr_dir)
>> +
>> +export CC
>> +export CFLAGS
>> +export top_srcdir
>> +
>> +all:
>> +	@for DIR in $(SUBDIRS); do				\
>> +		BUILD_TARGET=$(OUTPUT)/$$DIR;			\
>> +		mkdir -p $$BUILD_TARGET;			\
>> +		make OUTPUT=$$BUILD_TARGET -C $$DIR $@;		\
>> +	done
>> +
>> +install: all
>> +	@for DIR in $(SUBDIRS); do				\
>> +		BUILD_TARGET=$(OUTPUT)/$$DIR;			\
>> +		make OUTPUT=$$BUILD_TARGET -C $$DIR $@;		\
>> +	done
>> +
>> +run_tests: all
>> +	@for DIR in $(SUBDIRS); do				\
>> +		BUILD_TARGET=$(OUTPUT)/$$DIR;			\
>> +		make OUTPUT=$$BUILD_TARGET -C $$DIR $@;		\
>> +	done
>> +
>> +# Avoid any output on non arm64 on emit_tests
>> +emit_tests: all
>> +	@for DIR in $(SUBDIRS); do				\
>> +		BUILD_TARGET=$(OUTPUT)/$$DIR;			\
>> +		make OUTPUT=$$BUILD_TARGET -C $$DIR $@;		\
>> +	done
>> +
>> +clean:
>> +	@for DIR in $(SUBDIRS); do				\
>> +		BUILD_TARGET=$(OUTPUT)/$$DIR;			\
>> +		make OUTPUT=$$BUILD_TARGET -C $$DIR $@;		\
>> +	done
>> +
>> +.PHONY: all clean install run_tests emit_tests
>> diff --git a/tools/testing/selftests/arm64/README b/tools/testing/selftests/arm64/README
>> new file mode 100644
>> index 000000000000..aca892e62a6c
>> --- /dev/null
>> +++ b/tools/testing/selftests/arm64/README
>> @@ -0,0 +1,20 @@
>> +KSelfTest ARM64
>> +===============
>> +
>> +- These tests are arm64 specific and so not built or run but just skipped
>> +  completely when env-variable ARCH is found to be different than 'arm64'
>> +  and `uname -m` reports other than 'aarch64'.
>> +
>> +- Holding true the above, ARM64 KSFT tests can be run within the KSelfTest
>> +  framework using standard Linux top-level-makefile targets:
>> +
>> +      $ make TARGETS=arm64 kselftest-clean
>> +      $ make TARGETS=arm64 kselftest
>> +
>> +      or
>> +
>> +      $ make -C tools/testing/selftests TARGETS=arm64 \
>> +		INSTALL_PATH=<your-installation-path> install
>> +
>> +   Further details on building and running KFST can be found in:
>> +     Documentation/dev-tools/kselftest.rst
>> diff --git a/tools/testing/selftests/arm64/tags/Makefile b/tools/testing/selftests/arm64/tags/Makefile
>> new file mode 100644
>> index 000000000000..76205533135b
>> --- /dev/null
>> +++ b/tools/testing/selftests/arm64/tags/Makefile
>> @@ -0,0 +1,10 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# ARCH can be overridden by the user for cross compiling
>> +ARCH ?= $(shell uname -m 2>/dev/null || echo not)
>> +
>> +ifneq (,$(filter $(ARCH),aarch64 arm64))
>> +TEST_GEN_PROGS := tags_test
>> +TEST_PROGS := run_tags_test.sh
>> +endif
>> +
>> +include ../../lib.mk
>> diff --git a/tools/testing/selftests/arm64/run_tags_test.sh b/tools/testing/selftests/arm64/tags/run_tags_test.sh
>> similarity index 100%
>> rename from tools/testing/selftests/arm64/run_tags_test.sh
>> rename to tools/testing/selftests/arm64/tags/run_tags_test.sh
>> diff --git a/tools/testing/selftests/arm64/tags_test.c b/tools/testing/selftests/arm64/tags/tags_test.c
>> similarity index 100%
>> rename from tools/testing/selftests/arm64/tags_test.c
>> rename to tools/testing/selftests/arm64/tags/tags_test.c
>>


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH v3 14/14] arm64: dts: mediatek: Get rid of mediatek, larb for MM nodes
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	ming-fan.chen, anan.sun, Robin Murphy, linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

After adding device_link between the IOMMU consumer and smi,
the mediatek,larb is unnecessary now.

CC: Matthias Brugger <matthias.bgg@gmail.com>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Reviewed-by: Evan Green <evgreen@chromium.org>
---
 arch/arm64/boot/dts/mediatek/mt8173.dtsi | 15 ---------------
 1 file changed, 15 deletions(-)

diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 15f1842..06e2c09 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -921,7 +921,6 @@
 				 <&mmsys CLK_MM_MUTEX_32K>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			iommus = <&iommu M4U_PORT_MDP_RDMA0>;
-			mediatek,larb = <&larb0>;
 			mediatek,vpu = <&vpu>;
 		};
 
@@ -932,7 +931,6 @@
 				 <&mmsys CLK_MM_MUTEX_32K>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			iommus = <&iommu M4U_PORT_MDP_RDMA1>;
-			mediatek,larb = <&larb4>;
 		};
 
 		mdp_rsz0: rsz@14003000 {
@@ -962,7 +960,6 @@
 			clocks = <&mmsys CLK_MM_MDP_WDMA>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			iommus = <&iommu M4U_PORT_MDP_WDMA>;
-			mediatek,larb = <&larb0>;
 		};
 
 		mdp_wrot0: wrot@14007000 {
@@ -971,7 +968,6 @@
 			clocks = <&mmsys CLK_MM_MDP_WROT0>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			iommus = <&iommu M4U_PORT_MDP_WROT0>;
-			mediatek,larb = <&larb0>;
 		};
 
 		mdp_wrot1: wrot@14008000 {
@@ -980,7 +976,6 @@
 			clocks = <&mmsys CLK_MM_MDP_WROT1>;
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			iommus = <&iommu M4U_PORT_MDP_WROT1>;
-			mediatek,larb = <&larb4>;
 		};
 
 		ovl0: ovl@1400c000 {
@@ -990,7 +985,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_OVL0>;
 			iommus = <&iommu M4U_PORT_DISP_OVL0>;
-			mediatek,larb = <&larb0>;
 		};
 
 		ovl1: ovl@1400d000 {
@@ -1000,7 +994,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_OVL1>;
 			iommus = <&iommu M4U_PORT_DISP_OVL1>;
-			mediatek,larb = <&larb4>;
 		};
 
 		rdma0: rdma@1400e000 {
@@ -1010,7 +1003,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_RDMA0>;
 			iommus = <&iommu M4U_PORT_DISP_RDMA0>;
-			mediatek,larb = <&larb0>;
 		};
 
 		rdma1: rdma@1400f000 {
@@ -1020,7 +1012,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_RDMA1>;
 			iommus = <&iommu M4U_PORT_DISP_RDMA1>;
-			mediatek,larb = <&larb4>;
 		};
 
 		rdma2: rdma@14010000 {
@@ -1030,7 +1021,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_RDMA2>;
 			iommus = <&iommu M4U_PORT_DISP_RDMA2>;
-			mediatek,larb = <&larb4>;
 		};
 
 		wdma0: wdma@14011000 {
@@ -1040,7 +1030,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_WDMA0>;
 			iommus = <&iommu M4U_PORT_DISP_WDMA0>;
-			mediatek,larb = <&larb0>;
 		};
 
 		wdma1: wdma@14012000 {
@@ -1050,7 +1039,6 @@
 			power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
 			clocks = <&mmsys CLK_MM_DISP_WDMA1>;
 			iommus = <&iommu M4U_PORT_DISP_WDMA1>;
-			mediatek,larb = <&larb4>;
 		};
 
 		color0: color@14013000 {
@@ -1294,7 +1282,6 @@
 			      <0 0x16027800 0 0x800>,	/* VDEC_HWB */
 			      <0 0x16028400 0 0x400>;	/* VDEC_HWG */
 			interrupts = <GIC_SPI 204 IRQ_TYPE_LEVEL_LOW>;
-			mediatek,larb = <&larb1>;
 			iommus = <&iommu M4U_PORT_HW_VDEC_MC_EXT>,
 				 <&iommu M4U_PORT_HW_VDEC_PP_EXT>,
 				 <&iommu M4U_PORT_HW_VDEC_AVC_MV_EXT>,
@@ -1364,8 +1351,6 @@
 			      <0 0x19002000 0 0x1000>;	/* VENC_LT_SYS */
 			interrupts = <GIC_SPI 198 IRQ_TYPE_LEVEL_LOW>,
 				     <GIC_SPI 202 IRQ_TYPE_LEVEL_LOW>;
-			mediatek,larb = <&larb3>,
-					<&larb5>;
 			iommus = <&iommu M4U_PORT_VENC_RCPU>,
 				 <&iommu M4U_PORT_VENC_REC>,
 				 <&iommu M4U_PORT_VENC_BSDMA>,
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 13/14] arm: dts: mediatek: Get rid of mediatek, larb for MM nodes
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	ming-fan.chen, anan.sun, Robin Murphy, linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

After adding device_link between the IOMMU consumer and smi,
the mediatek,larb is unnecessary now.

CC: Matthias Brugger <matthias.bgg@gmail.com>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Reviewed-by: Evan Green <evgreen@chromium.org>
---
 arch/arm/boot/dts/mt2701.dtsi | 1 -
 arch/arm/boot/dts/mt7623.dtsi | 1 -
 2 files changed, 2 deletions(-)

diff --git a/arch/arm/boot/dts/mt2701.dtsi b/arch/arm/boot/dts/mt2701.dtsi
index 51e1305..57b5de3 100644
--- a/arch/arm/boot/dts/mt2701.dtsi
+++ b/arch/arm/boot/dts/mt2701.dtsi
@@ -564,7 +564,6 @@
 		clock-names = "jpgdec-smi",
 			      "jpgdec";
 		power-domains = <&scpsys MT2701_POWER_DOMAIN_ISP>;
-		mediatek,larb = <&larb2>;
 		iommus = <&iommu MT2701_M4U_PORT_JPGDEC_WDMA>,
 			 <&iommu MT2701_M4U_PORT_JPGDEC_BSDMA>;
 	};
diff --git a/arch/arm/boot/dts/mt7623.dtsi b/arch/arm/boot/dts/mt7623.dtsi
index a79f0b6..cf22c58 100644
--- a/arch/arm/boot/dts/mt7623.dtsi
+++ b/arch/arm/boot/dts/mt7623.dtsi
@@ -783,7 +783,6 @@
 		clock-names = "jpgdec-smi",
 			      "jpgdec";
 		power-domains = <&scpsys MT2701_POWER_DOMAIN_ISP>;
-		mediatek,larb = <&larb2>;
 		iommus = <&iommu MT2701_M4U_PORT_JPGDEC_WDMA>,
 			 <&iommu MT2701_M4U_PORT_JPGDEC_BSDMA>;
 	};
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 12/14] drm/mediatek: Add pm runtime support for ovl and rdma
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	Yongqiang Niu, ming-fan.chen, anan.sun, Robin Murphy,
	linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

From: Yongqiang Niu <yongqiang.niu@mediatek.com>

Display use the dispsys device to call pm_rumtime_get_sync before.
This patch add pm_runtime_xx with ovl and rdma device which has linked
with larb0, then it will enable the correpsonding larb0 clock
automatically by the device link.

Signed-off-by: Yongqiang Niu <yongqiang.niu@mediatek.com>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
 drivers/gpu/drm/mediatek/mtk_disp_ovl.c     |  5 +++++
 drivers/gpu/drm/mediatek/mtk_disp_rdma.c    |  5 +++++
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 18 ++++++++++++++++--
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c |  9 +++++++++
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |  1 +
 5 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
index c4f07c2..51958cf 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c
@@ -9,6 +9,7 @@
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include "mtk_drm_crtc.h"
 #include "mtk_drm_ddp_comp.h"
@@ -300,6 +301,8 @@ static int mtk_disp_ovl_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	pm_runtime_enable(dev);
+
 	ret = component_add(dev, &mtk_disp_ovl_component_ops);
 	if (ret)
 		dev_err(dev, "Failed to add component: %d\n", ret);
@@ -311,6 +314,8 @@ static int mtk_disp_ovl_remove(struct platform_device *pdev)
 {
 	component_del(&pdev->dev, &mtk_disp_ovl_component_ops);
 
+	pm_runtime_disable(&pdev->dev);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
index 9a6f0a2..15e5c3a 100644
--- a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
+++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c
@@ -9,6 +9,7 @@
 #include <linux/of_device.h>
 #include <linux/of_irq.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include "mtk_drm_crtc.h"
 #include "mtk_drm_ddp_comp.h"
@@ -306,6 +307,8 @@ static int mtk_disp_rdma_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
+	pm_runtime_enable(dev);
+
 	ret = component_add(dev, &mtk_disp_rdma_component_ops);
 	if (ret)
 		dev_err(dev, "Failed to add component: %d\n", ret);
@@ -317,6 +320,8 @@ static int mtk_disp_rdma_remove(struct platform_device *pdev)
 {
 	component_del(&pdev->dev, &mtk_disp_rdma_component_ops);
 
+	pm_runtime_disable(&pdev->dev);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index c1e891e..daf002e 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -358,13 +358,21 @@ static void mtk_drm_crtc_atomic_enable(struct drm_crtc *crtc,
 				       struct drm_crtc_state *old_state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
+	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
 	int ret;
 
 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
 
+	ret = pm_runtime_get_sync(comp->dev);
+	if (ret < 0)
+		DRM_DEV_ERROR(comp->dev, "Failed to enable power domain: %d\n",
+			      ret);
+
 	ret = mtk_crtc_ddp_hw_init(mtk_crtc);
-	if (ret)
+	if (ret) {
+		pm_runtime_put(comp->dev);
 		return;
+	}
 
 	drm_crtc_vblank_on(crtc);
 	mtk_crtc->enabled = true;
@@ -374,7 +382,8 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
 					struct drm_crtc_state *old_state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
-	int i;
+	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
+	int i, ret;
 
 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
 	if (!mtk_crtc->enabled)
@@ -398,6 +407,11 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
 	mtk_crtc_ddp_hw_fini(mtk_crtc);
 
 	mtk_crtc->enabled = false;
+
+	ret = pm_runtime_put(comp->dev);
+	if (ret < 0)
+		DRM_DEV_ERROR(comp->dev, "Failed to disable power domain: %d\n",
+			      ret);
 }
 
 static void mtk_drm_crtc_atomic_begin(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index 7dc8496..c45e1f0 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -256,6 +256,8 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
 		      const struct mtk_ddp_comp_funcs *funcs)
 {
+	struct platform_device *comp_pdev;
+
 	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
 		return -EINVAL;
 
@@ -282,6 +284,13 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 	if (IS_ERR(comp->clk))
 		return PTR_ERR(comp->clk);
 
+	comp_pdev = of_find_device_by_node(node);
+	if (!comp_pdev) {
+		dev_err(dev, "Waiting for device %s\n", node->full_name);
+		return -EPROBE_DEFER;
+	}
+	comp->dev = &comp_pdev->dev;
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 108de60..d1838a8 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -83,6 +83,7 @@ struct mtk_ddp_comp {
 	struct clk *clk;
 	void __iomem *regs;
 	int irq;
+	struct device *dev;
 	enum mtk_ddp_comp_id id;
 	const struct mtk_ddp_comp_funcs *funcs;
 };
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 11/14] memory: mtk-smi: Use device_is_bound to check if smi-common is ready
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	ming-fan.chen, anan.sun, Robin Murphy, linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

smi-larb driver should run after smi-common, Use device_is_bound to confirm
whether smicommon driver is ready.

CC: Matthias Brugger <matthias.bgg@gmail.com>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
 drivers/memory/mtk-smi.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index 3df9036..789d2ab 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -296,8 +296,14 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
 	smi_pdev = of_find_device_by_node(smi_node);
 	of_node_put(smi_node);
 	if (smi_pdev) {
-		if (!platform_get_drvdata(smi_pdev))
+		bool smicommon_is_bound;
+
+		device_lock(&smi_pdev->dev);
+		smicommon_is_bound = device_is_bound(&smi_pdev->dev);
+		device_unlock(&smi_pdev->dev);
+		if (!smicommon_is_bound)
 			return -EPROBE_DEFER;
+
 		larb->smi_common_dev = &smi_pdev->dev;
 		link = device_link_add(dev, larb->smi_common_dev,
 				       DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS);
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 10/14] iommu/mediatek: Use builtin_platform_driver
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	ming-fan.chen, anan.sun, Robin Murphy, linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

MediaTek IOMMU should wait for smi larb which need wait for the
power domain(mtk-scpsys.c) and the multimedia ccf who both are
module init. Thus, subsys_initcall for MediaTek IOMMU is not helpful.
Switch to builtin_platform_driver.

Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
 drivers/iommu/mtk_iommu.c    | 31 +------------------------------
 drivers/iommu/mtk_iommu_v1.c | 24 +-----------------------
 2 files changed, 2 insertions(+), 53 deletions(-)

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 2511b3c..109c3f2 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -735,22 +735,6 @@ static int mtk_iommu_probe(struct platform_device *pdev)
 	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);
-
-	iommu_device_sysfs_remove(&data->iommu);
-	iommu_device_unregister(&data->iommu);
-
-	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);
@@ -831,23 +815,10 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
 
 static struct platform_driver mtk_iommu_driver = {
 	.probe	= mtk_iommu_probe,
-	.remove	= mtk_iommu_remove,
 	.driver	= {
 		.name = "mtk-iommu",
 		.of_match_table = of_match_ptr(mtk_iommu_of_ids),
 		.pm = &mtk_iommu_pm_ops,
 	}
 };
-
-static int __init mtk_iommu_init(void)
-{
-	int ret;
-
-	ret = platform_driver_register(&mtk_iommu_driver);
-	if (ret != 0)
-		pr_err("Failed to register MTK IOMMU driver\n");
-
-	return ret;
-}
-
-subsys_initcall(mtk_iommu_init)
+builtin_platform_driver(mtk_iommu_driver);
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index a7f22a2..821d483 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -660,22 +660,6 @@ static int mtk_iommu_probe(struct platform_device *pdev)
 	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);
-
-	iommu_device_sysfs_remove(&data->iommu);
-	iommu_device_unregister(&data->iommu);
-
-	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);
@@ -712,16 +696,10 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
 
 static struct platform_driver mtk_iommu_driver = {
 	.probe	= mtk_iommu_probe,
-	.remove	= mtk_iommu_remove,
 	.driver	= {
 		.name = "mtk-iommu-v1",
 		.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);
-}
-subsys_initcall(m4u_init);
+builtin_platform_driver(mtk_iommu_driver);
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 09/14] memory: mtk-smi: Get rid of mtk_smi_larb_get/put
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	ming-fan.chen, anan.sun, Robin Murphy, linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

After adding device_link between the iommu consumer and smi-larb,
the pm_runtime_get(_sync) of smi-larb and smi-common will be called
automatically. we can get rid of mtk_smi_larb_get/put.

CC: Matthias Brugger <matthias.bgg@gmail.com>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Reviewed-by: Evan Green <evgreen@chromium.org>
---
 drivers/memory/mtk-smi.c   | 14 --------------
 include/soc/mediatek/smi.h | 20 --------------------
 2 files changed, 34 deletions(-)

diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
index 5dab56c..3df9036 100644
--- a/drivers/memory/mtk-smi.c
+++ b/drivers/memory/mtk-smi.c
@@ -125,20 +125,6 @@ static void mtk_smi_clk_disable(const struct mtk_smi *smi)
 	clk_disable_unprepare(smi->clk_apb);
 }
 
-int mtk_smi_larb_get(struct device *larbdev)
-{
-	int ret = pm_runtime_get_sync(larbdev);
-
-	return (ret < 0) ? ret : 0;
-}
-EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
-
-void mtk_smi_larb_put(struct device *larbdev)
-{
-	pm_runtime_put_sync(larbdev);
-}
-EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
-
 static int
 mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
 {
diff --git a/include/soc/mediatek/smi.h b/include/soc/mediatek/smi.h
index 5a34b87..f8bf595 100644
--- a/include/soc/mediatek/smi.h
+++ b/include/soc/mediatek/smi.h
@@ -20,26 +20,6 @@ struct mtk_smi_larb_iommu {
 	unsigned int   mmu;
 };
 
-/*
- * mtk_smi_larb_get: Enable the power domain and clocks for this local arbiter.
- *                   It also initialize some basic setting(like iommu).
- * mtk_smi_larb_put: Disable the power domain and clocks for this local arbiter.
- * Both should be called in non-atomic context.
- *
- * Returns 0 if successful, negative on failure.
- */
-int mtk_smi_larb_get(struct device *larbdev);
-void mtk_smi_larb_put(struct device *larbdev);
-
-#else
-
-static inline int mtk_smi_larb_get(struct device *larbdev)
-{
-	return 0;
-}
-
-static inline void mtk_smi_larb_put(struct device *larbdev) { }
-
 #endif
 
 #endif
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related

* [PATCH v3 08/14] drm/mediatek: Get rid of mtk_smi_larb_get/put
From: Yong Wu @ 2019-09-03  9:37 UTC (permalink / raw)
  To: Matthias Brugger, Joerg Roedel, Rob Herring
  Cc: youlin.pei, devicetree, Nicolas Boichat, cui.zhang,
	srv_heupstream, chao.hao, Will Deacon, linux-kernel, Evan Green,
	Tomasz Figa, iommu, Matthias Kaehlcke, linux-mediatek, yong.wu,
	Philipp Zabel, CK Hu, ming-fan.chen, anan.sun, Robin Murphy,
	linux-arm-kernel
In-Reply-To: <1567503456-24725-1-git-send-email-yong.wu@mediatek.com>

MediaTek IOMMU has already added the device_link between the consumer
and smi-larb device. If the drm device call the pm_runtime_get_sync,
the smi-larb's pm_runtime_get_sync also be called automatically.

CC: CK Hu <ck.hu@mediatek.com>
CC: Philipp Zabel <p.zabel@pengutronix.de>
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
Reviewed-by: Evan Green <evgreen@chromium.org>
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c     | 14 +-------------
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 30 -----------------------------
 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h |  1 -
 3 files changed, 1 insertion(+), 44 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index a900721..c1e891e 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -10,7 +10,6 @@
 #include <drm/drm_probe_helper.h>
 #include <linux/clk.h>
 #include <linux/pm_runtime.h>
-#include <soc/mediatek/smi.h>
 
 #include "mtk_drm_drv.h"
 #include "mtk_drm_crtc.h"
@@ -359,22 +358,13 @@ static void mtk_drm_crtc_atomic_enable(struct drm_crtc *crtc,
 				       struct drm_crtc_state *old_state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
-	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
 	int ret;
 
 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
 
-	ret = mtk_smi_larb_get(comp->larb_dev);
-	if (ret) {
-		DRM_ERROR("Failed to get larb: %d\n", ret);
-		return;
-	}
-
 	ret = mtk_crtc_ddp_hw_init(mtk_crtc);
-	if (ret) {
-		mtk_smi_larb_put(comp->larb_dev);
+	if (ret)
 		return;
-	}
 
 	drm_crtc_vblank_on(crtc);
 	mtk_crtc->enabled = true;
@@ -384,7 +374,6 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
 					struct drm_crtc_state *old_state)
 {
 	struct mtk_drm_crtc *mtk_crtc = to_mtk_crtc(crtc);
-	struct mtk_ddp_comp *comp = mtk_crtc->ddp_comp[0];
 	int i;
 
 	DRM_DEBUG_DRIVER("%s %d\n", __func__, crtc->base.id);
@@ -407,7 +396,6 @@ static void mtk_drm_crtc_atomic_disable(struct drm_crtc *crtc,
 
 	drm_crtc_vblank_off(crtc);
 	mtk_crtc_ddp_hw_fini(mtk_crtc);
-	mtk_smi_larb_put(comp->larb_dev);
 
 	mtk_crtc->enabled = false;
 }
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
index b38963f..7dc8496 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c
@@ -256,15 +256,9 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 		      struct mtk_ddp_comp *comp, enum mtk_ddp_comp_id comp_id,
 		      const struct mtk_ddp_comp_funcs *funcs)
 {
-	enum mtk_ddp_comp_type type;
-	struct device_node *larb_node;
-	struct platform_device *larb_pdev;
-
 	if (comp_id < 0 || comp_id >= DDP_COMPONENT_ID_MAX)
 		return -EINVAL;
 
-	type = mtk_ddp_matches[comp_id].type;
-
 	comp->id = comp_id;
 	comp->funcs = funcs ?: mtk_ddp_matches[comp_id].funcs;
 
@@ -288,30 +282,6 @@ int mtk_ddp_comp_init(struct device *dev, struct device_node *node,
 	if (IS_ERR(comp->clk))
 		return PTR_ERR(comp->clk);
 
-	/* Only DMA capable components need the LARB property */
-	comp->larb_dev = NULL;
-	if (type != MTK_DISP_OVL &&
-	    type != MTK_DISP_RDMA &&
-	    type != MTK_DISP_WDMA)
-		return 0;
-
-	larb_node = of_parse_phandle(node, "mediatek,larb", 0);
-	if (!larb_node) {
-		dev_err(dev,
-			"Missing mediadek,larb phandle in %pOF node\n", node);
-		return -EINVAL;
-	}
-
-	larb_pdev = of_find_device_by_node(larb_node);
-	if (!larb_pdev) {
-		dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
-		of_node_put(larb_node);
-		return -EPROBE_DEFER;
-	}
-	of_node_put(larb_node);
-
-	comp->larb_dev = &larb_pdev->dev;
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
index 0ad287f..108de60 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
+++ b/drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h
@@ -83,7 +83,6 @@ struct mtk_ddp_comp {
 	struct clk *clk;
 	void __iomem *regs;
 	int irq;
-	struct device *larb_dev;
 	enum mtk_ddp_comp_id id;
 	const struct mtk_ddp_comp_funcs *funcs;
 };
-- 
1.9.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related


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