* Re: [PATCH v2 00/91] drm/vc4: Support BCM2711 Display Pipelin
From: Jian-Hong Pan @ 2020-06-01 7:58 UTC (permalink / raw)
To: Maxime Ripard
Cc: linux-arm-kernel, devicetree, Linux Kernel, dri-devel,
Daniel Drake, Eric Anholt, bcm-kernel-feedback-list,
linux-rpi-kernel, Linux Upstreaming Team, linux-clk,
Nicolas Saenz Julienne, linux-i2c
In-Reply-To: <20200528073055.znutrhkryzu3grrl@gilmour.lan>
Maxime Ripard <maxime@cerno.tech> 於 2020年5月28日 週四 下午3:30寫道:
>
> Hi Daniel,
>
> On Wed, May 27, 2020 at 05:15:12PM +0800, Daniel Drake wrote:
> > On Wed, May 27, 2020 at 5:13 PM Maxime Ripard <maxime@cerno.tech> wrote:
> > > I'm about to send a v3 today or tomorrow, I can Cc you (and Jian-Hong) if you
> > > want.
> >
> > That would be great, although given the potentially inconsistent
> > results we've been seeing so far it would be great if you could
> > additionally push a git branch somewhere.
> > That way we can have higher confidence that we are applying exactly
> > the same patches to the same base etc.
>
> So I sent a new iteration yesterday, and of course forgot to cc you... Sorry for
> that.
>
> I've pushed my current branch here:
> https://git.kernel.org/pub/scm/linux/kernel/git/mripard/linux.git/log/?h=rpi4-kms
Thanks to Maxime!
I have tried your repository on branch rpi4-kms. The DRM VC4 is used!
But got some issues:
1. Some weird error message in dmesg. Not sure it is related, or not
[ 5.219321] [drm:vc5_hdmi_init_resources] *ERROR* Failed to get
HDMI state machine clock
https://gist.github.com/starnight/3f317dca121065a361cf08e91225e389
2. The screen flashes suddenly sometimes.
3. The higher resolutions, like 1920x1080 ... are lost after hot
re-plug HDMI cable (HDMI0)
Jian-Hong Pan
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 1/1] mfd: mt6360: Remove duplicate REGMAP_IRQ_REG_LINE() entry
From: Lee Jones @ 2020-06-01 7:57 UTC (permalink / raw)
To: gene_chen; +Cc: Lee Jones, linux-kernel, linux-arm-kernel, kbuild test robot
Fixes the following build warning:
>> drivers/mfd/mt6360-core.c:148:2: warning: initializer overrides prior initialization of this subobject [-Winitializer-overrides]
REGMAP_IRQ_REG_LINE(MT6360_CHG_TREG_EVT, 8),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/regmap.h:1191:10: note: expanded from macro 'REGMAP_IRQ_REG_LINE'
[_id] = { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/mfd/mt6360-core.c:124:2: note: previous initialization is here
REGMAP_IRQ_REG_LINE(MT6360_CHG_TREG_EVT, 8),
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/linux/regmap.h:1191:10: note: expanded from macro 'REGMAP_IRQ_REG_LINE'
[_id] = { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
drivers/mfd/mt6360-core.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/mfd/mt6360-core.c b/drivers/mfd/mt6360-core.c
index 9bb63e0b69e6a..db8cdf5272c1f 100644
--- a/drivers/mfd/mt6360-core.c
+++ b/drivers/mfd/mt6360-core.c
@@ -145,7 +145,6 @@ static const struct regmap_irq mt6360_pmu_irqs[] = {
REGMAP_IRQ_REG_LINE(MT6360_CHG_TERMI, 8),
REGMAP_IRQ_REG_LINE(MT6360_CHG_IEOCI, 8),
REGMAP_IRQ_REG_LINE(MT6360_PUMPX_DONEI, 8),
- REGMAP_IRQ_REG_LINE(MT6360_CHG_TREG_EVT, 8),
REGMAP_IRQ_REG_LINE(MT6360_BAT_OVP_ADC_EVT, 8),
REGMAP_IRQ_REG_LINE(MT6360_TYPEC_OTP_EVT, 8),
REGMAP_IRQ_REG_LINE(MT6360_ADC_WAKEUP_EVT, 8),
--
2.25.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: [RFC PATCH v5 6/6] drm: exynos: mixer: Add interconnect support
From: Chanwoo Choi @ 2020-06-01 7:58 UTC (permalink / raw)
To: Sylwester Nawrocki, georgi.djakov, krzk
Cc: linux-samsung-soc, b.zolnierkie, sw0312.kim, a.swigon, dri-devel,
linux-kernel, inki.dae, myungjoo.ham, linux-arm-kernel,
m.szyprowski
In-Reply-To: <20200529163200.18031-7-s.nawrocki@samsung.com>
Hi Sylwester,
On 5/30/20 1:32 AM, Sylwester Nawrocki wrote:
> From: Marek Szyprowski <m.szyprowski@samsung.com>
>
> This patch adds interconnect support to exynos-mixer. The mixer works
> the same as before when CONFIG_INTERCONNECT is 'n'.
>
> For proper operation of the video mixer block we need to ensure the
> interconnect busses like DMC or LEFTBUS provide enough bandwidth so
> as to avoid DMA buffer underruns in the mixer block. i.e we need to
> prevent those busses from operating in low perfomance OPPs when
> the mixer is running.
> In this patch the bus bandwidth request is done through the interconnect
> API, the bandiwidth value is calculated from selected DRM mode, i.e.
> video plane width, height, refresh rate and pixel format.
>
> Co-developed-by: Artur Świgoń <a.swigon@samsung.com>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> [s.nawrocki: renamed soc_path variable to icc_path, edited commit desc.]
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
> ---
> Changes for v5:
> - renamed soc_path variable to icc_path
> ---
> drivers/gpu/drm/exynos/exynos_mixer.c | 73 ++++++++++++++++++++++++++++++++---
> 1 file changed, 68 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
> index 21b726b..bdae683 100644
> --- a/drivers/gpu/drm/exynos/exynos_mixer.c
> +++ b/drivers/gpu/drm/exynos/exynos_mixer.c
> @@ -13,6 +13,7 @@
> #include <linux/component.h>
> #include <linux/delay.h>
> #include <linux/i2c.h>
> +#include <linux/interconnect.h>
> #include <linux/interrupt.h>
> #include <linux/irq.h>
> #include <linux/kernel.h>
> @@ -98,6 +99,7 @@ struct mixer_context {
> struct exynos_drm_crtc *crtc;
> struct exynos_drm_plane planes[MIXER_WIN_NR];
> unsigned long flags;
> + struct icc_path *icc_path;
>
> int irq;
> void __iomem *mixer_regs;
> @@ -934,6 +936,42 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc)
> mixer_reg_writemask(mixer_ctx, MXR_INT_EN, 0, MXR_INT_EN_VSYNC);
> }
>
> +static void mixer_set_memory_bandwidth(struct exynos_drm_crtc *crtc)
> +{
> + struct drm_display_mode *mode = &crtc->base.state->adjusted_mode;
> + struct mixer_context *ctx = crtc->ctx;
> + unsigned long bw, bandwidth = 0;
> + u32 avg_bw, peak_bw;
> + int i, j, sub;
> +
> + if (!ctx->icc_path)
> + return;
> +
> + for (i = 0; i < MIXER_WIN_NR; i++) {
> + struct drm_plane *plane = &ctx->planes[i].base;
> + const struct drm_format_info *format;
> +
> + if (plane->state && plane->state->crtc && plane->state->fb) {
> + format = plane->state->fb->format;
> + bw = mode->hdisplay * mode->vdisplay *
> + drm_mode_vrefresh(mode);
> + if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> + bw /= 2;
> + for (j = 0; j < format->num_planes; j++) {
> + sub = j ? (format->vsub * format->hsub) : 1;
> + bandwidth += format->cpp[j] * bw / sub;
First of all, I agree this approach.
Could you please add more detailed comments for understadning
about this calculation? As you commented, it seems that
the final bandwidth contains the width/height/refresh rate
and pixel format. If you add one real example, it will be very helpful.
(snip)
--
Best Regards,
Chanwoo Choi
Samsung Electronics
_______________________________________________
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 V2 2/3] arm64: dts: imx8m: add mu node
From: Aisheng Dong @ 2020-06-01 7:46 UTC (permalink / raw)
To: Peng Fan, shawnguo@kernel.org, Fabio Estevam,
kernel@pengutronix.de, robh+dt@kernel.org, sboyd@kernel.org,
linux@rempel-privat.de, jaswinder.singh@linaro.org
Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
dl-linux-imx, Leonard Crestez, Daniel Baluta,
linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
l.stach@pengutronix.de
In-Reply-To: <1590982999-7149-3-git-send-email-peng.fan@nxp.com>
> From: Peng Fan <peng.fan@nxp.com>
> Sent: Monday, June 1, 2020 11:43 AM
>
> Add mu node to let A53 could communicate with M Core.
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
> ---
> arch/arm64/boot/dts/freescale/imx8mm.dtsi | 9 +++++++++
> arch/arm64/boot/dts/freescale/imx8mn.dtsi | 9 +++++++++
> arch/arm64/boot/dts/freescale/imx8mp.dtsi | 9 +++++++++
> arch/arm64/boot/dts/freescale/imx8mq.dtsi | 9 +++++++++
> 4 files changed, 36 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> index aaf6e71101a1..fc001fb971e9 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi
> @@ -775,6 +775,15 @@
> status = "disabled";
> };
>
> + mu: mailbox@30aa0000 {
> + compatible = "fsl,imx8mm-mu", "fsl,imx6sx-mu";
> + reg = <0x30aa0000 0x10000>;
> + interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&clk IMX8MM_CLK_MU_ROOT>;
> + clock-names = "mu";
You missed my comments about this unneeded line in the last round of review.
https://lore.kernel.org/patchwork/patch/1244752/
Regards
Aisheng
> + #mbox-cells = <2>;
> + };
> +
> usdhc1: mmc@30b40000 {
> compatible = "fsl,imx8mm-usdhc", "fsl,imx7d-usdhc";
> reg = <0x30b40000 0x10000>;
> diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> b/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> index 9a4b65a267d4..c8290d21ccc9 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi
> @@ -675,6 +675,15 @@
> status = "disabled";
> };
>
> + mu: mailbox@30aa0000 {
> + compatible = "fsl,imx8mn-mu", "fsl,imx6sx-mu";
> + reg = <0x30aa0000 0x10000>;
> + interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&clk IMX8MN_CLK_MU_ROOT>;
> + clock-names = "mu";
> + #mbox-cells = <2>;
> + };
> +
> usdhc1: mmc@30b40000 {
> compatible = "fsl,imx8mn-usdhc", "fsl,imx7d-usdhc";
> reg = <0x30b40000 0x10000>;
> diff --git a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> index 45e2c0a4e889..b530804f763e 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mp.dtsi
> @@ -621,6 +621,15 @@
> status = "disabled";
> };
>
> + mu: mailbox@30aa0000 {
> + compatible = "fsl,imx8mp-mu", "fsl,imx6sx-mu";
> + reg = <0x30aa0000 0x10000>;
> + interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&clk IMX8MP_CLK_MU_ROOT>;
> + clock-names = "mu";
> + #mbox-cells = <2>;
> + };
> +
> i2c5: i2c@30ad0000 {
> compatible = "fsl,imx8mp-i2c", "fsl,imx21-i2c";
> #address-cells = <1>;
> diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> b/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> index 978f8122c0d2..66ba8da704f6 100644
> --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi
> @@ -959,6 +959,15 @@
> status = "disabled";
> };
>
> + mu: mailbox@30aa0000 {
> + compatible = "fsl,imx8mq-mu", "fsl,imx6sx-mu";
> + reg = <0x30aa0000 0x10000>;
> + interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&clk IMX8MQ_CLK_MU_ROOT>;
> + clock-names = "mu";
> + #mbox-cells = <2>;
> + };
> +
> usdhc1: mmc@30b40000 {
> compatible = "fsl,imx8mq-usdhc",
> "fsl,imx7d-usdhc";
> --
> 2.16.4
_______________________________________________
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 V2 1/3] dt-bindings: mailbox: imx-mu: support i.MX8M
From: Aisheng Dong @ 2020-06-01 7:43 UTC (permalink / raw)
To: Peng Fan, shawnguo@kernel.org, Fabio Estevam,
kernel@pengutronix.de, robh+dt@kernel.org, sboyd@kernel.org,
linux@rempel-privat.de, jaswinder.singh@linaro.org, Anson Huang
Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
dl-linux-imx, Leonard Crestez, Daniel Baluta,
linux-clk@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
l.stach@pengutronix.de
In-Reply-To: <1590982999-7149-2-git-send-email-peng.fan@nxp.com>
> From: Peng Fan <peng.fan@nxp.com>
> Sent: Monday, June 1, 2020 11:43 AM
>
> Add i.MX8MQ/M/N/P compatible string to support i.MX8M SoCs
>
> Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
BTW, Anson,
will you continue to help convert MU binding into json schemas?
Regards
Aisheng
_______________________________________________
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][v2] iommu: arm-smmu-v3: Copy SMMU table for kdump kernel
From: Will Deacon @ 2020-06-01 7:39 UTC (permalink / raw)
To: Prabhakar Kushwaha
Cc: Ganapatrao Prabhakerrao Kulkarni, Marc Zyngier, Bhupesh Sharma,
kexec mailing list, Bjorn Helgaas, Prabhakar Kushwaha,
Robin Murphy, linux-arm-kernel
In-Reply-To: <CAJ2QiJK4h=5abVdODWTYg8Loy0Hhnhm1HrBzAM3bZXKtGXYK+Q@mail.gmail.com>
On Thu, May 21, 2020 at 04:52:02PM +0530, Prabhakar Kushwaha wrote:
> On Thu, May 21, 2020 at 2:53 PM Will Deacon <will@kernel.org> wrote:
> >
> > On Tue, May 19, 2020 at 08:24:21AM +0530, Prabhakar Kushwaha wrote:
> > > On Mon, May 18, 2020 at 9:25 PM Will Deacon <will@kernel.org> wrote:
> > > > On Mon, May 11, 2020 at 07:46:06PM -0700, Prabhakar Kushwaha wrote:
> > > > > @@ -3272,6 +3281,23 @@ static int arm_smmu_init_l1_strtab(struct arm_smmu_device *smmu)
> > > > > return 0;
> > > > > }
> > > > >
> > > > > +static void arm_smmu_copy_table(struct arm_smmu_device *smmu,
> > > > > + struct arm_smmu_strtab_cfg *cfg, u32 size)
> > > > > +{
> > > > > + struct arm_smmu_strtab_cfg rdcfg;
> > > > > +
> > > > > + rdcfg.strtab_dma = readq_relaxed(smmu->base + ARM_SMMU_STRTAB_BASE);
> > > > > + rdcfg.strtab_base_cfg = readq_relaxed(smmu->base
> > > > > + + ARM_SMMU_STRTAB_BASE_CFG);
> > > > > +
> > > > > + rdcfg.strtab_dma &= STRTAB_BASE_ADDR_MASK;
> > > > > + rdcfg.strtab = memremap(rdcfg.strtab_dma, size, MEMREMAP_WB);
> > > > > +
> > > > > + memcpy_fromio(cfg->strtab, rdcfg.strtab, size);
> > > > > +
> > >
> > > this need a fix. It should be memcpy.
> > >
> > > > > + cfg->strtab_base_cfg = rdcfg.strtab_base_cfg;
> > > >
> > > > Sorry, but this is unacceptable. These things were allocated by the DMA API
> > > > so you can't just memcpy them around and hope for the best.
> > > >
> > >
> > > I was referring copy_context_table() in drivers/iommu/intel-iommu.c.
> > > here i see usage of memremap and memcpy to copy older iommu table.
> > > did I take wrong reference?
> > >
> > > What kind of issue you are foreseeing in using memcpy(). May be we can
> > > try to find a solution.
> >
> > Well the thing might not be cache-coherent to start with...
> >
>
> Thanks for telling possible issue area. Let me try to explain why
> this should not be an issue.
>
> kdump kernel runs from reserved memory space defined during the boot
> of first kernel. kdump does not touch memory of the previous kernel.
> So no page has been created in kdump kernel and there should not be
> any data/attribute/coherency issue from MMU point of view .
Then how does this work?:
rdcfg.strtab = memremap(rdcfg.strtab_dma, size, MEMREMAP_WB);
You're explicitly asking for a write-back mapping.
> During SMMU probe functions, dmem_alloc_coherent() will be used
> allocate new memory (part of existing flow).
> This patch copy STE or first level descriptor to *this* memory, after
> mapping physical address using memremap().
> It just copy everything so there should not be any issue related to
> attribute/content.
>
> Yes, copying done after mapping it as MEMREMAP_WB. if you want I can
> use it as MEMREMAP_WT
You need to take into account whether or not the device is coherent, and the
DMA API is designed to handle that for you. But even then, this is fragile
as hell because you end up having to infer the hardware configuration
from the device to understand the size and format of the data structures.
If the crashkernel isn't identical to the host kernel (in terms of kconfig,
driver version, firmware tables, cmdline etc) then this is very likely to
go wrong.
That's why I think that you need to reinitialise any devices that want to
do DMA.
Will
_______________________________________________
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 v1 1/2] scsi: ufs: Support WriteBooster on Samsung UFS devices
From: Stanley Chu @ 2020-06-01 7:25 UTC (permalink / raw)
To: Avri Altman
Cc: linux-scsi@vger.kernel.org, martin.petersen@oracle.com,
andy.teng@mediatek.com, jejb@linux.ibm.com,
chun-hung.wu@mediatek.com, kuohong.wang@mediatek.com,
linux-kernel@vger.kernel.org, cc.chou@mediatek.com,
cang@codeaurora.org, linux-mediatek@lists.infradead.org,
peter.wang@mediatek.com, alim.akhtar@samsung.com,
matthias.bgg@gmail.com, beanhuo@micron.com,
chaotian.jing@mediatek.com, bvanassche@acm.org,
linux-arm-kernel@lists.infradead.org, asutoshd@codeaurora.org
In-Reply-To: <SN6PR04MB46400873245235EA56838A19FC8C0@SN6PR04MB4640.namprd04.prod.outlook.com>
Hi Avri,
On Sat, 2020-05-30 at 20:37 +0000, Avri Altman wrote:
> > @@ -2801,11 +2801,17 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum
> > query_opcode opcode,
> > {
> > struct ufs_query_req *request = NULL;
> > struct ufs_query_res *response = NULL;
> > - int err, selector = 0;
> > + int err;
> > int timeout = QUERY_REQ_TIMEOUT;
> > + u8 selector = 0;
> >
> > BUG_ON(!hba);
> >
> > + if (hba->dev_quirks & UFS_DEVICE_QUIRK_WB_SPECIAL_SELECTOR) {
> > + if (ufshcd_is_wb_flags(idn))
> > + selector = 1;
> > + }
> > +
> Why not make the caller set the applicable selector,
> Instead of checking this for every flag?
This way have the minimum modification efforts and places compared to
other ways. However it looks a little wired because the selector control
is better assigned by users. I will submit next version with changing
the way selector assigned for comparison.
>
> > ufshcd_hold(hba, false);
> > mutex_lock(&hba->dev_cmd.lock);
> > ufshcd_init_query(hba, &request, &response, opcode, idn, index,
> > @@ -2882,6 +2888,11 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum
> > query_opcode opcode,
> > goto out;
> > }
> >
> > + if (hba->dev_quirks & UFS_DEVICE_QUIRK_WB_SPECIAL_SELECTOR) {
> > + if (ufshcd_is_wb_attrs(idn))
> > + selector = 1;
> > + }
> > +
> Same here
>
> > mutex_lock(&hba->dev_cmd.lock);
> > ufshcd_init_query(hba, &request, &response, opcode, idn, index,
> > selector);
> > @@ -3042,6 +3053,11 @@ int ufshcd_query_descriptor_retry(struct ufs_hba
> > *hba,
> > int err;
> > int retries;
> >
> > + if (hba->dev_quirks & UFS_DEVICE_QUIRK_WB_SPECIAL_SELECTOR) {
> > + if (ufshcd_is_wb_desc(idn, index))
> > + selector = 1;
> > + }
> > +
> And here.
> But this can't be true -
> Are you setting the selector = 1 for reading any field for those descriptors?
> Shouldn't it be for the wb specific fields?
Yes, thanks for remind this.
I shall assign selector = 1 for WB related fields only in descriptors.
>
>
> > for (retries = QUERY_REQ_RETRIES; retries > 0; retries--) {
> > err = __ufshcd_query_descriptor(hba, opcode, idn, index,
> > selector, desc_buf, buf_len);
> > @@ -6907,8 +6923,10 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
> > size_t buff_len;
> > u8 model_index;
> > u8 *desc_buf;
> > + u8 retry_cnt = 0;
> > struct ufs_dev_info *dev_info = &hba->dev_info;
> >
> > +retry:
> > buff_len = max_t(size_t, hba->desc_size.dev_desc,
> > QUERY_DESC_MAX_SIZE + 1);
> > desc_buf = kmalloc(buff_len, GFP_KERNEL);
> > @@ -6948,6 +6966,29 @@ static int ufs_get_device_desc(struct ufs_hba *hba)
> >
> > ufs_fixup_device_setup(hba);
> >
> > + if (!retry_cnt && (hba->dev_quirks &
> > + UFS_DEVICE_QUIRK_WB_SPECIAL_SELECTOR)) {
> If you only want to enter this clause once - you should use something other than retry_cnt,
> Which the reader expects to performs retries....
OK! I will fix this label by using another more comprehensible name.
>
> Also, this is becoming too wired -
> From your commit log I get that for specific Samsung devices,
> You need to query wb descriptor fields/attributes/flags using selectore = 1.
> But what it has to do with descriptor sizes?
Sorry to not mention clearly in the commit log.
Here driver needs to update the descriptor size to a "longer size" which
includes the "hidden WB related fields" which can be "found" by selector
= 1.
If descriptor size is not updated, any query can only get the fields
offset within current descriptor size even if selector = 1, and
out-of-boundary desc_buf[] access will happen in
ufshcd_read_desc_param().
PS. The check of "param_offset" to prevent possible out-of-boundary
desc_buf[] access can be patched as well.
Thanks,
Stanley Chu
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: kdump: Getting "warn_alloc" warning during boot of kdump kernel
From: Will Deacon @ 2020-06-01 7:34 UTC (permalink / raw)
To: Prabhakar Kushwaha
Cc: Ganapatrao Prabhakerrao Kulkarni, Catalin Marinas,
kexec mailing list, Kamlakant Patel, Prabhakar Kushwaha,
linux-arm-kernel
In-Reply-To: <CAJ2QiJJgw0Cm=XBeVvOJ8WnWB0Xfv3JEYKTQUovnwrrDw17w9g@mail.gmail.com>
On Fri, May 15, 2020 at 04:28:13PM +0530, Prabhakar Kushwaha wrote:
> We are getting "warn_alloc" warning during boot of kdump kernel. This
> warning is observed with latest upstream tag (v5.7-rc5).
Perhaps you can help to review:
https://lore.kernel.org/r/20200521093805.64398-1-chenzhou10@huawei.com
Since it looks like it should solve your problem.
Will
_______________________________________________
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 v8 2/3] perf auxtrace: Add four itrace options
From: Adrian Hunter @ 2020-06-01 7:24 UTC (permalink / raw)
To: Leo Yan, Arnaldo Carvalho de Melo, Jiri Olsa, Mark Rutland,
Alexander Shishkin, Will Deacon, James Clark, Peter Zijlstra,
Ingo Molnar, Namhyung Kim, Andi Kleen, Jin Yao, Ian Rogers,
Thomas Gleixner, linux-kernel, linux-arm-kernel, Al Grant,
Mathieu Poirier, Mike Leach
Cc: Tan Xiaojun
In-Reply-To: <20200530122442.490-3-leo.yan@linaro.org>
On 30/05/20 3:24 pm, Leo Yan wrote:
> From: Tan Xiaojun <tanxiaojun@huawei.com>
>
> This patch is to add four options to synthesize events which are
> described as below:
>
> 'f': synthesize first level cache events
> 'm': synthesize last level cache events
> 't': synthesize TLB events
> 'a': synthesize remote access events
>
> This four options will be used by ARM SPE as their first consumer.
>
> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
> Signed-off-by: James Clark <james.clark@arm.com>
> Signed-off-by: Leo Yan <leo.yan@linaro.org>
> Tested-by: James Clark <james.clark@arm.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> tools/perf/Documentation/itrace.txt | 6 +++++-
> tools/perf/util/auxtrace.c | 17 +++++++++++++++++
> tools/perf/util/auxtrace.h | 15 ++++++++++++++-
> 3 files changed, 36 insertions(+), 2 deletions(-)
>
> diff --git a/tools/perf/Documentation/itrace.txt b/tools/perf/Documentation/itrace.txt
> index 271484754fee..e817179c5027 100644
> --- a/tools/perf/Documentation/itrace.txt
> +++ b/tools/perf/Documentation/itrace.txt
> @@ -1,5 +1,5 @@
> i synthesize instructions events
> - b synthesize branches events
> + b synthesize branches events (branch misses for Arm SPE)
> c synthesize branches events (calls only)
> r synthesize branches events (returns only)
> x synthesize transactions events
> @@ -9,6 +9,10 @@
> of aux-output (refer to perf record)
> e synthesize error events
> d create a debug log
> + f synthesize first level cache events
> + m synthesize last level cache events
> + t synthesize TLB events
> + a synthesize remote access events
> g synthesize a call chain (use with i or x)
> G synthesize a call chain on existing event records
> l synthesize last branch entries (use with i or x)
> diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c
> index 8cf7d405ee67..fe76a056a179 100644
> --- a/tools/perf/util/auxtrace.c
> +++ b/tools/perf/util/auxtrace.c
> @@ -1331,6 +1331,11 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts,
> synth_opts->pwr_events = true;
> synth_opts->other_events = true;
> synth_opts->errors = true;
> + synth_opts->flc = true;
> + synth_opts->llc = true;
> + synth_opts->tlb = true;
> + synth_opts->remote_access = true;
> +
> if (no_sample) {
> synth_opts->period_type = PERF_ITRACE_PERIOD_INSTRUCTIONS;
> synth_opts->period = 1;
> @@ -1491,6 +1496,18 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
> goto out_err;
> p = endptr;
> break;
> + case 'f':
> + synth_opts->flc = true;
> + break;
> + case 'm':
> + synth_opts->llc = true;
> + break;
> + case 't':
> + synth_opts->tlb = true;
> + break;
> + case 'a':
> + synth_opts->remote_access = true;
> + break;
> case ' ':
> case ',':
> break;
> diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h
> index 0220a2e86c16..142ccf7d34df 100644
> --- a/tools/perf/util/auxtrace.h
> +++ b/tools/perf/util/auxtrace.h
> @@ -63,6 +63,7 @@ enum itrace_period_type {
> * because 'perf inject' will write it out
> * @instructions: whether to synthesize 'instructions' events
> * @branches: whether to synthesize 'branches' events
> + * (branch misses only for Arm SPE)
> * @transactions: whether to synthesize events for transactions
> * @ptwrites: whether to synthesize events for ptwrites
> * @pwr_events: whether to synthesize power events
> @@ -78,6 +79,10 @@ enum itrace_period_type {
> * @thread_stack: feed branches to the thread_stack
> * @last_branch: add branch context to 'instruction' events
> * @add_last_branch: add branch context to existing event records
> + * @flc: whether to synthesize first level cache events
> + * @llc: whether to synthesize last level cache events
> + * @tlb: whether to synthesize TLB events
> + * @remote_access: whether to synthesize remote access events
> * @callchain_sz: maximum callchain size
> * @last_branch_sz: branch context size
> * @period: 'instructions' events period
> @@ -107,6 +112,10 @@ struct itrace_synth_opts {
> bool thread_stack;
> bool last_branch;
> bool add_last_branch;
> + bool flc;
> + bool llc;
> + bool tlb;
> + bool remote_access;
> unsigned int callchain_sz;
> unsigned int last_branch_sz;
> unsigned long long period;
> @@ -596,7 +605,7 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
>
> #define ITRACE_HELP \
> " i: synthesize instructions events\n" \
> -" b: synthesize branches events\n" \
> +" b: synthesize branches events (branch misses for Arm SPE)\n" \
> " c: synthesize branches events (calls only)\n" \
> " r: synthesize branches events (returns only)\n" \
> " x: synthesize transactions events\n" \
> @@ -604,6 +613,10 @@ bool auxtrace__evsel_is_auxtrace(struct perf_session *session,
> " p: synthesize power events\n" \
> " e: synthesize error events\n" \
> " d: create a debug log\n" \
> +" f: synthesize first level cache events\n" \
> +" m: synthesize last level cache events\n" \
> +" t: synthesize TLB events\n" \
> +" a: synthesize remote access events\n" \
> " g[len]: synthesize a call chain (use with i or x)\n" \
> " l[len]: synthesize last branch entries (use with i or x)\n" \
> " sNUMBER: skip initial number of events\n" \
>
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: arm64/acpi: NULL dereference reports from UBSAN at boot
From: Will Deacon @ 2020-06-01 7:05 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: mark.rutland, rjw, ndesaulniers, linux-kernel, guohanjun,
linux-arm-kernel
In-Reply-To: <20200527134104.GA16115@e121166-lin.cambridge.arm.com>
On Wed, May 27, 2020 at 02:41:04PM +0100, Lorenzo Pieralisi wrote:
> On Tue, May 26, 2020 at 09:21:57PM +0100, Will Deacon wrote:
> > Hi Lorenzo, Hanjun, [+Nick]
> >
> > On Thu, May 21, 2020 at 06:37:38PM +0100, Lorenzo Pieralisi wrote:
> > > On Thu, May 21, 2020 at 11:09:53AM +0100, Will Deacon wrote:
> > > > Hi folks,
> > > >
> > > > I just tried booting the arm64 for-kernelci branch under QEMU (version
> > > > 4.2.50 (v4.2.0-779-g4354edb6dcc7)) with UBSAN enabled, and I see a
> > > > couple of NULL pointer dereferences reported at boot. I think they're
> > > > both GIC related (log below). I don't see a panic with UBSAN disabled,
> > > > so something's fishy here.
> > >
> > > May I ask you the QEMU command line please - just to make sure I can
> > > replicate it.
> >
> > As it turns out, I'm only able to reproduce this when building with Clang,
> > but I don't know whether that's because GCC is missing something of Clang
> > is signalling a false positive. You also don't need all of those whacky
> > fuzzing options enabled.
> >
> > Anyway, to reproduce:
> >
> > $ git checkout for-next/kernelci
> > $ make ARCH=arm64 CC=clang CROSS_COMPILE=aarch64-linux-gnu- defconfig
> > <then do a menuconfig and enable UBSAN>
> > $ make ARCH=arm64 CC=clang CROSS_COMPILE=aarch64-linux-gnu- Image
> >
> > I throw that at QEMU using:
> >
> > qemu-system-aarch64 -M virt -machine virtualization=true \
> > -machine virt,gic-version=3 \
> > -cpu max,sve=off -smp 2 -m 4096 \
> > -drive if=pflash,format=raw,file=efi.img,readonly \
> > -drive if=pflash,format=raw,file=varstore.img \
> > -drive if=virtio,format=raw,file=disk.img \
> > -device virtio-scsi-pci,id=scsi0 \
> > -device virtio-rng-pci \
> > -device virtio-net-pci,netdev=net0 \
> > -netdev user,id=net0,hostfwd=tcp::8222-:22 \
> > -nographic \
> > -kernel ~/work/linux/arch/arm64/boot/Image \
> > -append "earlycon root=/dev/vda2"
> >
> > I built QEMU a while ago according to:
> >
> > https://mirrors.edge.kernel.org/pub/linux/kernel/people/will/docs/qemu/qemu-arm64-howto.html
> >
> > and its version 4.2.50 (v4.2.0-779-g4354edb6dcc7).
> >
> > My clang is version 11.0.1.
>
> Thanks a lot Will.
>
> I *think* I was right - it is the ACPI_OFFSET() macro:
>
> #define ACPI_OFFSET(d, f) ACPI_PTR_DIFF (&(((d *) 0)->f), (void *) 0)
>
> that triggers the warnings (I suspected it because at least in one of
> the warnings I could not see any dereference of any dynamically
> allocated data).
Cheers, Lorenzo.
> Now on what to do with it - thoughts welcome.
Nick -- any idea what to do about the above? The '#define' pasted by
Lorenzo is causing a couple of spurious UBSAN splats when compiling with
clang 11.
Cheers,
Will
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: Please help to confirm the risk if using TPIDRRO_EL0 to save CPU number, thanks.
From: Will Deacon @ 2020-06-01 7:03 UTC (permalink / raw)
To: Lixin (Victor, Kirin)
Cc: fujun (F), Wuxuecheng, linux-arm-kernel@lists.infradead.org
In-Reply-To: <1D289F1E6D91D2489524BBB0B8880A7DA1A39219@dggeml509-mbx.china.huawei.com>
On Fri, May 29, 2020 at 09:03:37AM +0000, Lixin (Victor, Kirin) wrote:
> Intel optimized getcpu syscall on Linux/Android system by using vDSO, but
> ARM doesn't do any optimizations for getcpu syscall.
>
> In Apple open source, TPIDRRO_EL0/TPIDRURO is used to save the CPU number,
> [1]https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/arm/cswitch.s.auto.html
>
> Is there any risk if using TPIDRRO_EL0/TPIDRURO to implement
> the vDSO for getcpu? Is there any possible to break any ARM ABI? Can you
> help us to confirm the considerations?
Do you have a use-case for high-performance getcpu() that isn't better
suited to rseq()?
Will
_______________________________________________
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 2/3] media: rockchip: Introduce driver for Rockhip's camera interface
From: Maxime Chevallier @ 2020-06-01 6:58 UTC (permalink / raw)
To: Hans Verkuil
Cc: Mark Rutland, devicetree, Heiko Stuebner, linux-kernel,
Paul Kocialkowski, linux-rockchip, Helen Koike, Rob Herring,
Thomas Petazzoni, Miquel Raynal, Mauro Carvalho Chehab,
linux-arm-kernel, linux-media
In-Reply-To: <1a903b30-f3dc-c7e7-1652-96570fcc91f8@xs4all.nl>
Hi Hans,
On Thu, 16 Apr 2020 11:35:41 +0200
Hans Verkuil <hverkuil-cisco@xs4all.nl> wrote:
I'm very sorry I missed a lot of your reviews in my V2, that wasn't on
purpose. I'll fix this on the next iteration, sorry about that.
Thank you very much for your review !
Maxime
>+Helen Koike (rkisp1 maintainer)
>
>A quick review below...
>
>On 03/04/2020 16:21, Maxime Chevallier wrote:
>> Introduce a driver for the camera interface on some Rockchip platforms.
>>
>> This controller supports CSI2, Parallel and BT656 interfaces, but for
>> now only the parallel interface could be tested, hence it's the only one
>> that's supported in the first version of this driver.
>>
>> This controller can be fond on PX30, RK1808, RK3128, RK3288 and RK3288,
>
>fond -> found
>
>'RK3288 and RK3288'? Typo?
>
>> but for now it's only be tested on PX30.
>>
>> Most of this driver was written follwing the BSP driver from rockchip,
>
>follwing -> following
>
>> removing the parts that either didn't fit correctly the guidelines, or
>> that couldn't be tested.
>>
>> This basic version doesn't support cropping nor scaling, and is only
>> designed with one sensor being attached to it a any time.
>
>a any -> at any
>
>Make sure you test with v4l2-compliance, in fact, I need to see the output
>of 'v4l2-compliance -s' when you post v2. There shouldn't be any warnings
>or fails. Make sure you build v4l2-compliance from the git repo
>(https://git.linuxtv.org//v4l-utils.git) directly, that ensures that you
>use the latest and greatest version.
>
>How does this driver compare to drivers/staging/media/rkisp1? Is the ISP
>hardware completely different (it probably is, just checking)?
>
>How complex is the ISP hardware? This driver is a regular video driver,
>but should it move to a media controller based driver instead, like rkisp1?
>
>I don't know the hardware, so I can't tell.
>
>>
>> Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
>> ---
>> drivers/media/platform/Kconfig | 13 +
>> drivers/media/platform/Makefile | 1 +
>> drivers/media/platform/rockchip/cif/Makefile | 3 +
>> drivers/media/platform/rockchip/cif/capture.c | 1170 +++++++++++++++++
>> drivers/media/platform/rockchip/cif/dev.c | 407 ++++++
>> drivers/media/platform/rockchip/cif/dev.h | 208 +++
>> drivers/media/platform/rockchip/cif/regs.h | 256 ++++
>> 7 files changed, 2058 insertions(+)
>> create mode 100644 drivers/media/platform/rockchip/cif/Makefile
>> create mode 100644 drivers/media/platform/rockchip/cif/capture.c
>> create mode 100644 drivers/media/platform/rockchip/cif/dev.c
>> create mode 100644 drivers/media/platform/rockchip/cif/dev.h
>> create mode 100644 drivers/media/platform/rockchip/cif/regs.h
>>
>> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
>> index f65e98d3adf2..d2f413a99886 100644
>> --- a/drivers/media/platform/Kconfig
>> +++ b/drivers/media/platform/Kconfig
>> @@ -460,6 +460,19 @@ config VIDEO_ROCKCHIP_RGA
>>
>> To compile this driver as a module choose m here.
>>
>> +config VIDEO_ROCKCHIP_CIF
>> + tristate "Rockchip Camera Interface"
>> + depends on VIDEO_DEV && VIDEO_V4L2
>> + depends on ARCH_ROCKCHIP || COMPILE_TEST
>> + select VIDEOBUF2_DMA_SG
>> + select VIDEOBUF2_DMA_CONTIG
>> + select V4L2_FWNODE
>> + select V4L2_MEM2MEM_DEV
>> + help
>> + This is a v4l2 driver for Rockchip SOC Camera interface.
>> +
>> + To compile this driver as a module choose m here.
>> +
>> config VIDEO_TI_VPE
>> tristate "TI VPE (Video Processing Engine) driver"
>> depends on VIDEO_DEV && VIDEO_V4L2
>> diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
>> index d13db96e3015..67e7ac034be1 100644
>> --- a/drivers/media/platform/Makefile
>> +++ b/drivers/media/platform/Makefile
>> @@ -68,6 +68,7 @@ obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
>> obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
>>
>> obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/
>> +obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += rockchip/cif/
>>
>> obj-y += omap/
>>
>> diff --git a/drivers/media/platform/rockchip/cif/Makefile b/drivers/media/platform/rockchip/cif/Makefile
>> new file mode 100644
>> index 000000000000..727990824316
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/cif/Makefile
>> @@ -0,0 +1,3 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +obj-$(CONFIG_VIDEO_ROCKCHIP_CIF) += video_rkcif.o
>> +video_rkcif-objs += dev.o capture.o
>> diff --git a/drivers/media/platform/rockchip/cif/capture.c b/drivers/media/platform/rockchip/cif/capture.c
>> new file mode 100644
>> index 000000000000..9df7757c6ed9
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/cif/capture.c
>> @@ -0,0 +1,1170 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Rockchip CIF Driver
>> + *
>> + * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
>> + * Copyright (C) 2020 Maxime Chevallier <maxime.chevallier@bootlin.com>
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/reset.h>
>> +#include <media/v4l2-common.h>
>> +#include <media/v4l2-event.h>
>> +#include <media/v4l2-fh.h>
>> +#include <media/v4l2-fwnode.h>
>> +#include <media/v4l2-ioctl.h>
>> +#include <media/v4l2-mc.h>
>> +#include <media/v4l2-subdev.h>
>> +#include <media/videobuf2-dma-contig.h>
>> +
>> +#include "dev.h"
>> +#include "regs.h"
>> +
>> +#define CIF_REQ_BUFS_MIN 1
>> +#define CIF_MIN_WIDTH 64
>> +#define CIF_MIN_HEIGHT 64
>> +#define CIF_MAX_WIDTH 8192
>> +#define CIF_MAX_HEIGHT 8192
>> +
>> +#define RKCIF_PLANE_Y 0
>> +#define RKCIF_PLANE_CBCR 1
>> +
>> +#define CIF_FETCH_Y_LAST_LINE(VAL) ((VAL) & 0x1fff)
>> +/* Check if swap y and c in bt1120 mode */
>> +#define CIF_FETCH_IS_Y_FIRST(VAL) ((VAL) & 0xf)
>> +
>> +/* Get xsubs and ysubs for fourcc formats
>> + *
>> + * @xsubs: horizontal color samples in a 4*4 matrix, for yuv
>> + * @ysubs: vertical color samples in a 4*4 matrix, for yuv
>> + */
>> +static int fcc_xysubs(u32 fcc, u32 *xsubs, u32 *ysubs)
>> +{
>> + switch (fcc) {
>> + case V4L2_PIX_FMT_NV16:
>> + case V4L2_PIX_FMT_NV61:
>> + *xsubs = 2;
>> + *ysubs = 1;
>> + break;
>> + case V4L2_PIX_FMT_NV21:
>> + case V4L2_PIX_FMT_NV12:
>> + *xsubs = 2;
>> + *ysubs = 2;
>> + break;
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const struct cif_output_fmt out_fmts[] = {
>> + {
>> + .fourcc = V4L2_PIX_FMT_NV16,
>> + .cplanes = 2,
>> + .mplanes = 1,
>> + .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_UVUV,
>> + .bpp = { 8, 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_NV61,
>> + .fmt_val = YUV_OUTPUT_422 | UV_STORAGE_ORDER_VUVU,
>> + .cplanes = 2,
>> + .mplanes = 1,
>> + .bpp = { 8, 16 },
>> + },
>> + {
>> + .fourcc = V4L2_PIX_FMT_NV12,
>> + .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_UVUV,
>> + .cplanes = 2,
>> + .mplanes = 1,
>> + .bpp = { 8, 16 },
>> + .mbus = MEDIA_BUS_FMT_UYVY8_2X8,
>> + },
>> + {
>> + .fourcc = V4L2_PIX_FMT_NV21,
>> + .fmt_val = YUV_OUTPUT_420 | UV_STORAGE_ORDER_VUVU,
>> + .cplanes = 2,
>> + .mplanes = 1,
>> + .bpp = { 8, 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_RGB24,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 24 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_RGB565,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_BGR666,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 18 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SRGGB8,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 8 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGRBG8,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 8 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGBRG8,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 8 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SBGGR8,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 8 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SRGGB10,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGRBG10,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGBRG10,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SBGGR10,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SRGGB12,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGRBG12,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SGBRG12,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SBGGR12,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_SBGGR16,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }, {
>> + .fourcc = V4L2_PIX_FMT_Y16,
>> + .cplanes = 1,
>> + .mplanes = 1,
>> + .bpp = { 16 },
>> + }
>> +};
>> +
>> +static const struct cif_input_fmt in_fmts[] = {
>> + {
>> + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YUYV,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_INTERLACED,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_YVYU8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_YVYU,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_INTERLACED,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_UYVY,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_INTERLACED,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_VYUY8_2X8,
>> + .dvp_fmt_val = YUV_INPUT_422 | YUV_INPUT_ORDER_VYUY,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_YUV422,
>> + .fmt_type = CIF_FMT_TYPE_YUV,
>> + .field = V4L2_FIELD_INTERLACED,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RGB888,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_8,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW8,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_10,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW10,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }, {
>> + .mbus_code = MEDIA_BUS_FMT_Y12_1X12,
>> + .dvp_fmt_val = INPUT_MODE_RAW | RAW_DATA_WIDTH_12,
>> + .csi_fmt_val = CSI_WRDDR_TYPE_RAW12,
>> + .fmt_type = CIF_FMT_TYPE_RAW,
>> + .field = V4L2_FIELD_NONE,
>> + }
>> +};
>> +
>> +static const struct
>> +cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd)
>
>Don't put a linebreak after 'struct'. You can do this, though:
>
>static const struct cif_input_fmt *
>get_input_fmt(struct v4l2_subdev *sd)
>
>However, the whole prototype is just 72 chars long, so just keep it
>in one line.
>
>> +{
>> + struct v4l2_subdev_format fmt;
>> + int ret;
>> + u32 i;
>> +
>> + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
>> + fmt.pad = 0;
>> + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
>> + if (ret < 0) {
>> + v4l2_warn(sd->v4l2_dev,
>> + "sensor fmt invalid, set to default size\n");
>> + goto set_default;
>> + }
>> +
>> + for (i = 0; i < ARRAY_SIZE(in_fmts); i++)
>> + if (fmt.format.code == in_fmts[i].mbus_code &&
>> + fmt.format.field == in_fmts[i].field)
>> + return &in_fmts[i];
>> +
>> + v4l2_err(sd->v4l2_dev, "remote sensor mbus code not supported\n");
>> +
>> +set_default:
>> + return NULL;
>> +}
>> +
>> + static const struct
>
>Leading tab? I expect 'checkpatch.pl --strict' to catch that. Please run that
>before posting v2.
>
>> +cif_output_fmt *find_output_fmt(struct rkcif_stream *stream, u32 pixelfmt)
>> +{
>> + const struct cif_output_fmt *fmt;
>> + u32 i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(out_fmts); i++) {
>> + fmt = &out_fmts[i];
>> + if (fmt->fourcc == pixelfmt)
>> + return fmt;
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +/***************************** stream operations ******************************/
>> +static void rkcif_assign_new_buffer_oneframe(struct rkcif_stream *stream)
>> +{
>> + struct rkcif_dummy_buffer *dummy_buf = &stream->dummy_buf;
>> + struct rkcif_device *dev = stream->cifdev;
>> + void __iomem *base = dev->base_addr;
>> +
>> + /* Set up an empty buffer for the next frame */
>> + spin_lock(&stream->vbq_lock);
>> + if (!list_empty(&stream->buf_head)) {
>> + stream->curr_buf = list_first_entry(&stream->buf_head,
>> + struct rkcif_buffer, queue);
>> + list_del(&stream->curr_buf->queue);
>> + } else {
>> + stream->curr_buf = NULL;
>> + }
>> + spin_unlock(&stream->vbq_lock);
>> +
>> + if (stream->curr_buf) {
>> + write_cif_reg(base, CIF_FRM0_ADDR_Y,
>> + stream->curr_buf->buff_addr[RKCIF_PLANE_Y]);
>> + write_cif_reg(base, CIF_FRM0_ADDR_UV,
>> + stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]);
>> + write_cif_reg(base, CIF_FRM1_ADDR_Y,
>> + stream->curr_buf->buff_addr[RKCIF_PLANE_Y]);
>> + write_cif_reg(base, CIF_FRM1_ADDR_UV,
>> + stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]);
>> + } else {
>> + write_cif_reg(base, CIF_FRM0_ADDR_Y, dummy_buf->dma_addr);
>> + write_cif_reg(base, CIF_FRM0_ADDR_UV, dummy_buf->dma_addr);
>> + write_cif_reg(base, CIF_FRM1_ADDR_Y, dummy_buf->dma_addr);
>> + write_cif_reg(base, CIF_FRM1_ADDR_UV, dummy_buf->dma_addr);
>> + }
>> +}
>> +
>> +static void rkcif_stream_stop(struct rkcif_stream *stream)
>> +{
>> + struct rkcif_device *cif_dev = stream->cifdev;
>> + void __iomem *base = cif_dev->base_addr;
>> + u32 val;
>> +
>> + val = read_cif_reg(base, CIF_CTRL);
>> + write_cif_reg(base, CIF_CTRL, val & (~ENABLE_CAPTURE));
>> + write_cif_reg(base, CIF_INTEN, 0x0);
>> + write_cif_reg(base, CIF_INTSTAT, 0x3ff);
>> + write_cif_reg(base, CIF_FRAME_STATUS, 0x0);
>> +
>> + stream->state = RKCIF_STATE_READY;
>> +}
>> +
>> +static int rkcif_queue_setup(struct vb2_queue *queue,
>> + unsigned int *num_buffers,
>> + unsigned int *num_planes,
>> + unsigned int sizes[],
>> + struct device *alloc_devs[])
>> +{
>> + struct rkcif_stream *stream = queue->drv_priv;
>> + const struct v4l2_pix_format_mplane *pixm;
>> + const struct cif_output_fmt *cif_fmt;
>> + u32 i;
>> +
>> + pixm = &stream->pixm;
>> + cif_fmt = stream->cif_fmt_out;
>> +
>> + if (*num_planes) {
>> + if (*num_planes != cif_fmt->mplanes)
>> + return -EINVAL;
>> +
>> + for (i = 0; i < cif_fmt->mplanes; i++)
>> + if (sizes[i] < pixm->plane_fmt[i].sizeimage)
>> + return -EINVAL;
>> + return 0;
>> + }
>> +
>> + *num_planes = cif_fmt->mplanes;
>> +
>> + for (i = 0; i < cif_fmt->mplanes; i++) {
>> + const struct v4l2_plane_pix_format *plane_fmt;
>> +
>> + plane_fmt = &pixm->plane_fmt[i];
>> + sizes[i] = plane_fmt->sizeimage;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/*
>> + * The vb2_buffer are stored in rkcif_buffer, in order to unify
>> + * mplane buffer and none-mplane buffer.
>> + */
>> +static void rkcif_buf_queue(struct vb2_buffer *vb)
>> +{
>> + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
>> + struct rkcif_buffer *cifbuf = to_rkcif_buffer(vbuf);
>> + struct vb2_queue *queue = vb->vb2_queue;
>> + struct rkcif_stream *stream = queue->drv_priv;
>> + struct v4l2_pix_format_mplane *pixm = &stream->pixm;
>> + const struct cif_output_fmt *fmt = stream->cif_fmt_out;
>> + unsigned long lock_flags = 0;
>> + int i;
>> +
>> + memset(cifbuf->buff_addr, 0, sizeof(cifbuf->buff_addr));
>> + /* If mplanes > 1, every c-plane has its own m-plane,
>> + * otherwise, multiple c-planes are in the same m-plane
>> + */
>> + for (i = 0; i < fmt->mplanes; i++)
>> + cifbuf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
>> +
>> + if (fmt->mplanes == 1) {
>> + for (i = 0; i < fmt->cplanes - 1; i++)
>> + cifbuf->buff_addr[i + 1] = cifbuf->buff_addr[i] +
>> + pixm->plane_fmt[i].bytesperline * pixm->height;
>> + }
>> +
>> + spin_lock_irqsave(&stream->vbq_lock, lock_flags);
>> + list_add_tail(&cifbuf->queue, &stream->buf_head);
>> + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
>> +}
>> +
>> +static int rkcif_create_dummy_buf(struct rkcif_stream *stream)
>> +{
>
>Why do you need a dummy buffer? That's very unusual, so at the very least
>this needs comments.
>
>> + struct rkcif_dummy_buffer *dummy_buf = &stream->dummy_buf;
>> + struct rkcif_device *dev = stream->cifdev;
>> +
>> + /* get a maximum plane size */
>> + dummy_buf->size = max3(stream->pixm.plane_fmt[0].bytesperline *
>> + stream->pixm.height,
>> + stream->pixm.plane_fmt[1].sizeimage,
>> + stream->pixm.plane_fmt[2].sizeimage);
>> +
>> + dummy_buf->vaddr = dma_alloc_coherent(dev->dev, dummy_buf->size,
>> + &dummy_buf->dma_addr,
>> + GFP_KERNEL);
>> + if (!dummy_buf->vaddr) {
>> + v4l2_err(&dev->v4l2_dev,
>> + "Failed to allocate the memory for dummy buffer\n");
>> + return -ENOMEM;
>> + }
>> +
>> + v4l2_info(&dev->v4l2_dev, "Allocate dummy buffer, size: 0x%08x\n",
>> + dummy_buf->size);
>> +
>> + return 0;
>> +}
>> +
>> +static void rkcif_destroy_dummy_buf(struct rkcif_stream *stream)
>> +{
>> + struct rkcif_dummy_buffer *dummy_buf = &stream->dummy_buf;
>> + struct rkcif_device *dev = stream->cifdev;
>> +
>> + dma_free_coherent(dev->dev, dummy_buf->size,
>> + dummy_buf->vaddr, dummy_buf->dma_addr);
>> +}
>> +
>> +static void rkcif_stop_streaming(struct vb2_queue *queue)
>> +{
>> + struct rkcif_stream *stream = queue->drv_priv;
>> + struct rkcif_device *dev = stream->cifdev;
>> + struct rkcif_buffer *buf;
>> + struct v4l2_subdev *sd;
>> + int ret;
>> +
>> + stream->stopping = true;
>> + ret = wait_event_timeout(stream->wq_stopped,
>> + stream->state != RKCIF_STATE_STREAMING,
>> + msecs_to_jiffies(1000));
>> + if (!ret) {
>> + rkcif_stream_stop(stream);
>> + stream->stopping = false;
>> + }
>> + pm_runtime_put(dev->dev);
>> +
>> + /* stop the sub device*/
>> + sd = dev->sensor.sd;
>> + v4l2_subdev_call(sd, video, s_stream, 0);
>> + v4l2_subdev_call(sd, core, s_power, 0);
>> +
>> + /* release buffers */
>> + if (stream->curr_buf) {
>> + list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
>> + stream->curr_buf = NULL;
>> + }
>> + if (stream->next_buf) {
>> + list_add_tail(&stream->next_buf->queue, &stream->buf_head);
>> + stream->next_buf = NULL;
>> + }
>> +
>> + while (!list_empty(&stream->buf_head)) {
>> + buf = list_first_entry(&stream->buf_head,
>> + struct rkcif_buffer, queue);
>> + list_del(&buf->queue);
>> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
>> + }
>> +
>> + rkcif_destroy_dummy_buf(stream);
>> +}
>> +
>> +static u32 rkcif_determine_input_mode(struct rkcif_device *dev)
>> +{
>> + struct rkcif_sensor_info *sensor_info = &dev->sensor;
>> + struct rkcif_stream *stream = &dev->stream;
>> + v4l2_std_id std;
>> + u32 mode = INPUT_MODE_YUV;
>> + int ret;
>> +
>> + ret = v4l2_subdev_call(sensor_info->sd, video, querystd, &std);
>> + if (ret == 0) {
>> + /* retrieve std from sensor if exist */
>> + switch (std) {
>> + case V4L2_STD_NTSC:
>> + mode = INPUT_MODE_NTSC;
>> + break;
>> + case V4L2_STD_PAL:
>> + mode = INPUT_MODE_PAL;
>> + break;
>> + case V4L2_STD_ATSC:
>> + mode = INPUT_MODE_BT1120;
>> + break;
>
>Huh? V4L2_STD_* is only used for SDTV video receivers (i.e. Composite, S-Video,
>analog TV). Sensors do not use it. So what is going on here?
>
>And V4L2_STD_ATSC is deprecated and should not be used in new drivers.
>
>> + default:
>> + v4l2_err(&dev->v4l2_dev,
>> + "std: %lld is not supported", std);
>
>Missing break.
>
>> + }
>> + } else {
>> + /* determine input mode by mbus_code (fmt_type) */
>> + switch (stream->cif_fmt_in->fmt_type) {
>> + case CIF_FMT_TYPE_YUV:
>> + mode = INPUT_MODE_YUV;
>> + break;
>> + case CIF_FMT_TYPE_RAW:
>> + mode = INPUT_MODE_RAW;
>> + break;
>> + }
>> + }
>> +
>> + return mode;
>> +}
>> +
>> +static inline u32 rkcif_scl_ctl(struct rkcif_stream *stream)
>> +{
>> + u32 fmt_type = stream->cif_fmt_in->fmt_type;
>> +
>> + return (fmt_type == CIF_FMT_TYPE_YUV) ?
>> + ENABLE_YUV_16BIT_BYPASS : ENABLE_RAW_16BIT_BYPASS;
>> +}
>> +
>> +static int rkcif_stream_start(struct rkcif_stream *stream)
>> +{
>> + u32 val, mbus_flags, href_pol, vsync_pol,
>> + xfer_mode = 0, yc_swap = 0, skip_top = 0;
>> + struct rkcif_device *dev = stream->cifdev;
>> + struct rkcif_sensor_info *sensor_info;
>> + void __iomem *base = dev->base_addr;
>> +
>> + sensor_info = &dev->sensor;
>> + stream->frame_idx = 0;
>> +
>> + mbus_flags = sensor_info->mbus.flags;
>> + href_pol = (mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) ?
>> + HSY_HIGH_ACTIVE : HSY_LOW_ACTIVE;
>> + vsync_pol = (mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) ?
>> + VSY_HIGH_ACTIVE : VSY_LOW_ACTIVE;
>> +
>> + if (rkcif_determine_input_mode(dev) == INPUT_MODE_BT1120) {
>> + if (stream->cif_fmt_in->field == V4L2_FIELD_NONE)
>> + xfer_mode = BT1120_TRANSMIT_PROGRESS;
>> + else
>> + xfer_mode = BT1120_TRANSMIT_INTERFACE;
>> + if (!CIF_FETCH_IS_Y_FIRST(stream->cif_fmt_in->dvp_fmt_val))
>> + yc_swap = BT1120_YC_SWAP;
>> + }
>> +
>> + val = vsync_pol | href_pol | rkcif_determine_input_mode(dev) |
>> + stream->cif_fmt_out->fmt_val | stream->cif_fmt_in->dvp_fmt_val |
>> + xfer_mode | yc_swap;
>> + write_cif_reg(base, CIF_FOR, val);
>> + val = stream->pixm.width;
>> + if (stream->cif_fmt_in->fmt_type == CIF_FMT_TYPE_RAW)
>> + val = stream->pixm.width * 2;
>> + write_cif_reg(base, CIF_VIR_LINE_WIDTH, val);
>> + write_cif_reg(base, CIF_SET_SIZE,
>> + stream->pixm.width | (stream->pixm.height << 16));
>> +
>> + v4l2_subdev_call(sensor_info->sd, sensor, g_skip_top_lines, &skip_top);
>> +
>> + write_cif_reg(base, CIF_CROP, skip_top << CIF_CROP_Y_SHIFT);
>> + write_cif_reg(base, CIF_FRAME_STATUS, FRAME_STAT_CLS);
>> + write_cif_reg(base, CIF_INTSTAT, INTSTAT_CLS);
>> + write_cif_reg(base, CIF_SCL_CTRL, rkcif_scl_ctl(stream));
>> +
>> + rkcif_assign_new_buffer_oneframe(stream);
>> +
>> + write_cif_reg(base, CIF_INTEN, FRAME_END_EN | LINE_ERR_EN |
>> + PST_INF_FRAME_END);
>> +
>> + if (dev->chip_id == CHIP_RK1808_CIF &&
>> + rkcif_determine_input_mode(dev) == INPUT_MODE_BT1120)
>> + write_cif_reg(base, CIF_CTRL,
>> + AXI_BURST_16 | MODE_PINGPONG | ENABLE_CAPTURE);
>> + else
>> + write_cif_reg(base, CIF_CTRL,
>> + AXI_BURST_16 | MODE_ONEFRAME | ENABLE_CAPTURE);
>> +
>> + stream->state = RKCIF_STATE_STREAMING;
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_start_streaming(struct vb2_queue *queue, unsigned int count)
>> +{
>> + struct rkcif_stream *stream = queue->drv_priv;
>> + struct rkcif_device *dev = stream->cifdev;
>> + struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
>> + struct v4l2_subdev *sd;
>> + int ret;
>> +
>> + if (WARN_ON(stream->state != RKCIF_STATE_READY)) {
>> + ret = -EBUSY;
>> + v4l2_err(v4l2_dev, "stream in busy state\n");
>> + goto destroy_buf;
>> + }
>> +
>> + stream->cif_fmt_in = get_input_fmt(dev->sensor.sd);
>> +
>> + ret = rkcif_create_dummy_buf(stream);
>> + if (ret < 0) {
>> + v4l2_err(v4l2_dev, "Failed to create dummy_buf, %d\n", ret);
>> + goto destroy_buf;
>> + }
>> +
>> + ret = pm_runtime_get_sync(dev->dev);
>> + if (ret < 0) {
>> + v4l2_err(v4l2_dev, "Failed to get runtime pm, %d\n", ret);
>> + goto destroy_dummy_buf;
>> + }
>> +
>> + /* start sub-devices */
>> + sd = dev->sensor.sd;
>> + ret = v4l2_subdev_call(sd, core, s_power, 1);
>> + if (ret < 0 && ret != -ENOIOCTLCMD)
>> + goto runtime_put;
>> + ret = v4l2_subdev_call(sd, video, s_stream, 1);
>> + if (ret < 0)
>> + goto subdev_poweroff;
>> +
>> + ret = rkcif_stream_start(stream);
>> + if (ret < 0)
>> + goto stop_stream;
>> +
>> + return 0;
>> +
>> +stop_stream:
>> + rkcif_stream_stop(stream);
>> +subdev_poweroff:
>> + v4l2_subdev_call(sd, core, s_power, 0);
>> +runtime_put:
>> + pm_runtime_put(dev->dev);
>> +destroy_dummy_buf:
>> + rkcif_destroy_dummy_buf(stream);
>> +destroy_buf:
>> + while (!list_empty(&stream->buf_head)) {
>> + struct rkcif_buffer *buf;
>> +
>> + buf = list_first_entry(&stream->buf_head,
>> + struct rkcif_buffer, queue);
>> + list_del(&buf->queue);
>> + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +static struct vb2_ops rkcif_vb2_ops = {
>> + .queue_setup = rkcif_queue_setup,
>> + .buf_queue = rkcif_buf_queue,
>> + .wait_prepare = vb2_ops_wait_prepare,
>> + .wait_finish = vb2_ops_wait_finish,
>> + .stop_streaming = rkcif_stop_streaming,
>> + .start_streaming = rkcif_start_streaming,
>> +};
>> +
>> +static int rkcif_init_vb2_queue(struct vb2_queue *q,
>> + struct rkcif_stream *stream,
>> + enum v4l2_buf_type buf_type)
>> +{
>> + q->type = buf_type;
>> + q->io_modes = VB2_MMAP | VB2_DMABUF;
>> + q->drv_priv = stream;
>> + q->ops = &rkcif_vb2_ops;
>> + q->mem_ops = &vb2_dma_contig_memops;
>> + q->buf_struct_size = sizeof(struct rkcif_buffer);
>> + q->min_buffers_needed = CIF_REQ_BUFS_MIN;
>> + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
>> + q->lock = &stream->vlock;
>> + q->dev = stream->cifdev->dev;
>> +
>> + return vb2_queue_init(q);
>> +}
>> +
>> +static void rkcif_set_fmt(struct rkcif_stream *stream,
>> + struct v4l2_pix_format_mplane *pixm,
>> + bool try)
>> +{
>> + const struct cif_output_fmt *fmt;
>> + struct v4l2_rect input_rect;
>> + unsigned int imagesize = 0, planes;
>> + u32 xsubs = 1, ysubs = 1, i;
>> +
>> + fmt = find_output_fmt(stream, pixm->pixelformat);
>> + if (!fmt)
>> + fmt = &out_fmts[0];
>> +
>> + input_rect.width = CIF_MAX_WIDTH;
>> + input_rect.height = CIF_MAX_HEIGHT;
>> +
>> + pixm->width = clamp_t(u32, pixm->width,
>> + CIF_MIN_WIDTH, input_rect.width);
>> + pixm->height = clamp_t(u32, pixm->height,
>> + CIF_MIN_HEIGHT, input_rect.height);
>> +
>> + pixm->num_planes = fmt->mplanes;
>> + pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
>> + pixm->colorspace = V4L2_COLORSPACE_SRGB;
>> +
>> + pixm->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pixm->colorspace);
>> + pixm->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pixm->colorspace);
>
>This colorimetry information should come from the sensor subdev. That's the
>only component that knows this.
>
>> +
>> + pixm->pixelformat = fmt->fourcc;
>> + pixm->field = V4L2_FIELD_NONE;
>> +
>> + /* calculate plane size and image size */
>> + fcc_xysubs(fmt->fourcc, &xsubs, &ysubs);
>> +
>> + planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
>> +
>> + for (i = 0; i < planes; i++) {
>> + struct v4l2_plane_pix_format *plane_fmt;
>> + int width, height, bpl, size;
>> +
>> + if (i == 0) {
>> + width = pixm->width;
>> + height = pixm->height;
>> + } else {
>> + width = pixm->width / xsubs;
>> + height = pixm->height / ysubs;
>> + }
>> +
>> + bpl = width * fmt->bpp[i] / 8;
>> + size = bpl * height;
>> + imagesize += size;
>> +
>> + if (fmt->mplanes > i) {
>> + /* Set bpl and size for each mplane */
>> + plane_fmt = pixm->plane_fmt + i;
>> + plane_fmt->bytesperline = bpl;
>> + plane_fmt->sizeimage = size;
>> + }
>> + }
>> +
>> + /* convert to non-MPLANE format.
>> + * It's important since we want to unify non-MPLANE
>> + * and MPLANE.
>> + */
>> + if (fmt->mplanes == 1)
>> + pixm->plane_fmt[0].sizeimage = imagesize;
>> +
>> + if (!try) {
>> + stream->cif_fmt_out = fmt;
>> + stream->pixm = *pixm;
>> + }
>> +}
>> +
>> +void rkcif_stream_init(struct rkcif_device *dev)
>> +{
>> + struct rkcif_stream *stream = &dev->stream;
>> + struct v4l2_pix_format_mplane pixm;
>> +
>> + memset(stream, 0, sizeof(*stream));
>> + memset(&pixm, 0, sizeof(pixm));
>> + stream->cifdev = dev;
>> +
>> + INIT_LIST_HEAD(&stream->buf_head);
>> + spin_lock_init(&stream->vbq_lock);
>> + stream->state = RKCIF_STATE_READY;
>> + init_waitqueue_head(&stream->wq_stopped);
>> +
>> + /* Set default format */
>> + pixm.pixelformat = V4L2_PIX_FMT_NV12;
>> + pixm.width = RKCIF_DEFAULT_WIDTH;
>> + pixm.height = RKCIF_DEFAULT_HEIGHT;
>> + rkcif_set_fmt(stream, &pixm, false);
>> +
>> + stream->crop.left = 0;
>> + stream->crop.top = 0;
>> + stream->crop.width = 10;
>> + stream->crop.height = 10;
>
>I thought cropping wasn't supported yet? If that's indeed the case, then
>just drop the 'crop' struct.
>
>> +}
>> +
>> +static int rkcif_fh_open(struct file *filp)
>> +{
>> + struct video_device *vdev = video_devdata(filp);
>> + struct rkcif_stream *stream = to_rkcif_stream(vdev);
>> + struct rkcif_device *cifdev = stream->cifdev;
>> +
>> + rkcif_soft_reset(cifdev);
>
>Dubious. What happens if you are streaming and from another shell you
>run 'v4l2-ctl -D -d /dev/videoX'? That will open the device and call
>rkcif_soft_reset(). That doesn't sound safe.
>
>> +
>> + return v4l2_fh_open(filp);
>> +}
>> +
>> +static const struct v4l2_file_operations rkcif_fops = {
>> + .open = rkcif_fh_open,
>> + .release = vb2_fop_release,
>> + .unlocked_ioctl = video_ioctl2,
>> + .poll = vb2_fop_poll,
>> + .mmap = vb2_fop_mmap,
>> +};
>> +
>> +static int rkcif_enum_input(struct file *file, void *priv,
>> + struct v4l2_input *input)
>> +{
>> + if (input->index > 0)
>> + return -EINVAL;
>> +
>> + input->type = V4L2_INPUT_TYPE_CAMERA;
>> + strlcpy(input->name, "Camera", sizeof(input->name));
>
>Use strscpy instead of strcpy/strncpy/strlcpy. We standardized on that in the
>media subsystem.
>
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_try_fmt_vid_cap_mplane(struct file *file, void *fh,
>> + struct v4l2_format *f)
>> +{
>> + struct rkcif_stream *stream = video_drvdata(file);
>> +
>> + rkcif_set_fmt(stream, &f->fmt.pix_mp, true);
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_enum_fmt_vid_cap(struct file *file, void *priv,
>> + struct v4l2_fmtdesc *f)
>> +{
>> + const struct cif_output_fmt *fmt = NULL;
>> +
>> + if (f->index >= ARRAY_SIZE(out_fmts))
>> + return -EINVAL;
>> +
>> + fmt = &out_fmts[f->index];
>> + f->pixelformat = fmt->fourcc;
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_s_fmt_vid_cap_mplane(struct file *file,
>> + void *priv, struct v4l2_format *f)
>> +{
>> + struct rkcif_stream *stream = video_drvdata(file);
>> +
>> + rkcif_set_fmt(stream, &f->fmt.pix_mp, false);
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_g_fmt_vid_cap_mplane(struct file *file, void *fh,
>> + struct v4l2_format *f)
>> +{
>> + struct rkcif_stream *stream = video_drvdata(file);
>> +
>> + f->fmt.pix_mp = stream->pixm;
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_querycap(struct file *file, void *priv,
>> + struct v4l2_capability *cap)
>> +{
>> + struct rkcif_stream *stream = video_drvdata(file);
>> + struct device *dev = stream->cifdev->dev;
>> +
>> + strlcpy(cap->driver, dev->driver->name, sizeof(cap->driver));
>> + strlcpy(cap->card, dev->driver->name, sizeof(cap->card));
>> + snprintf(cap->bus_info, sizeof(cap->bus_info),
>> + "platform:%s", dev_name(dev));
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_enum_framesizes(struct file *file, void *fh,
>> + struct v4l2_frmsizeenum *fsize)
>> +{
>> + struct rkcif_stream *stream = video_drvdata(file);
>> + struct rkcif_device *dev = stream->cifdev;
>> + struct v4l2_subdev_frame_size_enum fse = {
>> + .index = fsize->index,
>> + .which = V4L2_SUBDEV_FORMAT_ACTIVE,
>> + };
>> + const struct cif_output_fmt *fmt;
>> + int ret;
>> +
>> + if (!dev->sensor.sd)
>> + return -EINVAL;
>> +
>> + fmt = find_output_fmt(stream, fsize->pixel_format);
>> + if (!fmt)
>> + return -EINVAL;
>> +
>> + fse.code = fmt->mbus;
>> +
>> + ret = v4l2_subdev_call(dev->sensor.sd, pad, enum_frame_size,
>> + NULL, &fse);
>> + if (ret)
>> + return ret;
>> +
>> + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
>> + fsize->discrete.width = fse.max_width;
>> + fsize->discrete.height = fse.max_height;
>> +
>> + return 0;
>> +}
>> +
>> +static int rkcif_g_input(struct file *file, void *fh, unsigned int *i)
>> +{
>> + *i = 0;
>> + return 0;
>> +}
>> +
>> +static int rkcif_s_input(struct file *file, void *fh, unsigned int i)
>> +{
>> + if (i)
>> + return -EINVAL;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct v4l2_ioctl_ops rkcif_v4l2_ioctl_ops = {
>> + .vidioc_reqbufs = vb2_ioctl_reqbufs,
>> + .vidioc_querybuf = vb2_ioctl_querybuf,
>> + .vidioc_create_bufs = vb2_ioctl_create_bufs,
>> + .vidioc_qbuf = vb2_ioctl_qbuf,
>> + .vidioc_expbuf = vb2_ioctl_expbuf,
>> + .vidioc_dqbuf = vb2_ioctl_dqbuf,
>> + .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
>> + .vidioc_streamon = vb2_ioctl_streamon,
>> + .vidioc_streamoff = vb2_ioctl_streamoff,
>> +
>> + .vidioc_enum_fmt_vid_cap = rkcif_enum_fmt_vid_cap,
>> + .vidioc_try_fmt_vid_cap_mplane = rkcif_try_fmt_vid_cap_mplane,
>> + .vidioc_s_fmt_vid_cap_mplane = rkcif_s_fmt_vid_cap_mplane,
>> + .vidioc_g_fmt_vid_cap_mplane = rkcif_g_fmt_vid_cap_mplane,
>> + .vidioc_querycap = rkcif_querycap,
>> + .vidioc_enum_framesizes = rkcif_enum_framesizes,
>> +
>> + .vidioc_enum_input = rkcif_enum_input,
>> + .vidioc_g_input = rkcif_g_input,
>> + .vidioc_s_input = rkcif_s_input,
>> +};
>> +
>> +void rkcif_unregister_stream_vdev(struct rkcif_device *dev)
>> +{
>> + struct rkcif_stream *stream = &dev->stream;
>> +
>> + media_entity_cleanup(&stream->vdev.entity);
>> + video_unregister_device(&stream->vdev);
>> +}
>> +
>> +int rkcif_register_stream_vdev(struct rkcif_device *dev)
>> +{
>> + struct rkcif_stream *stream = &dev->stream;
>> + struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
>> + struct video_device *vdev = &stream->vdev;
>> + int ret;
>> +
>> + strlcpy(vdev->name, CIF_VIDEODEVICE_NAME, sizeof(vdev->name));
>> + mutex_init(&stream->vlock);
>> +
>> + vdev->ioctl_ops = &rkcif_v4l2_ioctl_ops;
>> + vdev->release = video_device_release_empty;
>> + vdev->fops = &rkcif_fops;
>> + vdev->minor = -1;
>> + vdev->v4l2_dev = v4l2_dev;
>> + vdev->lock = &stream->vlock;
>> + vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
>> + V4L2_CAP_STREAMING;
>> + video_set_drvdata(vdev, stream);
>> + vdev->vfl_dir = VFL_DIR_RX;
>> + stream->pad.flags = MEDIA_PAD_FL_SINK;
>> +
>> + rkcif_init_vb2_queue(&stream->buf_queue, stream,
>> + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE |
>> + V4L2_BUF_TYPE_VIDEO_CAPTURE);
>> + vdev->queue = &stream->buf_queue;
>> + strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
>> +
>> + ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
>
>Hmm, you aren't basing your code on the latest media git repo
>(https://git.linuxtv.org//media_tree.git).
>
>VFL_TYPE_GRABBER was renamed to VFL_TYPE_VIDEO.
>
>> + if (ret < 0) {
>> + v4l2_err(v4l2_dev,
>> + "video_register_device failed with error %d\n", ret);
>> + return ret;
>> + }
>> +
>> + ret = media_entity_pads_init(&vdev->entity, 1, &stream->pad);
>> + if (ret < 0)
>> + goto unreg;
>> +
>> + return 0;
>> +unreg:
>> + video_unregister_device(vdev);
>> + return ret;
>> +}
>> +
>> +static void rkcif_vb_done_oneframe(struct rkcif_stream *stream,
>> + struct vb2_v4l2_buffer *vb_done)
>> +{
>> + const struct cif_output_fmt *fmt = stream->cif_fmt_out;
>> + u32 i;
>> +
>> + /* Dequeue a filled buffer */
>> + for (i = 0; i < fmt->mplanes; i++) {
>> + vb2_set_plane_payload(&vb_done->vb2_buf, i,
>> + stream->pixm.plane_fmt[i].sizeimage);
>> + }
>> + vb_done->vb2_buf.timestamp = ktime_get_ns();
>> + vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
>> +}
>> +
>> +void rkcif_irq_oneframe(struct rkcif_device *cif_dev)
>> +{
>> + struct rkcif_stream *stream = &cif_dev->stream;
>> + u32 lastline, lastpix, ctl, cif_frmst, intstat;
>> + void __iomem *base = cif_dev->base_addr;
>> +
>> + intstat = read_cif_reg(base, CIF_INTSTAT);
>> + cif_frmst = read_cif_reg(base, CIF_FRAME_STATUS);
>> + lastline = CIF_FETCH_Y_LAST_LINE(read_cif_reg(base, CIF_LAST_LINE));
>> + lastpix = read_cif_reg(base, CIF_LAST_PIX);
>> + ctl = read_cif_reg(base, CIF_CTRL);
>> +
>> + /* There are two irqs enabled:
>> + * - PST_INF_FRAME_END: cif FIFO is ready, this is prior to FRAME_END
>> + * - FRAME_END: cif has saved frame to memory, a frame ready
>> + */
>> +
>> + if ((intstat & PST_INF_FRAME_END)) {
>> + write_cif_reg(base, CIF_INTSTAT, PST_INF_FRAME_END_CLR);
>> +
>> + if (stream->stopping)
>> + /* To stop CIF ASAP, before FRAME_END irq */
>> + write_cif_reg(base, CIF_CTRL, ctl & (~ENABLE_CAPTURE));
>> + }
>> +
>> + if ((intstat & LINE_ERR)) {
>> + write_cif_reg(base, CIF_INTSTAT, LINE_ERR_CLR);
>> +
>> + if (stream->stopping) {
>> + rkcif_stream_stop(stream);
>> + stream->stopping = false;
>> + wake_up(&stream->wq_stopped);
>> + return;
>> + }
>> +
>> + v4l2_err(&cif_dev->v4l2_dev,
>> + "Bad frame, irq:0x%x frmst:0x%x size:%dx%d\n",
>> + intstat, cif_frmst, lastline, lastpix);
>> + /* Clear status to receive into the same buffer */
>> + write_cif_reg(base, CIF_FRAME_STATUS, FRM0_STAT_CLS);
>> + return;
>> + }
>> +
>> +
>> + if ((intstat & FRAME_END)) {
>> + struct vb2_v4l2_buffer *vb_done = NULL;
>> +
>> + write_cif_reg(base, CIF_INTSTAT, FRAME_END_CLR);
>> +
>> + if (stream->stopping) {
>> + rkcif_stream_stop(stream);
>> + stream->stopping = false;
>> + wake_up(&stream->wq_stopped);
>> + return;
>> + }
>> +
>> + if (lastline != stream->pixm.height ||
>> + !(cif_frmst & CIF_F0_READY)) {
>> + v4l2_err(&cif_dev->v4l2_dev,
>> + "Bad frame, irq:0x%x frmst:0x%x size:%dx%d\n",
>> + intstat, cif_frmst, lastline, lastpix);
>> + /* Clear status to receive into the same buffer */
>> + write_cif_reg(base, CIF_FRAME_STATUS, FRM0_STAT_CLS);
>> + return;
>> + }
>> +
>> + if (stream->curr_buf)
>> + vb_done = &stream->curr_buf->vb;
>> + rkcif_assign_new_buffer_oneframe(stream);
>> +
>> + /* In one-frame mode, must clear status manually to enable
>> + * the next frame end irq
>> + */
>> + write_cif_reg(base, CIF_FRAME_STATUS, FRM0_STAT_CLS);
>> +
>> + if (vb_done)
>> + rkcif_vb_done_oneframe(stream, vb_done);
>> +
>> + stream->frame_idx++;
>> + }
>> +}
>> diff --git a/drivers/media/platform/rockchip/cif/dev.c b/drivers/media/platform/rockchip/cif/dev.c
>> new file mode 100644
>> index 000000000000..88f3833b7cdc
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/cif/dev.c
>> @@ -0,0 +1,407 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Rockchip CIF Driver
>> + *
>> + * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/of_graph.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/of_reserved_mem.h>
>> +#include <linux/reset.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/pinctrl/consumer.h>
>> +#include <media/v4l2-fwnode.h>
>> +
>> +#include "dev.h"
>> +#include "regs.h"
>> +
>> +#define RKCIF_VERNO_LEN 10
>> +
>> +struct cif_match_data {
>> + int chip_id;
>> + const char * const *clks;
>> + const char * const *rsts;
>> + int clks_num;
>> + int rsts_num;
>> +};
>> +
>> +static int rkcif_create_links(struct rkcif_device *dev)
>> +{
>> + struct v4l2_subdev *sd = dev->sensor.sd;
>> + int ret;
>> +
>> + ret = media_entity_get_fwnode_pad(&sd->entity, sd->fwnode,
>> + MEDIA_PAD_FL_SOURCE);
>> + if (ret)
>> + return ret;
>> +
>> + ret = media_create_pad_link(&sd->entity, 0,
>> + &dev->stream.vdev.entity, 0,
>> + MEDIA_LNK_FL_ENABLED);
>> + if (ret) {
>> + dev_err(dev->dev, "failed to create link");
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int subdev_notifier_complete(struct v4l2_async_notifier *notifier)
>> +{
>> + struct rkcif_device *dev;
>> + int ret;
>> +
>> + dev = container_of(notifier, struct rkcif_device, notifier);
>> +
>> + mutex_lock(&dev->media_dev.graph_mutex);
>> +
>> + ret = rkcif_create_links(dev);
>> + if (ret < 0)
>> + goto unlock;
>> +
>> + ret = v4l2_device_register_subdev_nodes(&dev->v4l2_dev);
>> + if (ret < 0)
>> + goto unlock;
>> +
>> +unlock:
>> + mutex_unlock(&dev->media_dev.graph_mutex);
>> + return ret;
>> +}
>> +
>> +static int subdev_notifier_bound(struct v4l2_async_notifier *notifier,
>> + struct v4l2_subdev *subdev,
>> + struct v4l2_async_subdev *asd)
>> +{
>> + struct rkcif_device *cif_dev = container_of(notifier,
>> + struct rkcif_device, notifier);
>> +
>> + int pad;
>> +
>> + cif_dev->sensor.sd = subdev;
>> + pad = media_entity_get_fwnode_pad(&subdev->entity, subdev->fwnode,
>> + MEDIA_PAD_FL_SOURCE);
>> + if (pad < 0)
>> + return pad;
>> +
>> + cif_dev->sensor.pad = pad;
>> +
>> + return 0;
>> +}
>> +
>> +static const struct v4l2_async_notifier_operations subdev_notifier_ops = {
>> + .bound = subdev_notifier_bound,
>> + .complete = subdev_notifier_complete,
>> +};
>> +
>> +static int cif_subdev_notifier(struct rkcif_device *cif_dev)
>> +{
>> + struct v4l2_async_notifier *ntf = &cif_dev->notifier;
>> + struct device *dev = cif_dev->dev;
>> + struct v4l2_fwnode_endpoint vep = {
>> + .bus_type = V4L2_MBUS_PARALLEL,
>> + };
>> + struct fwnode_handle *ep;
>> + int ret;
>> +
>> + v4l2_async_notifier_init(ntf);
>> +
>> + ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
>> + FWNODE_GRAPH_ENDPOINT_NEXT);
>> + if (!ep)
>> + return -EINVAL;
>> +
>> + ret = v4l2_fwnode_endpoint_parse(ep, &vep);
>> + if (ret)
>> + return ret;
>> +
>> + ret = v4l2_async_notifier_add_fwnode_remote_subdev(ntf, ep,
>> + &cif_dev->asd);
>> + if (ret)
>> + return ret;
>> +
>> + ntf->ops = &subdev_notifier_ops;
>> +
>> + fwnode_handle_put(ep);
>> +
>> + ret = v4l2_async_notifier_register(&cif_dev->v4l2_dev, ntf);
>> + return ret;
>> +}
>> +
>> +static int rkcif_register_platform_subdevs(struct rkcif_device *cif_dev)
>> +{
>> + int ret;
>> +
>> + ret = rkcif_register_stream_vdev(cif_dev);
>
>You must wait with registering the video device (and media device as well
>for that matter) until all subdevs are ready. So this should be moved to
>subdev_notifier_complete.
>
>Otherwise device nodes are created that cannot be used yet because the
>subdevs aren't ready yet.
>
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = cif_subdev_notifier(cif_dev);
>> + if (ret < 0) {
>> + v4l2_err(&cif_dev->v4l2_dev,
>> + "Failed to register subdev notifier(%d)\n", ret);
>> + rkcif_unregister_stream_vdev(cif_dev);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static const char * const px30_cif_clks[] = {
>> + "aclk_cif",
>> + "hclk_cif",
>> + "pclk_cif",
>> + "cif_out",
>> +};
>> +
>> +static const char * const px30_cif_rsts[] = {
>> + "rst_cif_a",
>> + "rst_cif_h",
>> + "rst_cif_pclkin",
>> +};
>> +
>> +static const struct cif_match_data px30_cif_match_data = {
>> + .chip_id = CHIP_PX30_CIF,
>> + .clks = px30_cif_clks,
>> + .clks_num = ARRAY_SIZE(px30_cif_clks),
>> + .rsts = px30_cif_rsts,
>> + .rsts_num = ARRAY_SIZE(px30_cif_rsts),
>> +};
>> +
>> +static const struct of_device_id rkcif_plat_of_match[] = {
>> + {
>> + .compatible = "rockchip,px30-cif",
>> + .data = &px30_cif_match_data,
>> + },
>> + {},
>> +};
>> +
>> +static irqreturn_t rkcif_irq_handler(int irq, void *ctx)
>> +{
>> + struct device *dev = ctx;
>> + struct rkcif_device *cif_dev = dev_get_drvdata(dev);
>> +
>> + rkcif_irq_oneframe(cif_dev);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void rkcif_disable_sys_clk(struct rkcif_device *cif_dev)
>> +{
>> + int i;
>> +
>> + for (i = cif_dev->clk_size - 1; i >= 0; i--)
>> + clk_disable_unprepare(cif_dev->clks[i]);
>> +}
>> +
>> +static int rkcif_enable_sys_clk(struct rkcif_device *cif_dev)
>> +{
>> + int i, ret = -EINVAL;
>> +
>> + for (i = 0; i < cif_dev->clk_size; i++) {
>> + ret = clk_prepare_enable(cif_dev->clks[i]);
>> +
>> + if (ret < 0)
>> + goto err;
>> + }
>> +
>> + return 0;
>> +
>> +err:
>> + for (--i; i >= 0; --i)
>> + clk_disable_unprepare(cif_dev->clks[i]);
>> +
>> + return ret;
>> +}
>> +
>> +void rkcif_soft_reset(struct rkcif_device *cif_dev)
>> +{
>> + unsigned int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(cif_dev->cif_rst); i++)
>> + if (cif_dev->cif_rst[i])
>> + reset_control_assert(cif_dev->cif_rst[i]);
>> + udelay(5);
>> + for (i = 0; i < ARRAY_SIZE(cif_dev->cif_rst); i++)
>> + if (cif_dev->cif_rst[i])
>> + reset_control_deassert(cif_dev->cif_rst[i]);
>> +}
>> +
>> +static int rkcif_plat_probe(struct platform_device *pdev)
>> +{
>> + const struct of_device_id *match;
>> + struct device_node *node = pdev->dev.of_node;
>> + struct device *dev = &pdev->dev;
>> + struct v4l2_device *v4l2_dev;
>> + struct rkcif_device *cif_dev;
>> + const struct cif_match_data *data;
>> + struct resource *res;
>> + int i, ret, irq;
>> +
>> + match = of_match_node(rkcif_plat_of_match, node);
>> + if (IS_ERR(match))
>> + return PTR_ERR(match);
>> +
>> + cif_dev = devm_kzalloc(dev, sizeof(*cif_dev), GFP_KERNEL);
>> + if (!cif_dev)
>> + return -ENOMEM;
>> +
>> + dev_set_drvdata(dev, cif_dev);
>> + cif_dev->dev = dev;
>> +
>> + irq = platform_get_irq(pdev, 0);
>> + if (irq < 0)
>> + return irq;
>> +
>> + ret = devm_request_irq(dev, irq, rkcif_irq_handler, IRQF_SHARED,
>> + dev_driver_string(dev), dev);
>> + if (ret < 0) {
>> + dev_err(dev, "request irq failed: %d\n", ret);
>> + return ret;
>> + }
>> +
>> + cif_dev->irq = irq;
>> + data = match->data;
>> + cif_dev->chip_id = data->chip_id;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + cif_dev->base_addr = devm_ioremap_resource(dev, res);
>> +
>> + if (IS_ERR(cif_dev->base_addr))
>> + return PTR_ERR(cif_dev->base_addr);
>> +
>> + if (data->clks_num > RKCIF_MAX_BUS_CLK ||
>> + data->rsts_num > RKCIF_MAX_RESET) {
>> + dev_err(dev, "out of range: clks(%d %d) rsts(%d %d)\n",
>> + data->clks_num, RKCIF_MAX_BUS_CLK,
>> + data->rsts_num, RKCIF_MAX_RESET);
>> + return -EINVAL;
>> + }
>> +
>> + for (i = 0; i < data->clks_num; i++) {
>> + struct clk *clk = devm_clk_get(dev, data->clks[i]);
>> +
>> + if (IS_ERR(clk)) {
>> + dev_err(dev, "failed to get %s\n", data->clks[i]);
>> + return PTR_ERR(clk);
>> + }
>> +
>> + cif_dev->clks[i] = clk;
>> + }
>> +
>> + cif_dev->clk_size = data->clks_num;
>> +
>> + for (i = 0; i < data->rsts_num; i++) {
>> + struct reset_control *rst =
>> + devm_reset_control_get(dev, data->rsts[i]);
>> + if (IS_ERR(rst)) {
>> + dev_err(dev, "failed to get %s\n", data->rsts[i]);
>> + return PTR_ERR(rst);
>> + }
>> + cif_dev->cif_rst[i] = rst;
>> + }
>> +
>> + /* Initialize the stream */
>> + rkcif_stream_init(cif_dev);
>> +
>> + strlcpy(cif_dev->media_dev.model, "rkcif",
>> + sizeof(cif_dev->media_dev.model));
>> + cif_dev->media_dev.dev = &pdev->dev;
>> + v4l2_dev = &cif_dev->v4l2_dev;
>> + v4l2_dev->mdev = &cif_dev->media_dev;
>> + strlcpy(v4l2_dev->name, "rkcif", sizeof(v4l2_dev->name));
>> + v4l2_ctrl_handler_init(&cif_dev->ctrl_handler, 8);
>> + v4l2_dev->ctrl_handler = &cif_dev->ctrl_handler;
>> +
>> + ret = v4l2_device_register(cif_dev->dev, &cif_dev->v4l2_dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + media_device_init(&cif_dev->media_dev);
>> +
>> + ret = media_device_register(&cif_dev->media_dev);
>> + if (ret < 0) {
>> + v4l2_err(v4l2_dev, "Failed to register media device: %d\n",
>> + ret);
>> + goto err_unreg_v4l2_dev;
>> + }
>> +
>> + /* create & register platefom subdev (from of_node) */
>
>platefom -> platform
>
>> + ret = rkcif_register_platform_subdevs(cif_dev);
>> + if (ret < 0)
>> + goto err_unreg_media_dev;
>> +
>> + ret = of_reserved_mem_device_init(dev);
>> + if (ret)
>> + v4l2_warn(v4l2_dev, "No reserved memory region assign to CIF\n");
>> +
>> + pm_runtime_enable(&pdev->dev);
>> +
>> + return 0;
>> +
>> +err_unreg_media_dev:
>> + media_device_unregister(&cif_dev->media_dev);
>> +err_unreg_v4l2_dev:
>> + v4l2_device_unregister(&cif_dev->v4l2_dev);
>> + return ret;
>> +}
>> +
>> +static int rkcif_plat_remove(struct platform_device *pdev)
>> +{
>> + struct rkcif_device *cif_dev = platform_get_drvdata(pdev);
>> +
>> + pm_runtime_disable(&pdev->dev);
>> +
>> + media_device_unregister(&cif_dev->media_dev);
>> + v4l2_device_unregister(&cif_dev->v4l2_dev);
>> + rkcif_unregister_stream_vdev(cif_dev);
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused rkcif_runtime_suspend(struct device *dev)
>> +{
>> + struct rkcif_device *cif_dev = dev_get_drvdata(dev);
>> +
>> + rkcif_disable_sys_clk(cif_dev);
>> +
>> + return pinctrl_pm_select_sleep_state(dev);
>> +}
>> +
>> +static int __maybe_unused rkcif_runtime_resume(struct device *dev)
>> +{
>> + struct rkcif_device *cif_dev = dev_get_drvdata(dev);
>> + int ret;
>> +
>> + ret = pinctrl_pm_select_default_state(dev);
>> + if (ret < 0)
>> + return ret;
>> + rkcif_enable_sys_clk(cif_dev);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct dev_pm_ops rkcif_plat_pm_ops = {
>> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
>> + pm_runtime_force_resume)
>> + SET_RUNTIME_PM_OPS(rkcif_runtime_suspend, rkcif_runtime_resume, NULL)
>> +};
>> +
>> +static struct platform_driver rkcif_plat_drv = {
>> + .driver = {
>> + .name = CIF_DRIVER_NAME,
>> + .of_match_table = of_match_ptr(rkcif_plat_of_match),
>> + .pm = &rkcif_plat_pm_ops,
>> + },
>> + .probe = rkcif_plat_probe,
>> + .remove = rkcif_plat_remove,
>> +};
>> +
>> +module_platform_driver(rkcif_plat_drv);
>> +MODULE_AUTHOR("Rockchip Camera/ISP team");
>> +MODULE_DESCRIPTION("Rockchip CIF platform driver");
>> +MODULE_LICENSE("GPL");
>> diff --git a/drivers/media/platform/rockchip/cif/dev.h b/drivers/media/platform/rockchip/cif/dev.h
>> new file mode 100644
>> index 000000000000..3dc47d461d04
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/cif/dev.h
>> @@ -0,0 +1,208 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Rockchip CIF Driver
>> + *
>> + * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#ifndef _RKCIF_DEV_H
>> +#define _RKCIF_DEV_H
>> +
>> +#include <linux/mutex.h>
>> +#include <media/media-device.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-device.h>
>> +#include <media/videobuf2-v4l2.h>
>> +
>> +#define CIF_DRIVER_NAME "rkcif"
>> +#define CIF_VIDEODEVICE_NAME "stream_cif"
>> +
>> +#define RKCIF_MAX_BUS_CLK 8
>> +#define RKCIF_MAX_SENSOR 2
>> +#define RKCIF_MAX_RESET 5
>> +#define RKCIF_MAX_CSI_CHANNEL 4
>> +
>> +#define RKCIF_DEFAULT_WIDTH 640
>> +#define RKCIF_DEFAULT_HEIGHT 480
>> +
>> +#define write_cif_reg(base, addr, val) writel(val, (addr) + (base))
>> +#define read_cif_reg(base, addr) readl((addr) + (base))
>> +
>> +#define write_csihost_reg(base, addr, val) writel(val, (addr) + (base))
>> +#define read_csihost_reg(base, addr) readl((addr) + (base))
>> +
>> +enum rkcif_state {
>> + RKCIF_STATE_DISABLED,
>> + RKCIF_STATE_READY,
>> + RKCIF_STATE_STREAMING
>> +};
>> +
>> +enum rkcif_chip_id {
>> + CHIP_PX30_CIF,
>> + CHIP_RK1808_CIF,
>> + CHIP_RK3128_CIF,
>> + CHIP_RK3288_CIF
>> +};
>> +
>> +enum host_type_t {
>> + RK_CSI_RXHOST,
>> + RK_DSI_RXHOST
>> +};
>> +
>> +struct rkcif_buffer {
>> + struct vb2_v4l2_buffer vb;
>> + struct list_head queue;
>> + union {
>> + u32 buff_addr[VIDEO_MAX_PLANES];
>> + void *vaddr[VIDEO_MAX_PLANES];
>> + };
>> +};
>> +
>> +struct rkcif_dummy_buffer {
>> + void *vaddr;
>> + dma_addr_t dma_addr;
>> + u32 size;
>> +};
>> +
>> +extern int rkcif_debug;
>> +
>> +static inline struct rkcif_buffer *to_rkcif_buffer(struct vb2_v4l2_buffer *vb)
>> +{
>> + return container_of(vb, struct rkcif_buffer, vb);
>> +}
>> +
>> +/*
>> + * struct rkcif_sensor_info - Sensor infomations
>> + * @mbus: media bus configuration
>> + */
>> +struct rkcif_sensor_info {
>> + struct v4l2_subdev *sd;
>> + int pad;
>> + struct v4l2_mbus_config mbus;
>> + int lanes;
>> +};
>> +
>> +/*
>> + * struct cif_output_fmt - The output format
>> + *
>> + * @fourcc: pixel format in fourcc
>> + * @cplanes: number of colour planes
>> + * @fmt_val: the fmt val corresponding to CIF_FOR register
>> + * @bpp: bits per pixel for each cplanes
>> + */
>> +struct cif_output_fmt {
>> + u32 fourcc;
>> + u32 mbus;
>> + u8 cplanes;
>> + u8 mplanes;
>> + u32 fmt_val;
>> + u8 bpp[VIDEO_MAX_PLANES];
>> +};
>> +
>> +enum cif_fmt_type {
>> + CIF_FMT_TYPE_YUV = 0,
>> + CIF_FMT_TYPE_RAW,
>> +};
>> +
>> +/*
>> + * struct cif_input_fmt - The input mbus format from sensor
>> + *
>> + * @mbus_code: mbus format
>> + * @dvp_fmt_val: the fmt val corresponding to CIF_FOR register
>> + * @csi_fmt_val: the fmt val corresponding to CIF_CSI_ID_CTRL
>> + * @field: the field type of the input from sensor
>> + */
>> +struct cif_input_fmt {
>> + u32 mbus_code;
>> + u32 dvp_fmt_val;
>> + u32 csi_fmt_val;
>> + enum cif_fmt_type fmt_type;
>> + enum v4l2_field field;
>> +};
>> +
>> +/*
>> + * struct rkcif_stream - Stream states TODO
>> + *
>> + * @vbq_lock: lock to protect buf_queue
>> + * @buf_queue: queued buffer list
>> + * @dummy_buf: dummy space to store dropped data
>> + *
>> + * rkcif use shadowsock registers, so it need two buffer at a time
>> + * @curr_buf: the buffer used for current frame
>> + * @next_buf: the buffer used for next frame
>> + */
>> +struct rkcif_stream {
>> + struct rkcif_device *cifdev;
>> + enum rkcif_state state;
>> + bool stopping;
>> + wait_queue_head_t wq_stopped;
>> + int frame_idx;
>> + int frame_phase;
>> +
>> + /* lock between irq and buf_queue */
>> + spinlock_t vbq_lock;
>> + struct vb2_queue buf_queue;
>> + struct list_head buf_head;
>> + struct rkcif_dummy_buffer dummy_buf;
>> + struct rkcif_buffer *curr_buf;
>> + struct rkcif_buffer *next_buf;
>> +
>> + /* vfd lock */
>> + struct mutex vlock;
>> + struct video_device vdev;
>> + /* TODO: pad for dvp and mipi separately? */
>> + struct media_pad pad;
>> +
>> + const struct cif_output_fmt *cif_fmt_out;
>> + const struct cif_input_fmt *cif_fmt_in;
>> + struct v4l2_pix_format_mplane pixm;
>> + struct v4l2_rect crop;
>> + int crop_enable;
>> +};
>> +
>> +static inline struct rkcif_stream *to_rkcif_stream(struct video_device *vdev)
>> +{
>> + return container_of(vdev, struct rkcif_stream, vdev);
>> +}
>> +
>> +/*
>> + * struct rkcif_device - ISP platform device
>> + * @base_addr: base register address
>> + * @active_sensor: sensor in-use, set when streaming on
>> + * @stream: capture video device
>> + */
>> +struct rkcif_device {
>> + struct list_head list;
>> + struct device *dev;
>> + int irq;
>> + void __iomem *base_addr;
>> + void __iomem *csi_base;
>> + struct clk *clks[RKCIF_MAX_BUS_CLK];
>> + int clk_size;
>> + struct vb2_alloc_ctx *alloc_ctx;
>> + bool iommu_en;
>> + struct iommu_domain *domain;
>> + struct reset_control *cif_rst[RKCIF_MAX_RESET];
>> +
>> + struct v4l2_device v4l2_dev;
>> + struct media_device media_dev;
>> + struct v4l2_ctrl_handler ctrl_handler;
>> + struct v4l2_async_notifier notifier;
>> + struct v4l2_async_subdev asd;
>> + struct rkcif_sensor_info sensor;
>> +
>> + struct rkcif_stream stream;
>> +
>> + int chip_id;
>> +};
>> +
>> +void rkcif_unregister_stream_vdev(struct rkcif_device *dev);
>> +int rkcif_register_stream_vdev(struct rkcif_device *dev);
>> +void rkcif_stream_init(struct rkcif_device *dev);
>> +
>> +void rkcif_irq_oneframe(struct rkcif_device *cif_dev);
>> +void rkcif_irq_pingpong(struct rkcif_device *cif_dev);
>> +void rkcif_soft_reset(struct rkcif_device *cif_dev);
>> +
>> +#endif
>> diff --git a/drivers/media/platform/rockchip/cif/regs.h b/drivers/media/platform/rockchip/cif/regs.h
>> new file mode 100644
>> index 000000000000..5e0f926c70d3
>> --- /dev/null
>> +++ b/drivers/media/platform/rockchip/cif/regs.h
>> @@ -0,0 +1,256 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Rockchip CIF Driver
>> + *
>> + * Copyright (C) 2018 Rockchip Electronics Co., Ltd.
>> + */
>> +
>> +#ifndef _RKCIF_REGS_H
>> +#define _RKCIF_REGS_H
>> +
>> +/* CIF Reg Offset */
>> +#define CIF_CTRL 0x00
>> +#define CIF_INTEN 0x04
>> +#define CIF_INTSTAT 0x08
>> +#define CIF_FOR 0x0c
>> +#define CIF_LINE_NUM_ADDR 0x10
>> +#define CIF_FRM0_ADDR_Y 0x14
>> +#define CIF_FRM0_ADDR_UV 0x18
>> +#define CIF_FRM1_ADDR_Y 0x1c
>> +#define CIF_FRM1_ADDR_UV 0x20
>> +#define CIF_VIR_LINE_WIDTH 0x24
>> +#define CIF_SET_SIZE 0x28
>> +#define CIF_SCM_ADDR_Y 0x2c
>> +#define CIF_SCM_ADDR_U 0x30
>> +#define CIF_SCM_ADDR_V 0x34
>> +#define CIF_WB_UP_FILTER 0x38
>> +#define CIF_WB_LOW_FILTER 0x3c
>> +#define CIF_WBC_CNT 0x40
>> +#define CIF_CROP 0x44
>> +#define CIF_SCL_CTRL 0x48
>> +#define CIF_SCL_DST 0x4c
>> +#define CIF_SCL_FCT 0x50
>> +#define CIF_SCL_VALID_NUM 0x54
>> +#define CIF_LINE_LOOP_CTR 0x58
>> +#define CIF_FRAME_STATUS 0x60
>> +#define CIF_CUR_DST 0x64
>> +#define CIF_LAST_LINE 0x68
>> +#define CIF_LAST_PIX 0x6c
>> +
>> +/* RK1808 CIF CSI Registers Offset */
>> +#define CIF_CSI_ID0_CTRL0 0x80
>> +#define CIF_CSI_ID0_CTRL1 0x84
>> +#define CIF_CSI_ID1_CTRL0 0x88
>> +#define CIF_CSI_ID1_CTRL1 0x8c
>> +#define CIF_CSI_ID2_CTRL0 0x90
>> +#define CIF_CSI_ID2_CTRL1 0x94
>> +#define CIF_CSI_ID3_CTRL0 0x98
>> +#define CIF_CSI_ID3_CTRL1 0x9c
>> +#define CIF_CSI_WATER_LINE 0xa0
>> +#define CIF_CSI_FRM0_ADDR_Y_ID0 0xa4
>> +#define CIF_CSI_FRM1_ADDR_Y_ID0 0xa8
>> +#define CIF_CSI_FRM0_ADDR_UV_ID0 0xac
>> +#define CIF_CSI_FRM1_ADDR_UV_ID0 0xb0
>> +#define CIF_CSI_FRM0_VLW_Y_ID0 0xb4
>> +#define CIF_CSI_FRM1_VLW_Y_ID0 0xb8
>> +#define CIF_CSI_FRM0_VLW_UV_ID0 0xbc
>> +#define CIF_CSI_FRM1_VLW_UV_ID0 0xc0
>> +#define CIF_CSI_FRM0_ADDR_Y_ID1 0xc4
>> +#define CIF_CSI_FRM1_ADDR_Y_ID1 0xc8
>> +#define CIF_CSI_FRM0_ADDR_UV_ID1 0xcc
>> +#define CIF_CSI_FRM1_ADDR_UV_ID1 0xd0
>> +#define CIF_CSI_FRM0_VLW_Y_ID1 0xd4
>> +#define CIF_CSI_FRM1_VLW_Y_ID1 0xd8
>> +#define CIF_CSI_FRM0_VLW_UV_ID1 0xdc
>> +#define CIF_CSI_FRM1_VLW_UV_ID1 0xe0
>> +#define CIF_CSI_FRM0_ADDR_Y_ID2 0xe4
>> +#define CIF_CSI_FRM1_ADDR_Y_ID2 0xe8
>> +#define CIF_CSI_FRM0_ADDR_UV_ID2 0xec
>> +#define CIF_CSI_FRM1_ADDR_UV_ID2 0xf0
>> +#define CIF_CSI_FRM0_VLW_Y_ID2 0xf4
>> +#define CIF_CSI_FRM1_VLW_Y_ID2 0xf8
>> +#define CIF_CSI_FRM0_VLW_UV_ID2 0xfc
>> +#define CIF_CSI_FRM1_VLW_UV_ID2 0x100
>> +#define CIF_CSI_FRM0_ADDR_Y_ID3 0x104
>> +#define CIF_CSI_FRM1_ADDR_Y_ID3 0x108
>> +#define CIF_CSI_FRM0_ADDR_UV_ID3 0x10c
>> +#define CIF_CSI_FRM1_ADDR_UV_ID3 0x110
>> +#define CIF_CSI_FRM0_VLW_Y_ID3 0x114
>> +#define CIF_CSI_FRM1_VLW_Y_ID3 0x118
>> +#define CIF_CSI_FRM0_VLW_UV_ID3 0x11c
>> +#define CIF_CSI_FRM1_VLW_UV_ID3 0x120
>> +#define CIF_CSI_INTEN 0x124
>> +#define CIF_CSI_INTSTAT 0x128
>> +#define CIF_CSI_LINE_INT_NUM_ID0_1 0x12c
>> +#define CIF_CSI_LINE_INT_NUM_ID2_3 0x130
>> +#define CIF_CSI_LINE_CNT_ID0_1 0x134
>> +#define CIF_CSI_LINE_CNT_ID2_3 0x138
>> +#define CIF_CSI_ID0_CROP_START 0x13c
>> +#define CIF_CSI_ID1_CROP_START 0x140
>> +#define CIF_CSI_ID2_CROP_START 0x144
>> +#define CIF_CSI_ID3_CROP_START 0x148
>> +
>> +/* The key register bit description */
>> +
>> +/* CIF_CTRL Reg */
>> +#define DISABLE_CAPTURE (0x0 << 0)
>> +#define ENABLE_CAPTURE (0x1 << 0)
>> +#define MODE_ONEFRAME (0x0 << 1)
>> +#define MODE_PINGPONG (0x1 << 1)
>> +#define MODE_LINELOOP (0x2 << 1)
>> +#define AXI_BURST_16 (0xF << 12)
>> +
>> +/* CIF_INTEN */
>> +#define INTEN_DISABLE (0x0 << 0)
>> +#define FRAME_END_EN (0x1 << 0)
>> +#define LINE_ERR_EN (0x1 << 2)
>> +#define BUS_ERR_EN (0x1 << 6)
>> +#define SCL_ERR_EN (0x1 << 7)
>> +#define PST_INF_FRAME_END_EN (0x1 << 9)
>> +
>> +/* CIF INTSTAT */
>> +#define INTSTAT_CLS (0x3FF)
>> +#define FRAME_END (0x01 << 0)
>> +#define LINE_ERR (0x01 << 2)
>> +#define PST_INF_FRAME_END (0x01 << 9)
>> +#define FRAME_END_CLR (0x01 << 0)
>> +#define LINE_ERR_CLR (0x01 << 2)
>> +#define PST_INF_FRAME_END_CLR (0x01 << 9)
>> +#define INTSTAT_ERR (0xFC)
>> +
>> +/* FRAME STATUS */
>> +#define FRAME_STAT_CLS 0x00
>> +#define FRM0_STAT_CLS 0x20 /* write 0 to clear frame 0 */
>> +
>> +/* CIF FORMAT */
>> +#define VSY_HIGH_ACTIVE (0x01 << 0)
>> +#define VSY_LOW_ACTIVE (0x00 << 0)
>> +#define HSY_LOW_ACTIVE (0x01 << 1)
>> +#define HSY_HIGH_ACTIVE (0x00 << 1)
>> +#define INPUT_MODE_YUV (0x00 << 2)
>> +#define INPUT_MODE_PAL (0x02 << 2)
>> +#define INPUT_MODE_NTSC (0x03 << 2)
>> +#define INPUT_MODE_BT1120 (0x07 << 2)
>> +#define INPUT_MODE_RAW (0x04 << 2)
>> +#define INPUT_MODE_JPEG (0x05 << 2)
>> +#define INPUT_MODE_MIPI (0x06 << 2)
>> +#define YUV_INPUT_ORDER_UYVY (0x00 << 5)
>> +#define YUV_INPUT_ORDER_YVYU (0x01 << 5)
>> +#define YUV_INPUT_ORDER_VYUY (0x10 << 5)
>> +#define YUV_INPUT_ORDER_YUYV (0x03 << 5)
>> +#define YUV_INPUT_422 (0x00 << 7)
>> +#define YUV_INPUT_420 (0x01 << 7)
>> +#define INPUT_420_ORDER_EVEN (0x00 << 8)
>> +#define INPUT_420_ORDER_ODD (0x01 << 8)
>> +#define CCIR_INPUT_ORDER_ODD (0x00 << 9)
>> +#define CCIR_INPUT_ORDER_EVEN (0x01 << 9)
>> +#define RAW_DATA_WIDTH_8 (0x00 << 11)
>> +#define RAW_DATA_WIDTH_10 (0x01 << 11)
>> +#define RAW_DATA_WIDTH_12 (0x02 << 11)
>> +#define YUV_OUTPUT_422 (0x00 << 16)
>> +#define YUV_OUTPUT_420 (0x01 << 16)
>> +#define OUTPUT_420_ORDER_EVEN (0x00 << 17)
>> +#define OUTPUT_420_ORDER_ODD (0x01 << 17)
>> +#define RAWD_DATA_LITTLE_ENDIAN (0x00 << 18)
>> +#define RAWD_DATA_BIG_ENDIAN (0x01 << 18)
>> +#define UV_STORAGE_ORDER_UVUV (0x00 << 19)
>> +#define UV_STORAGE_ORDER_VUVU (0x01 << 19)
>> +#define BT1120_CLOCK_SINGLE_EDGES (0x00 << 24)
>> +#define BT1120_CLOCK_DOUBLE_EDGES (0x01 << 24)
>> +#define BT1120_TRANSMIT_INTERFACE (0x00 << 25)
>> +#define BT1120_TRANSMIT_PROGRESS (0x01 << 25)
>> +#define BT1120_YC_SWAP (0x01 << 26)
>> +
>> +/* CIF_SCL_CTRL */
>> +#define ENABLE_SCL_DOWN (0x01 << 0)
>> +#define DISABLE_SCL_DOWN (0x00 << 0)
>> +#define ENABLE_SCL_UP (0x01 << 1)
>> +#define DISABLE_SCL_UP (0x00 << 1)
>> +#define ENABLE_YUV_16BIT_BYPASS (0x01 << 4)
>> +#define DISABLE_YUV_16BIT_BYPASS (0x00 << 4)
>> +#define ENABLE_RAW_16BIT_BYPASS (0x01 << 5)
>> +#define DISABLE_RAW_16BIT_BYPASS (0x00 << 5)
>> +#define ENABLE_32BIT_BYPASS (0x01 << 6)
>> +#define DISABLE_32BIT_BYPASS (0x00 << 6)
>> +
>> +/* CIF_INTSTAT */
>> +#define CIF_F0_READY (0x01 << 0)
>> +#define CIF_F1_READY (0x01 << 1)
>> +
>> +/* CIF CROP */
>> +#define CIF_CROP_Y_SHIFT 16
>> +#define CIF_CROP_X_SHIFT 0
>> +
>> +/* CIF_CSI_ID_CTRL0 */
>> +#define CSI_DISABLE_CAPTURE (0x0 << 0)
>> +#define CSI_ENABLE_CAPTURE (0x1 << 0)
>> +#define CSI_WRDDR_TYPE_RAW8 (0x0 << 1)
>> +#define CSI_WRDDR_TYPE_RAW10 (0x1 << 1)
>> +#define CSI_WRDDR_TYPE_RAW12 (0x2 << 1)
>> +#define CSI_WRDDR_TYPE_RGB888 (0x3 << 1)
>> +#define CSI_WRDDR_TYPE_YUV422 (0x4 << 1)
>> +#define CSI_DISABLE_COMMAND_MODE (0x0 << 4)
>> +#define CSI_ENABLE_COMMAND_MODE (0x1 << 4)
>> +#define CSI_DISABLE_CROP (0x0 << 5)
>> +#define CSI_ENABLE_CROP (0x1 << 5)
>> +
>> +/* CIF_CSI_INTEN */
>> +#define CSI_FRAME0_START_INTEN(id) (0x1 << ((id) * 2))
>> +#define CSI_FRAME1_START_INTEN(id) (0x1 << ((id) * 2 + 1))
>> +#define CSI_FRAME0_END_INTEN(id) (0x1 << ((id) * 2 + 8))
>> +#define CSI_FRAME1_END_INTEN(id) (0x1 << ((id) * 2 + 9))
>> +#define CSI_DMA_Y_FIFO_OVERFLOW_INTEN (0x1 << 16)
>> +#define CSI_DMA_UV_FIFO_OVERFLOW_INTEN (0x1 << 17)
>> +#define CSI_CONFIG_FIFO_OVERFLOW_INTEN (0x1 << 18)
>> +#define CSI_BANDWIDTH_LACK_INTEN (0x1 << 19)
>> +#define CSI_RX_FIFO_OVERFLOW_INTEN (0x1 << 20)
>> +#define CSI_ALL_FRAME_START_INTEN (0xff << 0)
>> +#define CSI_ALL_FRAME_END_INTEN (0xff << 8)
>> +#define CSI_ALL_ERROR_INTEN (0x1f << 16)
>> +
>> +/* CIF_CSI_INTSTAT */
>> +#define CSI_FRAME0_START_ID0 (0x1 << 0)
>> +#define CSI_FRAME1_START_ID0 (0x1 << 1)
>> +#define CSI_FRAME0_START_ID1 (0x1 << 2)
>> +#define CSI_FRAME1_START_ID1 (0x1 << 3)
>> +#define CSI_FRAME0_START_ID2 (0x1 << 4)
>> +#define CSI_FRAME1_START_ID2 (0x1 << 5)
>> +#define CSI_FRAME0_START_ID3 (0x1 << 6)
>> +#define CSI_FRAME1_START_ID3 (0x1 << 7)
>> +#define CSI_FRAME0_END_ID0 (0x1 << 8)
>> +#define CSI_FRAME1_END_ID0 (0x1 << 9)
>> +#define CSI_FRAME0_END_ID1 (0x1 << 10)
>> +#define CSI_FRAME1_END_ID1 (0x1 << 11)
>> +#define CSI_FRAME0_END_ID2 (0x1 << 12)
>> +#define CSI_FRAME1_END_ID2 (0x1 << 13)
>> +#define CSI_FRAME0_END_ID3 (0x1 << 14)
>> +#define CSI_FRAME1_END_ID3 (0x1 << 15)
>> +#define CSI_DMA_Y_FIFO_OVERFLOW (0x1 << 16)
>> +#define CSI_DMA_UV_FIFO_OVERFLOW (0x1 << 17)
>> +#define CSI_CONFIG_FIFO_OVERFLOW (0x1 << 18)
>> +#define CSI_BANDWIDTH_LACK (0x1 << 19)
>> +#define CSI_RX_FIFO_OVERFLOW (0x1 << 20)
>> +
>> +#define CSI_FIFO_OVERFLOW (CSI_DMA_Y_FIFO_OVERFLOW | \
>> + CSI_DMA_UV_FIFO_OVERFLOW | \
>> + CSI_CONFIG_FIFO_OVERFLOW | \
>> + CSI_RX_FIFO_OVERFLOW)
>> +
>> +/* CSI Host Registers Define */
>> +#define CSIHOST_N_LANES 0x04
>> +#define CSIHOST_PHY_RSTZ 0x0c
>> +#define CSIHOST_RESETN 0x10
>> +#define CSIHOST_ERR1 0x20
>> +#define CSIHOST_ERR2 0x24
>> +#define CSIHOST_MSK1 0x28
>> +#define CSIHOST_MSK2 0x2c
>> +#define CSIHOST_CONTROL 0x40
>> +
>> +#define SW_CPHY_EN(x) ((x) << 0)
>> +#define SW_DSI_EN(x) ((x) << 4)
>> +#define SW_DATATYPE_FS(x) ((x) << 8)
>> +#define SW_DATATYPE_FE(x) ((x) << 14)
>> +#define SW_DATATYPE_LS(x) ((x) << 20)
>> +#define SW_DATATYPE_LE(x) ((x) << 26)
>> +
>> +#endif
>>
>
>Regards,
>
> Hans
--
Maxime Chevallier, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
_______________________________________________
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] i2c: imx-lpi2c: Fix runtime PM imbalance in lpi2c_imx_master_enable()
From: Markus Elfring @ 2020-06-01 6:42 UTC (permalink / raw)
To: Dinghao Liu, Fugang Duan, kernel, linux-imx, linux-i2c,
linux-arm-kernel
Cc: Dong Aisheng, kernel-janitors, Sascha Hauer, Kangjie Lu,
linux-kernel, Wolfram Sang, Shawn Guo, Fabio Estevam
In-Reply-To: <20200601061640.27632-1-dinghao.liu@zju.edu.cn>
> pm_runtime_get_sync() increments the runtime PM usage counter even
> the call returns an error code. Thus a corresponding decrement is
> needed on the error handling path to keep the counter balanced.
>
> Fix this by adding the missed function call.
How do you think about a wording variant like the following?
Change description:
The PM runtime usage counter is incremented even if a call of
the function “pm_runtime_get_sync” failed. Thus decrement it also
in an error case so that the reference counting is kept consistent.
Regards,
Markus
_______________________________________________
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 4/4] dt-bindings: drm: bridge: adi, adv7511.txt: convert to yaml
From: Ricardo Cañuelo @ 2020-06-01 6:33 UTC (permalink / raw)
To: laurent.pinchart
Cc: marex, devicetree, michal.simek, xuwei5, robh+dt, mcoquelin.stm32,
kernel, linux-arm-kernel
In-Reply-To: <20200601063308.13045-1-ricardo.canuelo@collabora.com>
Convert the ADV7511/11w/13/33/35 DT bindings to json-schema. The
original binding has been split into two files: adi,adv7511.yaml for
ADV7511/11W/13 and adi,adv7533.yaml for ADV7533/35.
Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
---
.../bindings/display/bridge/adi,adv7511.txt | 143 -----------
.../bindings/display/bridge/adi,adv7511.yaml | 231 ++++++++++++++++++
.../bindings/display/bridge/adi,adv7533.yaml | 175 +++++++++++++
3 files changed, 406 insertions(+), 143 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
create mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
create mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7533.yaml
diff --git a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
deleted file mode 100644
index 659523f538bf..000000000000
--- a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
+++ /dev/null
@@ -1,143 +0,0 @@
-Analog Devices ADV7511(W)/13/33/35 HDMI Encoders
-------------------------------------------------
-
-The ADV7511, ADV7511W, ADV7513, ADV7533 and ADV7535 are HDMI audio and video
-transmitters compatible with HDMI 1.4 and DVI 1.0. They support color space
-conversion, S/PDIF, CEC and HDCP. ADV7533/5 supports the DSI interface for input
-pixels, while the others support RGB interface.
-
-Required properties:
-
-- compatible: Should be one of:
- "adi,adv7511"
- "adi,adv7511w"
- "adi,adv7513"
- "adi,adv7533"
- "adi,adv7535"
-
-- reg: I2C slave addresses
- The ADV7511 internal registers are split into four pages exposed through
- different I2C addresses, creating four register maps. Each map has it own
- I2C address and acts as a standard slave device on the I2C bus. The main
- address is mandatory, others are optional and revert to defaults if not
- specified.
-
-
-The ADV7511 supports a large number of input data formats that differ by their
-color depth, color format, clock mode, bit justification and random
-arrangement of components on the data bus. The combination of the following
-properties describe the input and map directly to the video input tables of the
-ADV7511 datasheet that document all the supported combinations.
-
-- adi,input-depth: Number of bits per color component at the input (8, 10 or
- 12).
-- adi,input-colorspace: The input color space, one of "rgb", "yuv422" or
- "yuv444".
-- adi,input-clock: The input clock type, one of "1x" (one clock cycle per
- pixel), "2x" (two clock cycles per pixel), "ddr" (one clock cycle per pixel,
- data driven on both edges).
-
-The following input format properties are required except in "rgb 1x" and
-"yuv444 1x" modes, in which case they must not be specified.
-
-- adi,input-style: The input components arrangement variant (1, 2 or 3), as
- listed in the input format tables in the datasheet.
-- adi,input-justification: The input bit justification ("left", "evenly",
- "right").
-
-- avdd-supply: A 1.8V supply that powers up the AVDD pin on the chip.
-- dvdd-supply: A 1.8V supply that powers up the DVDD pin on the chip.
-- pvdd-supply: A 1.8V supply that powers up the PVDD pin on the chip.
-- dvdd-3v-supply: A 3.3V supply that powers up the pin called DVDD_3V
- on the chip.
-- bgvdd-supply: A 1.8V supply that powers up the BGVDD pin. This is
- needed only for ADV7511.
-
-The following properties are required for ADV7533 and ADV7535:
-
-- adi,dsi-lanes: Number of DSI data lanes connected to the DSI host. It should
- be one of 1, 2, 3 or 4.
-- a2vdd-supply: 1.8V supply that powers up the A2VDD pin on the chip.
-- v3p3-supply: A 3.3V supply that powers up the V3P3 pin on the chip.
-- v1p2-supply: A supply that powers up the V1P2 pin on the chip. It can be
- either 1.2V or 1.8V for ADV7533 but only 1.8V for ADV7535.
-
-Optional properties:
-
-- interrupts: Specifier for the ADV7511 interrupt
-- pd-gpios: Specifier for the GPIO connected to the power down signal
-
-- adi,clock-delay: Video data clock delay relative to the pixel clock, in ps
- (-1200 ps .. 1600 ps). Defaults to no delay.
-- adi,embedded-sync: The input uses synchronization signals embedded in the
- data stream (similar to BT.656). Defaults to separate H/V synchronization
- signals.
-- adi,disable-timing-generator: Only for ADV7533 and ADV7535. Disables the
- internal timing generator. The chip will rely on the sync signals in the
- DSI data lanes, rather than generate its own timings for HDMI output.
-- clocks: from common clock binding: reference to the CEC clock.
-- clock-names: from common clock binding: must be "cec".
-- reg-names : Names of maps with programmable addresses.
- It can contain any map needing a non-default address.
- Possible maps names are : "main", "edid", "cec", "packet"
-
-Required nodes:
-
-The ADV7511 has two video ports. Their connections are modelled using the OF
-graph bindings specified in Documentation/devicetree/bindings/graph.txt.
-
-- Video port 0 for the RGB, YUV or DSI input. In the case of ADV7533/5, the
- remote endpoint phandle should be a reference to a valid mipi_dsi_host device
- node.
-- Video port 1 for the HDMI output
-- Audio port 2 for the HDMI audio input
-
-
-Example
--------
-
- adv7511w: hdmi@39 {
- compatible = "adi,adv7511w";
- /*
- * The EDID page will be accessible on address 0x66 on the I2C
- * bus. All other maps continue to use their default addresses.
- */
- reg = <0x39>, <0x66>;
- reg-names = "main", "edid";
- interrupt-parent = <&gpio3>;
- interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
- clocks = <&cec_clock>;
- clock-names = "cec";
-
- adi,input-depth = <8>;
- adi,input-colorspace = "rgb";
- adi,input-clock = "1x";
- adi,input-style = <1>;
- adi,input-justification = "evenly";
-
- ports {
- #address-cells = <1>;
- #size-cells = <0>;
-
- port@0 {
- reg = <0>;
- adv7511w_in: endpoint {
- remote-endpoint = <&dpi_out>;
- };
- };
-
- port@1 {
- reg = <1>;
- adv7511_out: endpoint {
- remote-endpoint = <&hdmi_connector_in>;
- };
- };
-
- port@2 {
- reg = <2>;
- codec_endpoint: endpoint {
- remote-endpoint = <&i2s0_cpu_endpoint>;
- };
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
new file mode 100644
index 000000000000..71b344e812dd
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
@@ -0,0 +1,231 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/adi,adv7511.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADV7511/11W/13 HDMI Encoders
+
+maintainers:
+ - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+description: |
+ The ADV7511, ADV7511W and ADV7513 are HDMI audio and video
+ transmitters compatible with HDMI 1.4 and DVI 1.0. They support color
+ space conversion, S/PDIF, CEC and HDCP. The transmitter input is
+ parallel RGB or YUV data.
+
+properties:
+ compatible:
+ enum:
+ - adi,adv7511
+ - adi,adv7511w
+ - adi,adv7513
+
+ reg:
+ description: |
+ I2C slave addresses.
+
+ The ADV7511/11W/13 internal registers are split into four pages
+ exposed through different I2C addresses, creating four register
+ maps. Each map has it own I2C address and acts as a standard slave
+ device on the I2C bus. The main address is mandatory, others are
+ optional and revert to defaults if not specified.
+ minItems: 1
+ maxItems: 4
+
+ reg-names:
+ description:
+ Names of maps with programmable addresses. It can contain any map
+ needing a non-default address.
+ minItems: 1
+ items:
+ - const: main
+ - const: edid
+ - const: cec
+ - const: packet
+
+ clocks:
+ description: Reference to the CEC clock.
+ maxItems: 1
+
+ clock-names:
+ const: cec
+
+ interrupts:
+ maxItems: 1
+
+ pd-gpios:
+ description: GPIO connected to the power down signal.
+ maxItems: 1
+
+ avdd-supply:
+ description: A 1.8V supply that powers up the AVDD pin.
+
+ dvdd-supply:
+ description: A 1.8V supply that powers up the DVDD pin.
+
+ pvdd-supply:
+ description: A 1.8V supply that powers up the PVDD pin.
+
+ dvdd-3v-supply:
+ description: A 3.3V supply that powers up the DVDD_3V pin.
+
+ bgvdd-supply:
+ description: A 1.8V supply that powers up the BGVDD pin.
+
+ adi,input-depth:
+ description: Number of bits per color component at the input.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [ 8, 10, 12 ]
+
+ adi,input-colorspace:
+ description: Input color space.
+ enum: [ rgb, yuv422, yuv444 ]
+
+ adi,input-clock:
+ description: |
+ Input clock type.
+ "1x": one clock cycle per pixel
+ "2x": two clock cycles per pixel
+ "dd": one clock cycle per pixel, data driven on both edges
+ enum: [ 1x, 2x, dd ]
+
+ adi,clock-delay:
+ description:
+ Video data clock delay relative to the pixel clock, in ps
+ (-1200ps .. 1600 ps).
+ $ref: /schemas/types.yaml#/definitions/uint32
+ default: 0
+
+ adi,embedded-sync:
+ description:
+ If defined, the input uses synchronization signals embedded in the
+ data stream (similar to BT.656).
+ type: boolean
+
+ adi,input-style:
+ description:
+ Input components arrangement variant as listed in the input
+ format tables in the datasheet.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [ 1, 2, 3 ]
+
+ adi,input-justification:
+ description: Input bit justification.
+ enum: [ left, evenly, right ]
+
+ ports:
+ description:
+ The ADV7511(W)/13 has two video ports and one audio port. This node
+ models their connections as documented in
+ Documentation/devicetree/bindings/media/video-interfaces.txt
+ Documentation/devicetree/bindings/graph.txt
+ type: object
+ properties:
+ port@0:
+ description: Video port for the RGB or YUV input.
+ type: object
+
+ port@1:
+ description: Video port for the HDMI output.
+ type: object
+
+ port@2:
+ description: Audio port for the HDMI output.
+ type: object
+
+# adi,input-colorspace and adi,input-clock are required except in
+# "rgb 1x" and "yuv444 1x" modes, in which case they must not be
+# specified.
+if:
+ not:
+ properties:
+ adi,input-colorspace:
+ contains:
+ enum: [ rgb, yuv444 ]
+ adi,input-clock:
+ contains:
+ const: 1x
+
+then:
+ required:
+ - adi,input-style
+ - adi,input-justification
+
+else:
+ properties:
+ adi,input-style: false
+ adi,input-justification: false
+
+
+required:
+ - compatible
+ - reg
+ - ports
+ - adi,input-depth
+ - adi,input-colorspace
+ - adi,input-clock
+ - avdd-supply
+ - dvdd-supply
+ - pvdd-supply
+ - dvdd-3v-supply
+ - bgvdd-supply
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ adv7511w: hdmi@39 {
+ compatible = "adi,adv7511w";
+ /*
+ * The EDID page will be accessible on address 0x66 on the I2C
+ * bus. All other maps continue to use their default addresses.
+ */
+ reg = <0x39>, <0x66>;
+ reg-names = "main", "edid";
+ interrupt-parent = <&gpio3>;
+ interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
+ clocks = <&cec_clock>;
+ clock-names = "cec";
+ avdd-supply = <&v1v8>;
+ dvdd-supply = <&v1v8>;
+ pvdd-supply = <&v1v8>;
+ dvdd-3v-supply = <&v3v3>;
+ bgvdd-supply = <&v1v8>;
+
+ adi,input-depth = <8>;
+ adi,input-colorspace = "yuv422";
+ adi,input-clock = "1x";
+
+ adi,input-style = <3>;
+ adi,input-justification = "right";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ adv7511w_in: endpoint {
+ remote-endpoint = <&dpi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ adv7511_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ codec_endpoint: endpoint {
+ remote-endpoint = <&i2s0_cpu_endpoint>;
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/display/bridge/adi,adv7533.yaml b/Documentation/devicetree/bindings/display/bridge/adi,adv7533.yaml
new file mode 100644
index 000000000000..18761f49e5fe
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/adi,adv7533.yaml
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: GPL-2.0-only
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/adi,adv7533.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Analog Devices ADV7533/35 HDMI Encoders
+
+maintainers:
+ - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+
+description: |
+ The ADV7533 and ADV7535 are HDMI audio and video transmitters
+ compatible with HDMI 1.4 and DVI 1.0. They support color space
+ conversion, S/PDIF, CEC and HDCP. The transmitter input is MIPI DSI.
+
+properties:
+ compatible:
+ enum:
+ - adi,adv7533
+ - adi,adv7535
+
+ reg:
+ description: |
+ I2C slave addresses.
+
+ The ADV7533/35 internal registers are split into four pages
+ exposed through different I2C addresses, creating four register
+ maps. Each map has it own I2C address and acts as a standard slave
+ device on the I2C bus. The main address is mandatory, others are
+ optional and revert to defaults if not specified.
+ minItems: 1
+ maxItems: 4
+
+ reg-names:
+ description:
+ Names of maps with programmable addresses. It can contain any map
+ needing a non-default address.
+ minItems: 1
+ items:
+ - const: main
+ - const: edid
+ - const: cec
+ - const: packet
+
+ clocks:
+ description: Reference to the CEC clock.
+ maxItems: 1
+
+ clock-names:
+ const: cec
+
+ interrupts:
+ maxItems: 1
+
+ pd-gpios:
+ description: GPIO connected to the power down signal.
+ maxItems: 1
+
+ avdd-supply:
+ description: A 1.8V supply that powers up the AVDD pin.
+
+ dvdd-supply:
+ description: A 1.8V supply that powers up the DVDD pin.
+
+ pvdd-supply:
+ description: A 1.8V supply that powers up the PVDD pin.
+
+ a2vdd-supply:
+ description: A 1.8V supply that powers up the A2VDD pin.
+
+ v3p3-supply:
+ description: A 3.3V supply that powers up the V3P3 pin.
+
+ v1p2-supply:
+ description:
+ A supply that powers up the V1P2 pin. It can be either 1.2V
+ or 1.8V for ADV7533 but only 1.8V for ADV7535.
+
+ adi,disable-timing-generator:
+ description:
+ Disables the internal timing generator. The chip will rely on the
+ sync signals in the DSI data lanes, rather than generating its own
+ timings for HDMI output.
+ type: boolean
+
+ adi,dsi-lanes:
+ description: Number of DSI data lanes connected to the DSI host.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [ 1, 2, 3, 4 ]
+
+ ports:
+ description:
+ The ADV7533/35 has two video ports and one audio port. This node
+ models their connections as documented in
+ Documentation/devicetree/bindings/media/video-interfaces.txt
+ Documentation/devicetree/bindings/graph.txt
+ type: object
+ properties:
+ port@0:
+ description:
+ Video port for the DSI input. The remote endpoint phandle
+ should be a reference to a valid mipi_dsi_host_device.
+ type: object
+
+ port@1:
+ description: Video port for the HDMI output.
+ type: object
+
+ port@2:
+ description: Audio port for the HDMI output.
+ type: object
+
+required:
+ - compatible
+ - reg
+ - ports
+ - adi,dsi-lanes
+ - avdd-supply
+ - dvdd-supply
+ - pvdd-supply
+ - a2vdd-supply
+ - v3p3-supply
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ adv7533: hdmi@39 {
+ compatible = "adi,adv7533";
+ /*
+ * The EDID page will be accessible on address 0x66 on the I2C
+ * bus. All other maps continue to use their default addresses.
+ */
+ reg = <0x39>, <0x66>;
+ reg-names = "main", "edid";
+ interrupt-parent = <&gpio3>;
+ interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
+ clocks = <&cec_clock>;
+ clock-names = "cec";
+ adi,dsi-lanes = <4>;
+ avdd-supply = <&v1v8>;
+ dvdd-supply = <&v1v8>;
+ pvdd-supply = <&v1v8>;
+ a2vdd-supply = <&v1v8>;
+ v3p3-supply = <&v3v3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ adv7533_in: endpoint {
+ remote-endpoint = <&dsi_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ adv7533_out: endpoint {
+ remote-endpoint = <&hdmi_connector_in>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ codec_endpoint: endpoint {
+ remote-endpoint = <&i2s0_cpu_endpoint>;
+ };
+ };
+ };
+ };
+
+...
--
2.18.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
* [PATCH v3 1/4] ARM: dts: zynq: add port definitions to hdmi-tx@39
From: Ricardo Cañuelo @ 2020-06-01 6:33 UTC (permalink / raw)
To: laurent.pinchart
Cc: marex, devicetree, michal.simek, xuwei5, robh+dt, mcoquelin.stm32,
kernel, linux-arm-kernel
In-Reply-To: <20200601063308.13045-1-ricardo.canuelo@collabora.com>
Define a 'ports' node for 'adv7511: hdmi-tx@39' to make it compliant with
the adi,adv7511 DT binding.
This fills the minimum requirements to meet the binding requirements,
remote endpoints are not defined.
Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Acked-by: Michal Simek <michal.simek@xilinx.com>
---
arch/arm/boot/dts/zynq-zc702.dts | 10 ++++++++++
arch/arm/boot/dts/zynq-zc706.dts | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/zynq-zc702.dts b/arch/arm/boot/dts/zynq-zc702.dts
index 27cd6cb52f1b..79fd236edded 100644
--- a/arch/arm/boot/dts/zynq-zc702.dts
+++ b/arch/arm/boot/dts/zynq-zc702.dts
@@ -135,6 +135,16 @@
adi,input-clock = "1x";
adi,input-style = <3>;
adi,input-justification = "right";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ };
+ port@1 {
+ reg = <1>;
+ };
+ };
};
};
diff --git a/arch/arm/boot/dts/zynq-zc706.dts b/arch/arm/boot/dts/zynq-zc706.dts
index 77943c16d33f..99fa51ba6e93 100644
--- a/arch/arm/boot/dts/zynq-zc706.dts
+++ b/arch/arm/boot/dts/zynq-zc706.dts
@@ -93,6 +93,16 @@
adi,input-clock = "1x";
adi,input-style = <3>;
adi,input-justification = "evenly";
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ };
+ port@1 {
+ reg = <1>;
+ };
+ };
};
};
--
2.18.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
* [PATCH v3 2/4] arm64: dts: hisilicon: hikey: fixes to comply with adi, adv7533 DT binding
From: Ricardo Cañuelo @ 2020-06-01 6:33 UTC (permalink / raw)
To: laurent.pinchart
Cc: marex, devicetree, michal.simek, xuwei5, robh+dt, mcoquelin.stm32,
kernel, linux-arm-kernel
In-Reply-To: <20200601063308.13045-1-ricardo.canuelo@collabora.com>
hi3660-hikey960.dts:
Define a 'ports' node for 'adv7533: adv7533@39' and the
'adi,dsi-lanes' property to make it compliant with the adi,adv7533 DT
binding.
This fills the requirements to meet the binding requirements,
remote endpoints are not defined.
hi6220-hikey.dts:
Change property name s/pd-gpio/pd-gpios, gpio properties should be
plural. This is just a cosmetic change.
Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts | 11 +++++++++++
arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts | 2 +-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
index e035cf195b19..8c4bfbaf3a80 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
@@ -530,6 +530,17 @@
status = "ok";
compatible = "adi,adv7533";
reg = <0x39>;
+ adi,dsi-lanes = <4>;
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ port@0 {
+ reg = <0>;
+ };
+ port@1 {
+ reg = <1>;
+ };
+ };
};
};
diff --git a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
index c14205cd6bf5..3e47150c05ec 100644
--- a/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi6220-hikey.dts
@@ -516,7 +516,7 @@
reg = <0x39>;
interrupt-parent = <&gpio1>;
interrupts = <1 2>;
- pd-gpio = <&gpio0 4 0>;
+ pd-gpios = <&gpio0 4 0>;
adi,dsi-lanes = <4>;
#sound-dai-cells = <0>;
--
2.18.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
* [PATCH v3 0/4] Convert adi,adv7511.txt DT bindings to yaml
From: Ricardo Cañuelo @ 2020-06-01 6:33 UTC (permalink / raw)
To: laurent.pinchart
Cc: marex, devicetree, michal.simek, xuwei5, robh+dt, mcoquelin.stm32,
kernel, linux-arm-kernel
Hi,
This series convert the adi,adv7511.txt DT bindings to json-schema. As a
result of the conversion some dts files needed to be updated.
The changes to the dts files are of three types:
- Reordering of the I2C slave addresses list of the ADV75xx node. The
addresses in the 'reg' property and the matching names in
'reg-names' for an I2C slave don't need to be in any particular
order, but the DT schema defines these properties as a cell array
and a string array respectively, which are ordered, so the
definitions in the dts files must match the order in the binding.
- Filling the minimum binding requirements. Most of the time this
means creating a 'ports' node in the boards that don't define
them. Note, however, that the purpose of this is simply to make the
definition compliant with the binding. I didn't define any endpoints
for the ports.
- Removing unneeded properties.
About the binding conversion:
- The original binding covered five different devices: ADV7511,
ADV7511W, ADV7513, ADV7533 and ADV7535. They all share a common set
of properties but ADV7533 and ADV7535 have enough differences from
the rest to warrant their own binding file. In v1 I modelled all the
properties constraints for all five devices in a single file but it
turned out a bit too complex. Splitting the binding into one for
ADV7511/11W/13 and another for ADV7533/35 makes them much easier to
read and maintain.
Patches 1/4 to 3/4 contain the dts changes. Patch 4/4 contains the
binding conversion.
NOTE: the bindings have been tested with:
make dt_binding_check ARCH=<arch> DT_SCHEMA_FILES=<...adi,adv7511.yaml>
make dt_binding_check ARCH=<arch> DT_SCHEMA_FILES=<...adi,adv7533.yaml>
make dtbs_check ARCH=<arch> DT_SCHEMA_FILES=<...adi,adv7511.yaml>
make dtbs_check ARCH=<arch> DT_SCHEMA_FILES=<...adi,adv7533.yaml>
for <arch> = arm and arm64. dts changes haven't been tested in hardware.
Some existing DTs are expected to fail after this conversion.
Changes in v3:
- Removed from the patch series (already in mainline):
- arm64: dts: renesas: make hdmi encoder nodes compliant with DT bindings
- ARM: dts: renesas: make hdmi encoder nodes compliant with DT bindings
- ARM: dts: iwg20d-q7-dbcm-ca: remove unneeded properties in hdmi@39
- Additional DTs fixes:
- boot/dts/stm32mp15xx-dhcor-avenger96.dtsi.
- [Laurent] adi,adv7511.yaml and adi,adv7533.yaml.
- Documentation fixes and typos.
- Removed unnecessary allOf's.
- adi,embedded-sync data type changed to boolean.
- Power supplies defined as required.
- Examples updated.
Ricardo Cañuelo (4):
ARM: dts: zynq: add port definitions to hdmi-tx@39
arm64: dts: hisilicon: hikey: fixes to comply with adi,adv7533 DT
binding
ARM: dts: stm32: make hdmi-transmitter node compliant with DT bindings
dt-bindings: drm: bridge: adi,adv7511.txt: convert to yaml
.../bindings/display/bridge/adi,adv7511.txt | 143 -----------
.../bindings/display/bridge/adi,adv7511.yaml | 231 ++++++++++++++++++
.../bindings/display/bridge/adi,adv7533.yaml | 175 +++++++++++++
.../boot/dts/stm32mp15xx-dhcor-avenger96.dtsi | 6 +-
arch/arm/boot/dts/zynq-zc702.dts | 10 +
arch/arm/boot/dts/zynq-zc706.dts | 10 +
.../boot/dts/hisilicon/hi3660-hikey960.dts | 11 +
.../arm64/boot/dts/hisilicon/hi6220-hikey.dts | 2 +-
8 files changed, 440 insertions(+), 148 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7511.txt
create mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7511.yaml
create mode 100644 Documentation/devicetree/bindings/display/bridge/adi,adv7533.yaml
--
2.18.0
_______________________________________________
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 3/4] ARM: dts: stm32: make hdmi-transmitter node compliant with DT bindings
From: Ricardo Cañuelo @ 2020-06-01 6:33 UTC (permalink / raw)
To: laurent.pinchart
Cc: marex, devicetree, michal.simek, xuwei5, robh+dt, mcoquelin.stm32,
kernel, linux-arm-kernel
In-Reply-To: <20200601063308.13045-1-ricardo.canuelo@collabora.com>
Reorder the I2C slave addresses of the hdmi-transmitter node and remove
the adi,input-style and adi,input-justification properties to meet the
adi,adv7513 binding requirements.
Signed-off-by: Ricardo Cañuelo <ricardo.canuelo@collabora.com>
---
arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi
index 930202742a3f..b67a21a8698a 100644
--- a/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi
+++ b/arch/arm/boot/dts/stm32mp15xx-dhcor-avenger96.dtsi
@@ -185,8 +185,8 @@
&i2c4 {
hdmi-transmitter@3d {
compatible = "adi,adv7513";
- reg = <0x3d>, <0x2d>, <0x4d>, <0x5d>;
- reg-names = "main", "cec", "edid", "packet";
+ reg = <0x3d>, <0x4d>, <0x2d> , <0x5d>;
+ reg-names = "main", "edid", "cec", "packet";
clocks = <&cec_clock>;
clock-names = "cec";
@@ -204,8 +204,6 @@
adi,input-depth = <8>;
adi,input-colorspace = "rgb";
adi,input-clock = "1x";
- adi,input-style = <1>;
- adi,input-justification = "evenly";
ports {
#address-cells = <1>;
--
2.18.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 RFC] KVM: arm64: Sidestep stage2_unmap_vm() on vcpu reset when S2FWB is supported
From: zhukeqian @ 2020-06-01 6:26 UTC (permalink / raw)
To: Marc Zyngier, Alexandru Elisei
Cc: Zenghui Yu, kvmarm, linux-arm-kernel, linux-kernel
In-Reply-To: <13db879dff56d091f98f7c5416ab1535@kernel.org>
Hi Marc,
On 2020/5/31 0:31, Marc Zyngier wrote:
> Hi Alex,
>
> On 2020-05-30 11:46, Alexandru Elisei wrote:
>> Hi,
>
> [...]
>
>>>> diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c
>>>> index 48d0ec44ad77..e6378162cdef 100644
>>>> --- a/virt/kvm/arm/arm.c
>>>> +++ b/virt/kvm/arm/arm.c
>>>> @@ -983,8 +983,11 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
>>>> /*
>>>> * Ensure a rebooted VM will fault in RAM pages and detect if the
>>>> * guest MMU is turned off and flush the caches as needed.
>>>> + *
>>>> + * S2FWB enforces all memory accesses to RAM being cacheable, we
>>>> + * ensure that the cache is always coherent.
>>>> */
>>>> - if (vcpu->arch.has_run_once)
>>>> + if (vcpu->arch.has_run_once && !cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
>>> I think userspace does not invalidate the icache when loading a new kernel image,
>>> and if the guest patched instructions, they could potentially still be in the
>>> icache. Should the icache be invalidated if FWB is present?
>>
>> I noticed that this was included in the current pull request and I
>> remembered that
>> I wasn't sure about this part. Did some more digging and it turns out that FWB
>> implies no cache maintenance needed for *data to instruction*
>> coherence. From ARM
>> DDI 0487F.b, page D5-2635:
>>
>> "When ARMv8.4-S2FWB is implemented, the architecture requires that
>> CLIDR_EL1.{LOUU, LOIUS} are zero so that no levels of data cache need to be
>> cleaned in order to manage coherency with instruction fetches".
>>
>> However, there's no mention that I found for instruction to data coherence,
>> meaning that the icache would still need to be invalidated on each vcpu in order
>> to prevent fetching of patched instructions from the icache. Am I
>> missing something?
>
> I think you are right, and this definitely matches the way we deal with
> the icache on the fault path. For some bizarre reason, I always assume
> that FWB implies DIC, which isn't true at all.
>
> I'm planning to address it as follows. Please let me know what you think.
>
> Thanks,
>
> M.
>
> From f7860d1d284f41afea176cc17e5c9d895ae665e9 Mon Sep 17 00:00:00 2001
> From: Marc Zyngier <maz@kernel.org>
> Date: Sat, 30 May 2020 17:22:19 +0100
> Subject: [PATCH] KVM: arm64: Flush the instruction cache if not unmapping the
> VM on reboot
>
> On a system with FWB, we don't need to unmap Stage-2 on reboot,
> as even if userspace takes this opportunity to repaint the whole
> of memory, FWB ensures that the data side stays consistent even
> if the guest uses non-cacheable mappings.
>
> However, the I-side is not necessarily coherent with the D-side
> if CTR_EL0.DIC is 0. In this case, invalidate the i-cache to
> preserve coherency.
>
> Reported-by: Alexandru Elisei <alexandru.elisei@arm.com>
> Fixes: 892713e97ca1 ("KVM: arm64: Sidestep stage2_unmap_vm() on vcpu reset when S2FWB is supported")
> Signed-off-by: Marc Zyngier <maz@kernel.org>
> ---
> arch/arm64/kvm/arm.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
> index b0b569f2cdd0..d6988401c22a 100644
> --- a/arch/arm64/kvm/arm.c
> +++ b/arch/arm64/kvm/arm.c
> @@ -989,11 +989,17 @@ static int kvm_arch_vcpu_ioctl_vcpu_init(struct kvm_vcpu *vcpu,
> * Ensure a rebooted VM will fault in RAM pages and detect if the
> * guest MMU is turned off and flush the caches as needed.
> *
> - * S2FWB enforces all memory accesses to RAM being cacheable, we
> - * ensure that the cache is always coherent.
> + * S2FWB enforces all memory accesses to RAM being cacheable,
> + * ensuring that the data side is always coherent. We still
> + * need to invalidate the I-cache though, as FWB does *not*
> + * imply CTR_EL0.DIC.
> */
> - if (vcpu->arch.has_run_once && !cpus_have_const_cap(ARM64_HAS_STAGE2_FWB))
> - stage2_unmap_vm(vcpu->kvm);
> + if (vcpu->arch.has_run_once) {
> + if (!cpus_have_final_cap(ARM64_HAS_STAGE2_FWB))
> + stage2_unmap_vm(vcpu->kvm);
> + else
> + __flush_icache_all();
After I looking into this function, I think it's OK here. Please ignore
my question :-).
> + }
>
> vcpu_reset_hcr(vcpu);
>
Thanks,
Keqian
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* RE: [EXT] [PATCH] [v3] i2c: imx-lpi2c: Fix runtime PM imbalance on error
From: Andy Duan @ 2020-06-01 6:24 UTC (permalink / raw)
To: Dinghao Liu, kjlu@umn.edu
Cc: Aisheng Dong, Fabio Estevam, Sascha Hauer,
linux-kernel@vger.kernel.org, Wolfram Sang, Markus Elfring,
dl-linux-imx, Pengutronix Kernel Team, Shawn Guo,
linux-arm-kernel@lists.infradead.org, linux-i2c@vger.kernel.org
In-Reply-To: <20200601061640.27632-1-dinghao.liu@zju.edu.cn>
From: Dinghao Liu <dinghao.liu@zju.edu.cn> Sent: Monday, June 1, 2020 2:17 PM
> pm_runtime_get_sync() increments the runtime PM usage counter even the
> call returns an error code. Thus a corresponding decrement is needed on the
> error handling path to keep the counter balanced.
>
> Fix this by adding the missed function call.
>
> Fixes: 13d6eb20fc79a ("i2c: imx-lpi2c: add runtime pm support")
> Co-developed-by: Markus Elfring <Markus.Elfring@web.de>
> Signed-off-by: Markus Elfring <Markus.Elfring@web.de>
> Signed-off-by: Dinghao Liu <dinghao.liu@zju.edu.cn>
Reviewed-by: Fugang Duan <fugang.duan@nxp.com>
> ---
>
> Changelog:
>
> v2: - Use pm_runtime_put_noidle() instead of
> pm_runtime_put_autosuspend().
>
> v3: - Refine commit message.
> ---
> drivers/i2c/busses/i2c-imx-lpi2c.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c
> b/drivers/i2c/busses/i2c-imx-lpi2c.c
> index 94743ba581fe..bdee02dff284 100644
> --- a/drivers/i2c/busses/i2c-imx-lpi2c.c
> +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c
> @@ -260,8 +260,10 @@ static int lpi2c_imx_master_enable(struct
> lpi2c_imx_struct *lpi2c_imx)
> int ret;
>
> ret = pm_runtime_get_sync(lpi2c_imx->adapter.dev.parent);
> - if (ret < 0)
> + if (ret < 0) {
> + pm_runtime_put_noidle(lpi2c_imx->adapter.dev.parent);
> return ret;
> + }
>
> temp = MCR_RST;
> writel(temp, lpi2c_imx->base + LPI2C_MCR);
> --
> 2.17.1
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH 5/7] drm: bridge: cadence: Initial support for MHDP HDMI bridge driver
From: sandor.yu @ 2020-06-01 6:17 UTC (permalink / raw)
To: a.hajda, narmstrong, Laurent.pinchart, jonas, jernej.skrabec,
heiko, hjc, Sandor.yu, dkos, dri-devel
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, linux-imx
In-Reply-To: <cover.1590982881.git.Sandor.yu@nxp.com>
From: Sandor Yu <Sandor.yu@nxp.com>
This adds initial support for cadence MHDP HDMI bridge driver.
Basic HDMI functions are supported, that include:
-Video mode set on-the-fly
-Cable hotplug detect
-MAX support resolution to 3096x2160@60fps
-HDMI audio
-AV infoframe
-EDID read
-SCDC read
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
drivers/gpu/drm/bridge/cadence/Kconfig | 4 +
drivers/gpu/drm/bridge/cadence/Makefile | 3 +-
.../gpu/drm/bridge/cadence/cdns-hdmi-core.c | 600 ++++++++++++++++++
.../gpu/drm/bridge/cadence/cdns-mhdp-common.h | 14 +
.../gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c | 330 ++++++++++
include/drm/bridge/cdns-mhdp.h | 69 ++
6 files changed, 1019 insertions(+), 1 deletion(-)
create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
create mode 100644 drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig
index b7b8d30b18b6..9bc098302837 100644
--- a/drivers/gpu/drm/bridge/cadence/Kconfig
+++ b/drivers/gpu/drm/bridge/cadence/Kconfig
@@ -9,3 +9,7 @@ config DRM_CDNS_MHDP
config DRM_CDNS_DP
tristate "Cadence DP DRM driver"
depends on DRM_CDNS_MHDP
+
+config DRM_CDNS_HDMI
+ tristate "Cadence HDMI DRM driver"
+ depends on DRM_CDNS_MHDP
diff --git a/drivers/gpu/drm/bridge/cadence/Makefile b/drivers/gpu/drm/bridge/cadence/Makefile
index cb3c88311a64..1d60166c2bf5 100644
--- a/drivers/gpu/drm/bridge/cadence/Makefile
+++ b/drivers/gpu/drm/bridge/cadence/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o
+cdns_mhdp_drmcore-y := cdns-mhdp-common.o cdns-mhdp-audio.o cdns-mhdp-dp.o cdns-mhdp-hdmi.o
cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_DP) += cdns-dp-core.o
+cdns_mhdp_drmcore-$(CONFIG_DRM_CDNS_HDMI) += cdns-hdmi-core.o
obj-$(CONFIG_DRM_CDNS_MHDP) += cdns_mhdp_drmcore.o
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
new file mode 100644
index 000000000000..5775ed21b734
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-hdmi-core.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cadence High-Definition Multimedia Interface (HDMI) driver
+ *
+ * Copyright (C) 2019-2020 NXP Semiconductor, Inc.
+ *
+ */
+#include <drm/bridge/cdns-mhdp.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_print.h>
+#include <drm/drm_scdc_helper.h>
+#include <drm/drm_vblank.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+
+#include "cdns-mhdp-common.h"
+
+static void hdmi_sink_config(struct cdns_mhdp_device *mhdp)
+{
+ struct drm_scdc *scdc = &mhdp->connector.base.display_info.hdmi.scdc;
+ u8 buff = 0;
+
+ /* Default work in HDMI1.4 */
+ mhdp->hdmi.hdmi_type = MODE_HDMI_1_4;
+
+ /* check sink support SCDC or not */
+ if (scdc->supported != true) {
+ DRM_INFO("Sink Not Support SCDC\n");
+ return;
+ }
+
+ if (mhdp->hdmi.char_rate > 340000) {
+ /*
+ * TMDS Character Rate above 340MHz should working in HDMI2.0
+ * Enable scrambling and TMDS_Bit_Clock_Ratio
+ */
+ buff = SCDC_TMDS_BIT_CLOCK_RATIO_BY_40 | SCDC_SCRAMBLING_ENABLE;
+ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0;
+ } else if (scdc->scrambling.low_rates) {
+ /*
+ * Enable scrambling and HDMI2.0 when scrambling capability of sink
+ * be indicated in the HF-VSDB LTE_340Mcsc_scramble bit
+ */
+ buff = SCDC_SCRAMBLING_ENABLE;
+ mhdp->hdmi.hdmi_type = MODE_HDMI_2_0;
+ }
+
+ /* TMDS config */
+ cdns_hdmi_scdc_write(mhdp, SCDC_TMDS_CONFIG, buff);
+}
+
+static void hdmi_lanes_config(struct cdns_mhdp_device *mhdp)
+{
+ u32 lane_mapping = mhdp->plat_data->lane_mapping;
+ /* Line swaping */
+ cdns_mhdp_reg_write(mhdp, LANES_CONFIG, 0x00400000 | lane_mapping);
+}
+
+static int hdmi_avi_info_set(struct cdns_mhdp_device *mhdp,
+ struct drm_display_mode *mode)
+{
+ struct hdmi_avi_infoframe frame;
+ int format = mhdp->video_info.color_fmt;
+ struct drm_connector_state *conn_state = mhdp->connector.base.state;
+ struct drm_display_mode *adj_mode;
+ enum hdmi_quantization_range qr;
+ u8 buf[32];
+ int ret;
+
+ /* Initialise info frame from DRM mode */
+ drm_hdmi_avi_infoframe_from_display_mode(&frame,
+ &mhdp->connector.base, mode);
+
+ switch (format) {
+ case YCBCR_4_4_4:
+ frame.colorspace = HDMI_COLORSPACE_YUV444;
+ break;
+ case YCBCR_4_2_2:
+ frame.colorspace = HDMI_COLORSPACE_YUV422;
+ break;
+ case YCBCR_4_2_0:
+ frame.colorspace = HDMI_COLORSPACE_YUV420;
+ break;
+ default:
+ frame.colorspace = HDMI_COLORSPACE_RGB;
+ break;
+ }
+
+ drm_hdmi_avi_infoframe_colorspace(&frame, conn_state);
+
+ adj_mode = &mhdp->bridge.base.encoder->crtc->state->adjusted_mode;
+
+ qr = drm_default_rgb_quant_range(adj_mode);
+
+ drm_hdmi_avi_infoframe_quant_range(&frame, &mhdp->connector.base,
+ adj_mode, qr);
+
+ ret = hdmi_avi_infoframe_check(&frame);
+ if (WARN_ON(ret))
+ return false;
+
+ ret = hdmi_avi_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
+ if (ret < 0) {
+ DRM_ERROR("failed to pack AVI infoframe: %d\n", ret);
+ return -1;
+ }
+
+ buf[0] = 0;
+ cdns_mhdp_infoframe_set(mhdp, 0, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_AVI);
+ return 0;
+}
+
+static void hdmi_vendor_info_set(struct cdns_mhdp_device *mhdp,
+ struct drm_display_mode *mode)
+{
+ struct hdmi_vendor_infoframe frame;
+ u8 buf[32];
+ int ret;
+
+ /* Initialise vendor frame from DRM mode */
+ ret = drm_hdmi_vendor_infoframe_from_display_mode(&frame, &mhdp->connector.base, mode);
+ if (ret < 0) {
+ DRM_INFO("No vendor infoframe\n");
+ return;
+ }
+
+ ret = hdmi_vendor_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
+ if (ret < 0) {
+ DRM_WARN("Unable to pack vendor infoframe: %d\n", ret);
+ return;
+ }
+
+ buf[0] = 0;
+ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_VENDOR);
+}
+
+static void hdmi_drm_info_set(struct cdns_mhdp_device *mhdp)
+{
+ struct drm_connector_state *conn_state;
+ struct hdmi_drm_infoframe frame;
+ u8 buf[32];
+ int ret;
+
+ conn_state = mhdp->connector.base.state;
+
+ if (!conn_state->hdr_output_metadata)
+ return;
+
+ ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, conn_state);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("couldn't set HDR metadata in infoframe\n");
+ return;
+ }
+
+ ret = hdmi_drm_infoframe_pack(&frame, buf + 1, sizeof(buf) - 1);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("couldn't pack HDR infoframe\n");
+ return;
+ }
+
+ buf[0] = 0;
+ cdns_mhdp_infoframe_set(mhdp, 3, sizeof(buf), buf, HDMI_INFOFRAME_TYPE_DRM);
+}
+
+void cdns_hdmi_mode_set(struct cdns_mhdp_device *mhdp)
+{
+ struct drm_display_mode *mode = &mhdp->mode;
+ int ret;
+
+ /* video mode check */
+ if (mode->clock == 0 || mode->hdisplay == 0 || mode->vdisplay == 0)
+ return;
+
+ hdmi_lanes_config(mhdp);
+
+ cdns_mhdp_plat_call(mhdp, pclk_rate);
+
+ /* Delay for HDMI FW stable after pixel clock relock */
+ msleep(20);
+
+ cdns_mhdp_plat_call(mhdp, phy_set);
+
+ hdmi_sink_config(mhdp);
+
+ ret = cdns_hdmi_ctrl_init(mhdp, mhdp->hdmi.hdmi_type, mhdp->hdmi.char_rate);
+ if (ret < 0) {
+ DRM_ERROR("%s, ret = %d\n", __func__, ret);
+ return;
+ }
+
+ /* Config GCP */
+ if (mhdp->video_info.color_depth == 8)
+ cdns_hdmi_disable_gcp(mhdp);
+ else
+ cdns_hdmi_enable_gcp(mhdp);
+
+ ret = hdmi_avi_info_set(mhdp, mode);
+ if (ret < 0) {
+ DRM_ERROR("%s ret = %d\n", __func__, ret);
+ return;
+ }
+
+ /* vendor info frame is enabled only for HDMI1.4 4K mode */
+ hdmi_vendor_info_set(mhdp, mode);
+
+ hdmi_drm_info_set(mhdp);
+
+ ret = cdns_hdmi_mode_config(mhdp, mode, &mhdp->video_info);
+ if (ret < 0) {
+ DRM_ERROR("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret);
+ return;
+ }
+}
+
+static enum drm_connector_status
+cdns_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct cdns_mhdp_device *mhdp =
+ container_of(connector, struct cdns_mhdp_device, connector.base);
+ u8 hpd = 0xf;
+
+ hpd = cdns_mhdp_read_hpd(mhdp);
+ if (hpd == 1)
+ return connector_status_connected;
+ else if (hpd == 0)
+ return connector_status_disconnected;
+ else {
+ DRM_INFO("Unknow cable status, hdp=%u\n", hpd);
+ return connector_status_unknown;
+ }
+}
+
+static int cdns_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+ struct cdns_mhdp_device *mhdp =
+ container_of(connector, struct cdns_mhdp_device, connector.base);
+ int num_modes = 0;
+ struct edid *edid;
+
+ edid = drm_do_get_edid(&mhdp->connector.base,
+ cdns_hdmi_get_edid_block, mhdp);
+ if (edid) {
+ dev_info(mhdp->dev, "%x,%x,%x,%x,%x,%x,%x,%x\n",
+ edid->header[0], edid->header[1],
+ edid->header[2], edid->header[3],
+ edid->header[4], edid->header[5],
+ edid->header[6], edid->header[7]);
+ drm_connector_update_edid_property(connector, edid);
+ num_modes = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ if (num_modes == 0)
+ DRM_ERROR("Invalid edid\n");
+ return num_modes;
+}
+
+static bool blob_equal(const struct drm_property_blob *a,
+ const struct drm_property_blob *b)
+{
+ if (a && b)
+ return a->length == b->length &&
+ !memcmp(a->data, b->data, a->length);
+
+ return !a == !b;
+}
+
+static int cdns_hdmi_connector_atomic_check(struct drm_connector *connector,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector_state *new_con_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ struct drm_connector_state *old_con_state =
+ drm_atomic_get_old_connector_state(state, connector);
+ struct drm_crtc *crtc = new_con_state->crtc;
+ struct drm_crtc_state *new_crtc_state;
+
+ if (!blob_equal(new_con_state->hdr_output_metadata,
+ old_con_state->hdr_output_metadata) ||
+ new_con_state->colorspace != old_con_state->colorspace) {
+ new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(new_crtc_state))
+ return PTR_ERR(new_crtc_state);
+
+ new_crtc_state->mode_changed =
+ !new_con_state->hdr_output_metadata ||
+ !old_con_state->hdr_output_metadata ||
+ new_con_state->colorspace != old_con_state->colorspace;
+ }
+
+ return 0;
+}
+
+static const struct drm_connector_funcs cdns_hdmi_connector_funcs = {
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = cdns_hdmi_connector_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 const struct drm_connector_helper_funcs cdns_hdmi_connector_helper_funcs = {
+ .get_modes = cdns_hdmi_connector_get_modes,
+ .atomic_check = cdns_hdmi_connector_atomic_check,
+};
+
+static int cdns_hdmi_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct cdns_mhdp_device *mhdp = bridge->driver_private;
+ struct drm_mode_config *config = &bridge->dev->mode_config;
+ struct drm_encoder *encoder = bridge->encoder;
+ struct drm_connector *connector = &mhdp->connector.base;
+
+ connector->interlace_allowed = 1;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ drm_connector_helper_add(connector, &cdns_hdmi_connector_helper_funcs);
+
+ drm_connector_init(bridge->dev, connector, &cdns_hdmi_connector_funcs,
+ DRM_MODE_CONNECTOR_HDMIA);
+
+ drm_object_attach_property(&connector->base,
+ config->hdr_output_metadata_property, 0);
+
+ if (!drm_mode_create_hdmi_colorspace_property(connector))
+ drm_object_attach_property(&connector->base,
+ connector->colorspace_property, 0);
+
+ drm_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static enum drm_mode_status
+cdns_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+ struct cdns_mhdp_device *mhdp = bridge->driver_private;
+ enum drm_mode_status mode_status = MODE_OK;
+ int ret;
+
+ /* We don't support double-clocked and Interlaced modes */
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK ||
+ mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_BAD;
+
+ /* MAX support pixel clock rate 594MHz */
+ if (mode->clock > 594000)
+ return MODE_CLOCK_HIGH;
+
+ /* 4096x2160 is not supported */
+ if (mode->hdisplay > 3840 || mode->vdisplay > 2160)
+ return MODE_BAD_HVALUE;
+
+ /* Check modes supported by PHY */
+ mhdp->hdmi.mode_valid = mode;
+ ret = cdns_mhdp_plat_call(mhdp, phy_video_valid);
+ if (ret == false)
+ return MODE_CLOCK_RANGE;
+
+ return mode_status;
+}
+
+static void cdns_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *orig_mode,
+ const struct drm_display_mode *mode)
+{
+ struct cdns_mhdp_device *mhdp = bridge->driver_private;
+ struct video_info *video = &mhdp->video_info;
+
+ video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+ video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+
+ DRM_INFO("Mode: %dx%dp%d\n", mode->hdisplay, mode->vdisplay, mode->clock);
+ memcpy(&mhdp->mode, mode, sizeof(struct drm_display_mode));
+
+ mutex_lock(&mhdp->lock);
+ cdns_hdmi_mode_set(mhdp);
+ mutex_unlock(&mhdp->lock);
+}
+
+bool cdns_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct cdns_mhdp_device *mhdp = bridge->driver_private;
+ struct video_info *video = &mhdp->video_info;
+
+ video->color_depth = 8;
+ video->color_fmt = PXL_RGB;
+
+ return true;
+}
+
+static const struct drm_bridge_funcs cdns_hdmi_bridge_funcs = {
+ .attach = cdns_hdmi_bridge_attach,
+ .mode_set = cdns_hdmi_bridge_mode_set,
+ .mode_valid = cdns_hdmi_bridge_mode_valid,
+ .mode_fixup = cdns_hdmi_bridge_mode_fixup,
+};
+
+static void hotplug_work_func(struct work_struct *work)
+{
+ struct cdns_mhdp_device *mhdp = container_of(work,
+ struct cdns_mhdp_device, hotplug_work.work);
+ struct drm_connector *connector = &mhdp->connector.base;
+
+ drm_helper_hpd_irq_event(connector->dev);
+
+ if (connector->status == connector_status_connected) {
+ DRM_INFO("HDMI Cable Plug In\n");
+ enable_irq(mhdp->irq[IRQ_OUT]);
+ } else if (connector->status == connector_status_disconnected) {
+ /* Cable Disconnedted */
+ DRM_INFO("HDMI Cable Plug Out\n");
+ enable_irq(mhdp->irq[IRQ_IN]);
+ }
+}
+
+static irqreturn_t cdns_hdmi_irq_thread(int irq, void *data)
+{
+ struct cdns_mhdp_device *mhdp = data;
+
+ disable_irq_nosync(irq);
+
+ mod_delayed_work(system_wq, &mhdp->hotplug_work,
+ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+ return IRQ_HANDLED;
+}
+
+static int __cdns_hdmi_probe(struct platform_device *pdev,
+ struct cdns_mhdp_device *mhdp)
+{
+ struct device *dev = &pdev->dev;
+ struct platform_device_info pdevinfo;
+ struct resource *iores = NULL;
+ int ret;
+
+ mutex_init(&mhdp->lock);
+
+ INIT_DELAYED_WORK(&mhdp->hotplug_work, hotplug_work_func);
+
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mhdp->regs_base = devm_ioremap(dev, iores->start, resource_size(iores));
+ if (IS_ERR(mhdp->regs_base)) {
+ dev_err(dev, "No regs_base memory\n");
+ return -ENOMEM;
+ }
+
+ mhdp->irq[IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
+ if (mhdp->irq[IRQ_IN] < 0) {
+ dev_info(dev, "No plug_in irq number\n");
+ return -EPROBE_DEFER;
+ }
+
+ mhdp->irq[IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
+ if (mhdp->irq[IRQ_OUT] < 0) {
+ dev_info(dev, "No plug_out irq number\n");
+ return -EPROBE_DEFER;
+ }
+
+ cdns_mhdp_plat_call(mhdp, power_on);
+
+ /* Initialize FW */
+ cdns_mhdp_plat_call(mhdp, firmware_init);
+
+ /* HDMI FW alive check */
+ ret = cdns_mhdp_check_alive(mhdp);
+ if (ret == false) {
+ dev_err(dev, "NO HDMI FW running\n");
+ return -ENXIO;
+ }
+
+ /* Enable Hotplug Detect thread */
+ irq_set_status_flags(mhdp->irq[IRQ_IN], IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_IN],
+ NULL, cdns_hdmi_irq_thread,
+ IRQF_ONESHOT, dev_name(dev),
+ mhdp);
+ if (ret < 0) {
+ dev_err(dev, "can't claim irq %d\n",
+ mhdp->irq[IRQ_IN]);
+ return -EINVAL;
+ }
+
+ irq_set_status_flags(mhdp->irq[IRQ_OUT], IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(dev, mhdp->irq[IRQ_OUT],
+ NULL, cdns_hdmi_irq_thread,
+ IRQF_ONESHOT, dev_name(dev),
+ mhdp);
+ if (ret < 0) {
+ dev_err(dev, "can't claim irq %d\n",
+ mhdp->irq[IRQ_OUT]);
+ return -EINVAL;
+ }
+
+ if (cdns_mhdp_read_hpd(mhdp))
+ enable_irq(mhdp->irq[IRQ_OUT]);
+ else
+ enable_irq(mhdp->irq[IRQ_IN]);
+
+ mhdp->bridge.base.driver_private = mhdp;
+ mhdp->bridge.base.funcs = &cdns_hdmi_bridge_funcs;
+#ifdef CONFIG_OF
+ mhdp->bridge.base.of_node = dev->of_node;
+#endif
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+ pdevinfo.parent = dev;
+ pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+ dev_set_drvdata(dev, mhdp);
+
+ /* register audio driver */
+ cdns_mhdp_register_audio_driver(dev);
+
+ return 0;
+}
+
+static void __cdns_hdmi_remove(struct cdns_mhdp_device *mhdp)
+{
+ cdns_mhdp_unregister_audio_driver(mhdp->dev);
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe/remove API, used from platforms based on the DRM bridge API.
+ */
+int cdns_hdmi_probe(struct platform_device *pdev,
+ struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ ret = __cdns_hdmi_probe(pdev, mhdp);
+ if (ret < 0)
+ return ret;
+
+ drm_bridge_add(&mhdp->bridge.base);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cdns_hdmi_probe);
+
+void cdns_hdmi_remove(struct platform_device *pdev)
+{
+ struct cdns_mhdp_device *mhdp = platform_get_drvdata(pdev);
+
+ drm_bridge_remove(&mhdp->bridge.base);
+
+ __cdns_hdmi_remove(mhdp);
+}
+EXPORT_SYMBOL_GPL(cdns_hdmi_remove);
+
+/* -----------------------------------------------------------------------------
+ * Bind/unbind API, used from platforms based on the component framework.
+ */
+int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
+ struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ ret = __cdns_hdmi_probe(pdev, mhdp);
+ if (ret)
+ return ret;
+
+ ret = drm_bridge_attach(encoder, &mhdp->bridge.base, NULL, 0);
+ if (ret) {
+ cdns_hdmi_remove(pdev);
+ DRM_ERROR("Failed to initialize bridge with drm\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cdns_hdmi_bind);
+
+void cdns_hdmi_unbind(struct device *dev)
+{
+ struct cdns_mhdp_device *mhdp = dev_get_drvdata(dev);
+
+ __cdns_hdmi_remove(mhdp);
+}
+EXPORT_SYMBOL_GPL(cdns_hdmi_unbind);
+
+MODULE_AUTHOR("Sandor Yu <sandor.yu@nxp.com>");
+MODULE_DESCRIPTION("Cadence HDMI transmitter driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cdn-hdmi");
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h
index b122bf5f0bdf..fea648070611 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-common.h
@@ -23,4 +23,18 @@ int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,
/* Audio */
int cdns_mhdp_register_audio_driver(struct device *dev);
void cdns_mhdp_unregister_audio_driver(struct device *dev);
+
+/* HDMI */
+int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value);
+void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp,
+ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type);
+int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp,
+ int protocol, u32 char_rate);
+int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp);
+int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp);
+int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp,
+ struct drm_display_mode *mode, struct video_info *video_info);
+int cdns_hdmi_get_edid_block(void *data, u8 *edid,
+ u32 block, size_t length);
+
#endif
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
new file mode 100644
index 000000000000..b8826a9ed524
--- /dev/null
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp-hdmi.c
@@ -0,0 +1,330 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019-2020 NXP Semiconductor, Inc.
+ *
+ */
+
+#include <drm/bridge/cdns-mhdp.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_print.h>
+#include <linux/io.h>
+#include <linux/regmap.h>
+
+#include "cdns-mhdp-common.h"
+
+void cdns_mhdp_infoframe_set(struct cdns_mhdp_device *mhdp,
+ u8 entry_id, u8 packet_len, u8 *packet, u8 packet_type)
+{
+ u32 *packet32, len32;
+ u32 val, i;
+
+ /* invalidate entry */
+ val = F_ACTIVE_IDLE_TYPE(1) | F_PKT_ALLOC_ADDRESS(entry_id);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
+ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
+
+ /* flush fifo 1 */
+ cdns_mhdp_bus_write(F_FIFO1_FLUSH(1), mhdp, SOURCE_PIF_FIFO1_FLUSH);
+
+ /* write packet into memory */
+ packet32 = (u32 *)packet;
+ len32 = packet_len / 4;
+ for (i = 0; i < len32; i++)
+ cdns_mhdp_bus_write(F_DATA_WR(packet32[i]), mhdp, SOURCE_PIF_DATA_WR);
+
+ /* write entry id */
+ cdns_mhdp_bus_write(F_WR_ADDR(entry_id), mhdp, SOURCE_PIF_WR_ADDR);
+
+ /* write request */
+ cdns_mhdp_bus_write(F_HOST_WR(1), mhdp, SOURCE_PIF_WR_REQ);
+
+ /* update entry */
+ val = F_ACTIVE_IDLE_TYPE(1) | F_TYPE_VALID(1) |
+ F_PACKET_TYPE(packet_type) | F_PKT_ALLOC_ADDRESS(entry_id);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_PIF_PKT_ALLOC_REG);
+
+ cdns_mhdp_bus_write(F_PKT_ALLOC_WR_EN(1), mhdp, SOURCE_PIF_PKT_ALLOC_WR_EN);
+}
+
+int cdns_hdmi_get_edid_block(void *data, u8 *edid,
+ u32 block, size_t length)
+{
+ struct cdns_mhdp_device *mhdp = data;
+ u8 msg[2], reg[5], i;
+ int ret;
+
+ for (i = 0; i < 4; i++) {
+ msg[0] = block / 2;
+ msg[1] = block % 2;
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_EDID,
+ sizeof(msg), msg);
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
+ HDMI_TX_EDID, sizeof(reg) + length);
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
+ if (ret)
+ continue;
+
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length);
+ if (ret)
+ continue;
+
+ if ((reg[3] << 8 | reg[4]) == length)
+ break;
+ }
+
+ if (ret)
+ DRM_ERROR("get block[%d] edid failed: %d\n", block, ret);
+ return ret;
+}
+
+int cdns_hdmi_scdc_read(struct cdns_mhdp_device *mhdp, u8 addr, u8 *data)
+{
+ u8 msg[4], reg[6];
+ int ret;
+
+ msg[0] = 0x54;
+ msg[1] = addr;
+ msg[2] = 0;
+ msg[3] = 1;
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_READ,
+ sizeof(msg), msg);
+ if (ret)
+ goto err_scdc_read;
+
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
+ HDMI_TX_READ, sizeof(reg));
+ if (ret)
+ goto err_scdc_read;
+
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
+ if (ret)
+ goto err_scdc_read;
+
+ *data = reg[5];
+
+err_scdc_read:
+ if (ret)
+ DRM_ERROR("scdc read failed: %d\n", ret);
+ return ret;
+}
+
+int cdns_hdmi_scdc_write(struct cdns_mhdp_device *mhdp, u8 addr, u8 value)
+{
+ u8 msg[5], reg[5];
+ int ret;
+
+ msg[0] = 0x54;
+ msg[1] = addr;
+ msg[2] = 0;
+ msg[3] = 1;
+ msg[4] = value;
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_HDMI_TX, HDMI_TX_WRITE,
+ sizeof(msg), msg);
+ if (ret)
+ goto err_scdc_write;
+
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_HDMI_TX,
+ HDMI_TX_WRITE, sizeof(reg));
+ if (ret)
+ goto err_scdc_write;
+
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
+ if (ret)
+ goto err_scdc_write;
+
+ if (reg[0] != 0)
+ ret = -EINVAL;
+
+err_scdc_write:
+ if (ret)
+ DRM_ERROR("scdc write failed: %d\n", ret);
+ return ret;
+}
+
+int cdns_hdmi_ctrl_init(struct cdns_mhdp_device *mhdp,
+ int protocol, u32 char_rate)
+{
+ u32 reg0;
+ u32 reg1;
+ u32 val;
+ int ret;
+
+ /* Set PHY to HDMI data */
+ ret = cdns_mhdp_reg_write(mhdp, PHY_DATA_SEL, F_SOURCE_PHY_MHDP_SEL(1));
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_HPD,
+ F_HPD_VALID_WIDTH(4) | F_HPD_GLITCH_WIDTH(0));
+ if (ret < 0)
+ return ret;
+
+ /* open CARS */
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PHY_CAR, 0xF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_HDTX_CAR, 0xFF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_PKT_CAR, 0xF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_AIF_CAR, 0xF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CIPHER_CAR, 0xF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CRYPTO_CAR, 0xF);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, SOURCE_CEC_CAR, 3);
+ if (ret < 0)
+ return ret;
+
+ reg0 = reg1 = 0x7c1f;
+ if (protocol == MODE_HDMI_2_0 && char_rate >= 340000) {
+ reg0 = 0;
+ reg1 = 0xFFFFF;
+ }
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_0, reg0);
+ if (ret < 0)
+ return ret;
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CLOCK_REG_1, reg1);
+ if (ret < 0)
+ return ret;
+
+ /* set hdmi mode and preemble mode data enable */
+ val = F_HDMI_MODE(protocol) | F_HDMI2_PREAMBLE_EN(1) | F_DATA_EN(1) |
+ F_HDMI2_CTRL_IL_MODE(1) | F_BCH_EN(1) | F_PIC_3D(0XF);
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+
+ return ret;
+}
+
+int cdns_hdmi_mode_config(struct cdns_mhdp_device *mhdp,
+ struct drm_display_mode *mode,
+ struct video_info *video_info)
+{
+ int ret;
+ u32 val;
+ u32 vsync_lines = mode->vsync_end - mode->vsync_start;
+ u32 eof_lines = mode->vsync_start - mode->vdisplay;
+ u32 sof_lines = mode->vtotal - mode->vsync_end;
+ u32 hblank = mode->htotal - mode->hdisplay;
+ u32 hactive = mode->hdisplay;
+ u32 vblank = mode->vtotal - mode->vdisplay;
+ u32 vactive = mode->vdisplay;
+ u32 hfront = mode->hsync_start - mode->hdisplay;
+ u32 hback = mode->htotal - mode->hsync_end;
+ u32 vfront = eof_lines;
+ u32 hsync = hblank - hfront - hback;
+ u32 vsync = vsync_lines;
+ u32 vback = sof_lines;
+ u32 v_h_polarity = ((mode->flags & DRM_MODE_FLAG_NHSYNC) ? 0 : 1) +
+ ((mode->flags & DRM_MODE_FLAG_NVSYNC) ? 0 : 2);
+
+ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_H_SIZE, (hactive << 16) + hblank);
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, SCHEDULER_V_SIZE, (vactive << 16) + vblank);
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_FRONT_WIDTH, (vfront << 16) + hfront);
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_SYNC_WIDTH, (vsync << 16) + hsync);
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_SIGNAL_BACK_WIDTH, (vback << 16) + hback);
+ if (ret < 0)
+ return ret;
+
+ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, v_h_polarity);
+ if (ret < 0)
+ return ret;
+
+ /* Reset Data Enable */
+ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
+ val &= ~F_DATA_EN(1);
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+ if (ret < 0)
+ return ret;
+
+ /* Set bpc */
+ val &= ~F_VIF_DATA_WIDTH(3);
+ switch (video_info->color_depth) {
+ case 10:
+ val |= F_VIF_DATA_WIDTH(1);
+ break;
+ case 12:
+ val |= F_VIF_DATA_WIDTH(2);
+ break;
+ case 16:
+ val |= F_VIF_DATA_WIDTH(3);
+ break;
+ case 8:
+ default:
+ val |= F_VIF_DATA_WIDTH(0);
+ break;
+ }
+
+ /* select color encoding */
+ val &= ~F_HDMI_ENCODING(3);
+ switch (video_info->color_fmt) {
+ case YCBCR_4_4_4:
+ val |= F_HDMI_ENCODING(2);
+ break;
+ case YCBCR_4_2_2:
+ val |= F_HDMI_ENCODING(1);
+ break;
+ case YCBCR_4_2_0:
+ val |= F_HDMI_ENCODING(3);
+ break;
+ case PXL_RGB:
+ default:
+ val |= F_HDMI_ENCODING(0);
+ break;
+ }
+
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+ if (ret < 0)
+ return ret;
+
+ /* set data enable */
+ val |= F_DATA_EN(1);
+ ret = cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+
+ return ret;
+}
+
+int cdns_hdmi_disable_gcp(struct cdns_mhdp_device *mhdp)
+{
+ u32 val;
+
+ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
+ val &= ~F_GCP_EN(1);
+
+ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+}
+
+int cdns_hdmi_enable_gcp(struct cdns_mhdp_device *mhdp)
+{
+ u32 val;
+
+ val = cdns_mhdp_reg_read(mhdp, HDTX_CONTROLLER);
+ val |= F_GCP_EN(1);
+
+ return cdns_mhdp_reg_write(mhdp, HDTX_CONTROLLER, val);
+}
diff --git a/include/drm/bridge/cdns-mhdp.h b/include/drm/bridge/cdns-mhdp.h
index 6ffb97e17fae..7902ecb115e2 100644
--- a/include/drm/bridge/cdns-mhdp.h
+++ b/include/drm/bridge/cdns-mhdp.h
@@ -84,6 +84,7 @@
/* bellow registers need access by mailbox */
/* source phy comp */
+#define PHY_DATA_SEL 0x0818
#define LANES_CONFIG 0x0814
/* source car addr */
@@ -97,6 +98,17 @@
#define SOURCE_CIPHER_CAR 0x0920
#define SOURCE_CRYPTO_CAR 0x0924
+/* mhdp tx_top_comp */
+#define SCHEDULER_H_SIZE 0x1000
+#define SCHEDULER_V_SIZE 0x1004
+#define HDTX_SIGNAL_FRONT_WIDTH 0x100c
+#define HDTX_SIGNAL_SYNC_WIDTH 0x1010
+#define HDTX_SIGNAL_BACK_WIDTH 0x1014
+#define HDTX_CONTROLLER 0x1018
+#define HDTX_HPD 0x1020
+#define HDTX_CLOCK_REG_0 0x1024
+#define HDTX_CLOCK_REG_1 0x1028
+
/* clock meters addr */
#define CM_CTRL 0x0a00
#define CM_I2S_CTRL 0x0a04
@@ -333,6 +345,7 @@
#define GENERAL_READ_REGISTER 0x07
#define GENERAL_GET_HPD_STATE 0x11
+/* DPTX opcode */
#define DPTX_SET_POWER_MNG 0x00
#define DPTX_SET_HOST_CAPABILITIES 0x01
#define DPTX_GET_EDID 0x02
@@ -352,6 +365,17 @@
#define DPTX_FORCE_LANES 0x10
#define DPTX_HPD_STATE 0x11
+/* HDMI TX opcode */
+#define HDMI_TX_READ 0x00
+#define HDMI_TX_WRITE 0x01
+#define HDMI_TX_UPDATE_READ 0x02
+#define HDMI_TX_EDID 0x03
+#define HDMI_TX_EVENTS 0x04
+#define HDMI_TX_HPD_STATUS 0x05
+#define HDMI_TX_DEBUG_ECHO 0xAA
+#define HDMI_TX_TEST 0xBB
+#define HDMI_TX_EDID_INTERNAL 0xF0
+
#define FW_STANDBY 0
#define FW_ACTIVE 1
@@ -402,6 +426,34 @@
#define TU_SIZE 30
#define CDNS_DP_MAX_LINK_RATE 540000
+#define F_HDMI_ENCODING(x) (((x) & ((1 << 2) - 1)) << 16)
+#define F_VIF_DATA_WIDTH(x) (((x) & ((1 << 2) - 1)) << 2)
+#define F_HDMI_MODE(x) (((x) & ((1 << 2) - 1)) << 0)
+#define F_GCP_EN(x) (((x) & ((1 << 1) - 1)) << 12)
+#define F_DATA_EN(x) (((x) & ((1 << 1) - 1)) << 15)
+#define F_HDMI2_PREAMBLE_EN(x) (((x) & ((1 << 1) - 1)) << 18)
+#define F_PIC_3D(x) (((x) & ((1 << 4) - 1)) << 7)
+#define F_BCH_EN(x) (((x) & ((1 << 1) - 1)) << 11)
+#define F_SOURCE_PHY_MHDP_SEL(x) (((x) & ((1 << 2) - 1)) << 3)
+#define F_HPD_VALID_WIDTH(x) (((x) & ((1 << 12) - 1)) << 0)
+#define F_HPD_GLITCH_WIDTH(x) (((x) & ((1 << 8) - 1)) << 12)
+#define F_HDMI2_CTRL_IL_MODE(x) (((x) & ((1 << 1) - 1)) << 19)
+#define F_SOURCE_PHY_LANE0_SWAP(x) (((x) & ((1 << 2) - 1)) << 0)
+#define F_SOURCE_PHY_LANE1_SWAP(x) (((x) & ((1 << 2) - 1)) << 2)
+#define F_SOURCE_PHY_LANE2_SWAP(x) (((x) & ((1 << 2) - 1)) << 4)
+#define F_SOURCE_PHY_LANE3_SWAP(x) (((x) & ((1 << 2) - 1)) << 6)
+#define F_SOURCE_PHY_COMB_BYPASS(x) (((x) & ((1 << 1) - 1)) << 21)
+#define F_SOURCE_PHY_20_10(x) (((x) & ((1 << 1) - 1)) << 22)
+#define F_PKT_ALLOC_ADDRESS(x) (((x) & ((1 << 4) - 1)) << 0)
+#define F_ACTIVE_IDLE_TYPE(x) (((x) & ((1 << 1) - 1)) << 17)
+#define F_FIFO1_FLUSH(x) (((x) & ((1 << 1) - 1)) << 0)
+#define F_PKT_ALLOC_WR_EN(x) (((x) & ((1 << 1) - 1)) << 0)
+#define F_DATA_WR(x) (x)
+#define F_WR_ADDR(x) (((x) & ((1 << 4) - 1)) << 0)
+#define F_HOST_WR(x) (((x) & ((1 << 1) - 1)) << 0)
+#define F_TYPE_VALID(x) (((x) & ((1 << 1) - 1)) << 16)
+#define F_PACKET_TYPE(x) (((x) & ((1 << 8) - 1)) << 8)
+
/* audio */
#define AUDIO_PACK_EN BIT(8)
#define SAMPLING_FREQ(x) (((x) & 0xf) << 16)
@@ -481,6 +533,12 @@ enum audio_format {
AFMT_UNUSED,
};
+enum {
+ MODE_DVI,
+ MODE_HDMI_1_4,
+ MODE_HDMI_2_0,
+};
+
struct audio_info {
enum audio_format format;
int sample_rate;
@@ -567,6 +625,11 @@ struct cdns_mhdp_device {
struct drm_dp_aux aux;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
} dp;
+ struct _hdmi_data {
+ u32 char_rate;
+ u32 hdmi_type;
+ const struct drm_display_mode *mode_valid;
+ } hdmi;
};
const struct cdns_plat_data *plat_data;
@@ -603,4 +666,10 @@ u32 cdns_phy_reg_read(struct cdns_mhdp_device *mhdp, u32 addr);
int cdns_dp_bind(struct platform_device *pdev, struct drm_encoder *encoder,
struct cdns_mhdp_device *mhdp);
void cdns_dp_unbind(struct device *dev);
+
+/* HDMI */
+int cdns_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
+ struct cdns_mhdp_device *mhdp);
+void cdns_hdmi_unbind(struct device *dev);
+
#endif /* CDNS_MHDP_H_ */
--
2.17.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 6/7] drm: imx: mhdp: Initial support for i.MX8MQ MHDP HDMI
From: sandor.yu @ 2020-06-01 6:17 UTC (permalink / raw)
To: a.hajda, narmstrong, Laurent.pinchart, jonas, jernej.skrabec,
heiko, hjc, Sandor.yu, dkos, dri-devel
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, linux-imx
In-Reply-To: <cover.1590982881.git.Sandor.yu@nxp.com>
From: Sandor Yu <Sandor.yu@nxp.com>
Add initial support for i.MX8MQ MHDP HDMI.
Add MHDP HDMI PHY configuration.
The features are same as mhdp hdmi bridge driver.
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
drivers/gpu/drm/imx/mhdp/Kconfig | 5 +-
drivers/gpu/drm/imx/mhdp/Makefile | 2 +-
drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c | 588 ++++++++++++++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 12 +
drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 2 +
5 files changed, 606 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig
index c9e07a3bf3df..fc0cf708b900 100644
--- a/drivers/gpu/drm/imx/mhdp/Kconfig
+++ b/drivers/gpu/drm/imx/mhdp/Kconfig
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
config DRM_IMX_CDNS_MHDP
- tristate "NXP i.MX MX8 DRM DP"
+ tristate "NXP i.MX MX8 DRM DP/HDMI"
select DRM_CDNS_MHDP
select DRM_CDNS_DP
+ select DRM_CDNS_HDMI
help
- Choose this if you want to use Displayport on i.MX8.
+ Choose this if you want to use Displayport/HDMI on i.MX8.
diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile
index 4383e689445a..ca51a2a9641c 100644
--- a/drivers/gpu/drm/imx/mhdp/Makefile
+++ b/drivers/gpu/drm/imx/mhdp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o
+cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o cdns-mhdp-hdmi-phy.o
obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
new file mode 100644
index 000000000000..54b01e13a2b6
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-hdmi-phy.c
@@ -0,0 +1,588 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cadence High-Definition Multimedia Interface (HDMI) PHY driver
+ *
+ * Copyright (C) 2019-2020 NXP Semiconductor, Inc.
+ */
+#include <drm/drm_of.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_print.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/io.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_encoder_slave.h>
+#include <drm/drm_atomic.h>
+#include <linux/io.h>
+
+#include <drm/bridge/cdns-mhdp.h>
+#include "cdns-mhdp-phy.h"
+
+/* HDMI TX clock control settings */
+struct hdmi_ctrl {
+ u32 pixel_clk_freq_min;
+ u32 pixel_clk_freq_max;
+ u32 feedback_factor;
+ u32 data_range_kbps_min;
+ u32 data_range_kbps_max;
+ u32 cmnda_pll0_ip_div;
+ u32 cmn_ref_clk_dig_div;
+ u32 ref_clk_divider_scaler;
+ u32 pll_fb_div_total;
+ u32 cmnda_pll0_fb_div_low;
+ u32 cmnda_pll0_fb_div_high;
+ u32 pixel_div_total;
+ u32 cmnda_pll0_pxdiv_low;
+ u32 cmnda_pll0_pxdiv_high;
+ u32 vco_freq_min;
+ u32 vco_freq_max;
+ u32 vco_ring_select;
+ u32 cmnda_hs_clk_0_sel;
+ u32 cmnda_hs_clk_1_sel;
+ u32 hsclk_div_at_xcvr;
+ u32 hsclk_div_tx_sub_rate;
+ u32 cmnda_pll0_hs_sym_div_sel;
+ u32 cmnda_pll0_clk_freq_min;
+ u32 cmnda_pll0_clk_freq_max;
+};
+
+/* HDMI TX clock control settings, pixel clock is output */
+static const struct hdmi_ctrl imx8mq_ctrl_table[] = {
+/*Minclk Maxclk Fdbak DR_min DR_max ip_d dig DS Totl */
+{ 27000, 27000, 1000, 270000, 270000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x3, 27000, 27000},
+{ 27000, 27000, 1250, 337500, 337500, 0x03, 0x1, 0x1, 300, 0x0EC, 0x03C, 100, 0x030, 0x030, 2700000, 2700000, 0, 2, 2, 2, 4, 0x3, 33750, 33750},
+{ 27000, 27000, 1500, 405000, 405000, 0x03, 0x1, 0x1, 360, 0x11C, 0x048, 120, 0x03A, 0x03A, 3240000, 3240000, 0, 2, 2, 2, 4, 0x3, 40500, 40500},
+{ 27000, 27000, 2000, 540000, 540000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 80, 0x026, 0x026, 2160000, 2160000, 0, 2, 2, 2, 4, 0x2, 54000, 54000},
+{ 54000, 54000, 1000, 540000, 540000, 0x03, 0x1, 0x1, 480, 0x17C, 0x060, 80, 0x026, 0x026, 4320000, 4320000, 1, 2, 2, 2, 4, 0x3, 54000, 54000},
+{ 54000, 54000, 1250, 675000, 675000, 0x04, 0x1, 0x1, 400, 0x13C, 0x050, 50, 0x017, 0x017, 2700000, 2700000, 0, 1, 1, 2, 4, 0x2, 67500, 67500},
+{ 54000, 54000, 1500, 810000, 810000, 0x04, 0x1, 0x1, 480, 0x17C, 0x060, 60, 0x01C, 0x01C, 3240000, 3240000, 0, 2, 2, 2, 2, 0x2, 81000, 81000},
+{ 54000, 54000, 2000, 1080000, 1080000, 0x03, 0x1, 0x1, 240, 0x0BC, 0x030, 40, 0x012, 0x012, 2160000, 2160000, 0, 2, 2, 2, 1, 0x1, 108000, 108000},
+{ 74250, 74250, 1000, 742500, 742500, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 80, 0x026, 0x026, 5940000, 5940000, 1, 2, 2, 2, 4, 0x3, 74250, 74250},
+{ 74250, 74250, 1250, 928125, 928125, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 50, 0x017, 0x017, 3712500, 3712500, 1, 1, 1, 2, 4, 0x2, 92812, 92812},
+{ 74250, 74250, 1500, 1113750, 1113750, 0x04, 0x1, 0x1, 660, 0x20C, 0x084, 60, 0x01C, 0x01C, 4455000, 4455000, 1, 2, 2, 2, 2, 0x2, 111375, 111375},
+{ 74250, 74250, 2000, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 40, 0x012, 0x012, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500},
+{ 99000, 99000, 1000, 990000, 990000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 2, 0x2, 99000, 99000},
+{ 99000, 99000, 1250, 1237500, 1237500, 0x03, 0x1, 0x1, 275, 0x0D8, 0x037, 25, 0x00B, 0x00A, 2475000, 2475000, 0, 1, 1, 2, 2, 0x1, 123750, 123750},
+{ 99000, 99000, 1500, 1485000, 1485000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 30, 0x00D, 0x00D, 2970000, 2970000, 0, 2, 2, 2, 1, 0x1, 148500, 148500},
+{ 99000, 99000, 2000, 1980000, 1980000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 40, 0x012, 0x012, 3960000, 3960000, 1, 2, 2, 2, 1, 0x1, 198000, 198000},
+{148500, 148500, 1000, 1485000, 1485000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 2, 0x2, 148500, 148500},
+{148500, 148500, 1250, 1856250, 1856250, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 3712500, 3712500, 1, 1, 1, 2, 2, 0x1, 185625, 185625},
+{148500, 148500, 1500, 2227500, 2227500, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 30, 0x00D, 0x00D, 4455000, 4455000, 1, 1, 1, 2, 2, 0x1, 222750, 222750},
+{148500, 148500, 2000, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 40, 0x012, 0x012, 5940000, 5940000, 1, 2, 2, 2, 1, 0x1, 297000, 297000},
+{198000, 198000, 1000, 1980000, 1980000, 0x03, 0x1, 0x1, 220, 0x0AC, 0x02C, 10, 0x003, 0x003, 1980000, 1980000, 0, 1, 1, 2, 1, 0x0, 198000, 198000},
+{198000, 198000, 1250, 2475000, 2475000, 0x03, 0x1, 0x1, 550, 0x1B4, 0x06E, 25, 0x00B, 0x00A, 4950000, 4950000, 1, 1, 1, 2, 2, 0x1, 247500, 247500},
+{198000, 198000, 1500, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 15, 0x006, 0x005, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000},
+{198000, 198000, 2000, 3960000, 3960000, 0x03, 0x1, 0x1, 440, 0x15C, 0x058, 20, 0x008, 0x008, 3960000, 3960000, 1, 1, 1, 2, 1, 0x0, 396000, 396000},
+{297000, 297000, 1000, 2970000, 2970000, 0x03, 0x1, 0x1, 330, 0x104, 0x042, 10, 0x003, 0x003, 2970000, 2970000, 0, 1, 1, 2, 1, 0x0, 297000, 297000},
+{297000, 297000, 1500, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 15, 0x006, 0x005, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500},
+{297000, 297000, 2000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 20, 0x008, 0x008, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000},
+{594000, 594000, 1000, 5940000, 5940000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 1, 0x0, 594000, 594000},
+{594000, 594000, 750, 4455000, 4455000, 0x03, 0x1, 0x1, 495, 0x188, 0x063, 10, 0x003, 0x003, 4455000, 4455000, 1, 1, 1, 2, 1, 0x0, 445500, 445500},
+{594000, 594000, 625, 3712500, 3712500, 0x04, 0x1, 0x1, 550, 0x1B4, 0x06E, 10, 0x003, 0x003, 3712500, 3712500, 1, 1, 1, 2, 1, 0x0, 371250, 371250},
+{594000, 594000, 500, 2970000, 2970000, 0x03, 0x1, 0x1, 660, 0x20C, 0x084, 10, 0x003, 0x003, 5940000, 5940000, 1, 1, 1, 2, 2, 0x1, 297000, 297000},
+};
+
+/* HDMI TX PLL tuning settings */
+struct hdmi_pll_tuning {
+ u32 vco_freq_bin;
+ u32 vco_freq_min;
+ u32 vco_freq_max;
+ u32 volt_to_current_coarse;
+ u32 volt_to_current;
+ u32 ndac_ctrl;
+ u32 pmos_ctrl;
+ u32 ptat_ndac_ctrl;
+ u32 feedback_div_total;
+ u32 charge_pump_gain;
+ u32 coarse_code;
+ u32 v2i_code;
+ u32 vco_cal_code;
+};
+
+/* HDMI TX PLL tuning settings, pixel clock is output */
+static const struct hdmi_pll_tuning imx8mq_pll_table[] = {
+/* bin VCO_freq min/max coar cod NDAC PMOS PTAT div-T P-Gain Coa V2I CAL */
+ { 1, 1980000, 1980000, 0x4, 0x3, 0x0, 0x09, 0x09, 220, 0x42, 160, 5, 183 },
+ { 2, 2160000, 2160000, 0x4, 0x3, 0x0, 0x09, 0x09, 240, 0x42, 166, 6, 208 },
+ { 3, 2475000, 2475000, 0x5, 0x3, 0x1, 0x00, 0x07, 275, 0x42, 167, 6, 209 },
+ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 300, 0x42, 188, 6, 230 },
+ { 4, 2700000, 2700000, 0x5, 0x3, 0x1, 0x00, 0x07, 400, 0x4C, 188, 6, 230 },
+ { 5, 2970000, 2970000, 0x6, 0x3, 0x1, 0x00, 0x07, 330, 0x42, 183, 6, 225 },
+ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 360, 0x42, 203, 7, 256 },
+ { 6, 3240000, 3240000, 0x6, 0x3, 0x1, 0x00, 0x07, 480, 0x4C, 203, 7, 256 },
+ { 7, 3712500, 3712500, 0x4, 0x3, 0x0, 0x07, 0x0F, 550, 0x4C, 212, 7, 257 },
+ { 8, 3960000, 3960000, 0x5, 0x3, 0x0, 0x07, 0x0F, 440, 0x42, 184, 6, 226 },
+ { 9, 4320000, 4320000, 0x5, 0x3, 0x1, 0x07, 0x0F, 480, 0x42, 205, 7, 258 },
+ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 495, 0x42, 219, 7, 272 },
+ { 10, 4455000, 4455000, 0x5, 0x3, 0x0, 0x07, 0x0F, 660, 0x4C, 219, 7, 272 },
+ { 11, 4950000, 4950000, 0x6, 0x3, 0x1, 0x00, 0x07, 550, 0x42, 213, 7, 258 },
+ { 12, 5940000, 5940000, 0x7, 0x3, 0x1, 0x00, 0x07, 660, 0x42, 244, 8, 292 },
+};
+
+static void hdmi_arc_config(struct cdns_mhdp_device *mhdp)
+{
+ u16 txpu_calib_code;
+ u16 txpd_calib_code;
+ u16 txpu_adj_calib_code;
+ u16 txpd_adj_calib_code;
+ u16 prev_calib_code;
+ u16 new_calib_code;
+ u16 rdata;
+
+ /* Power ARC */
+ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 0x0001);
+
+ prev_calib_code = cdns_phy_reg_read(mhdp, TX_DIG_CTRL_REG_2);
+ txpu_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPUCAL_CTRL);
+ txpd_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPDCAL_CTRL);
+ txpu_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPU_ADJ_CTRL);
+ txpd_adj_calib_code = cdns_phy_reg_read(mhdp, CMN_TXPD_ADJ_CTRL);
+
+ new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2)
+ + txpu_adj_calib_code + txpd_adj_calib_code;
+
+ if (new_calib_code != prev_calib_code) {
+ rdata = cdns_phy_reg_read(mhdp, TX_ANA_CTRL_REG_1);
+ rdata &= 0xDFFF;
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata);
+ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, new_calib_code);
+ mdelay(10);
+ rdata |= 0x2000;
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, rdata);
+ udelay(150);
+ }
+
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2098);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0010);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x4001);
+ mdelay(5);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2198);
+ mdelay(5);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030D);
+ udelay(100);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030F);
+}
+
+static void hdmi_phy_set_vswing(struct cdns_mhdp_device *mhdp)
+{
+ const u32 num_lanes = 4;
+ u32 k;
+
+ for (k = 0; k < num_lanes; k++) {
+ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_DRV | (k << 9)), 0x7c0);
+ cdns_phy_reg_write(mhdp, (TX_TXCC_CPOST_MULT_00_0 | (k << 9)), 0x0);
+ cdns_phy_reg_write(mhdp, (TX_TXCC_CAL_SCLR_MULT_0 | (k << 9)), 0x120);
+ }
+}
+
+static int hdmi_feedback_factor(struct cdns_mhdp_device *mhdp)
+{
+ u32 feedback_factor;
+
+ switch (mhdp->video_info.color_fmt) {
+ case YCBCR_4_2_2:
+ feedback_factor = 1000;
+ break;
+ case YCBCR_4_2_0:
+ switch (mhdp->video_info.color_depth) {
+ case 8:
+ feedback_factor = 500;
+ break;
+ case 10:
+ feedback_factor = 625;
+ break;
+ case 12:
+ feedback_factor = 750;
+ break;
+ case 16:
+ feedback_factor = 1000;
+ break;
+ default:
+ DRM_ERROR("Invalid ColorDepth\n");
+ return 0;
+ }
+ break;
+ default:
+ /* Assume RGB/YUV444 */
+ switch (mhdp->video_info.color_depth) {
+ case 10:
+ feedback_factor = 1250;
+ break;
+ case 12:
+ feedback_factor = 1500;
+ break;
+ case 16:
+ feedback_factor = 2000;
+ break;
+ default:
+ feedback_factor = 1000;
+ }
+ }
+ return feedback_factor;
+}
+
+static int hdmi_phy_config(struct cdns_mhdp_device *mhdp,
+ const struct hdmi_ctrl *p_ctrl_table,
+ const struct hdmi_pll_tuning *p_pll_table,
+ char pclk_in)
+{
+ const u32 num_lanes = 4;
+ u32 val, i, k;
+
+ /* enable PHY isolation mode only for CMN */
+ cdns_phy_reg_write(mhdp, PHY_PMA_ISOLATION_CTRL, 0xD000);
+
+ /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_PLL_CTRL1);
+ val &= 0xFF00;
+ val |= 0x0012;
+ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_PLL_CTRL1, val);
+
+ /* assert PHY reset from isolation register */
+ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0000);
+ /* assert PMA CMN reset */
+ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0000);
+
+ /* register XCVR_DIAG_BIDI_CTRL */
+ for (k = 0; k < num_lanes; k++)
+ cdns_phy_reg_write(mhdp, XCVR_DIAG_BIDI_CTRL | (k << 9), 0x00FF);
+
+ /* Describing Task phy_cfg_hdp */
+
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ val &= 0xFFF7;
+ val |= 0x0008;
+ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
+
+ /* PHY Registers */
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ val &= 0xCFFF;
+ val |= p_ctrl_table->cmn_ref_clk_dig_div << 12;
+ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
+
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val &= 0x00FF;
+ val |= 0x1200;
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+
+ /* Common control module control and diagnostic registers */
+ val = cdns_phy_reg_read(mhdp, CMN_CDIAG_REFCLK_CTRL);
+ val &= 0x8FFF;
+ val |= p_ctrl_table->ref_clk_divider_scaler << 12;
+ val |= 0x00C0;
+ cdns_phy_reg_write(mhdp, CMN_CDIAG_REFCLK_CTRL, val);
+
+ /* High speed clock used */
+ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL);
+ val &= 0xFF00;
+ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 0;
+ val |= (p_ctrl_table->cmnda_hs_clk_1_sel >> 1) << 4;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val);
+
+ for (k = 0; k < num_lanes; k++) {
+ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)));
+ val &= 0xCFFF;
+ val |= (p_ctrl_table->cmnda_hs_clk_0_sel >> 1) << 12;
+ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val);
+ }
+
+ /* PLL 0 control state machine registers */
+ val = p_ctrl_table->vco_ring_select << 12;
+ cdns_phy_reg_write(mhdp, CMN_PLLSM0_USER_DEF_CTRL, val);
+
+ if (pclk_in == true)
+ val = 0x30A0;
+ else {
+ val = cdns_phy_reg_read(mhdp, CMN_PLL0_VCOCAL_START);
+ val &= 0xFE00;
+ val |= p_pll_table->vco_cal_code;
+ }
+ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_START, val);
+
+ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_INIT_TMR, 0x0064);
+ cdns_phy_reg_write(mhdp, CMN_PLL0_VCOCAL_ITER_TMR, 0x000A);
+
+ /* Common functions control and diagnostics registers */
+ val = p_ctrl_table->cmnda_pll0_hs_sym_div_sel << 8;
+ val |= p_ctrl_table->cmnda_pll0_ip_div;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_INCLK_CTRL, val);
+
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_OVRD, 0x0000);
+
+ val = p_ctrl_table->cmnda_pll0_fb_div_high;
+ val |= (1 << 15);
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBH_OVRD, val);
+
+ val = p_ctrl_table->cmnda_pll0_fb_div_low;
+ val |= (1 << 15);
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_FBL_OVRD, val);
+
+ if (pclk_in == false) {
+ val = p_ctrl_table->cmnda_pll0_pxdiv_low;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVL, val);
+
+ val = p_ctrl_table->cmnda_pll0_pxdiv_high;
+ val |= (1 << 15);
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PXL_DIVH, val);
+ }
+
+ val = p_pll_table->volt_to_current_coarse;
+ val |= (p_pll_table->volt_to_current) << 4;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_V2I_TUNE, val);
+
+ val = p_pll_table->charge_pump_gain;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_CP_TUNE, val);
+
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_LF_PROG, 0x0008);
+
+ val = p_pll_table->pmos_ctrl;
+ val |= (p_pll_table->ndac_ctrl) << 8;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE1, val);
+
+ val = p_pll_table->ptat_ndac_ctrl;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_PTATIS_TUNE2, val);
+
+ if (pclk_in == true)
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0022);
+ else
+ cdns_phy_reg_write(mhdp, CMN_DIAG_PLL0_TEST_MODE, 0x0020);
+ cdns_phy_reg_write(mhdp, CMN_PSM_CLK_CTRL, 0x0016);
+
+ /* Transceiver control and diagnostic registers */
+ for (k = 0; k < num_lanes; k++) {
+ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)));
+ val &= 0xBFFF;
+ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val);
+ }
+
+ for (k = 0; k < num_lanes; k++) {
+ val = cdns_phy_reg_read(mhdp, (TX_DIAG_TX_CTRL | (k << 9)));
+ val &= 0xFF3F;
+ val |= (p_ctrl_table->hsclk_div_tx_sub_rate >> 1) << 6;
+ cdns_phy_reg_write(mhdp, (TX_DIAG_TX_CTRL | (k << 9)), val);
+ }
+
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ val &= 0xFF8F;
+ /*
+ * TODO
+ * Pixel clock source from CCM val |= 0x0030
+ * Pixel clock gererated by PHY(iMX8MQ):
+ * --single ended reference clock val |= 0x0030;
+ * --differential clock val |= 0x0000;
+ */
+ val |= 0x0030;
+ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
+
+ /* for differential clock on the refclk_p and
+ * refclk_m off chip pins: CMN_DIAG_ACYA[8]=1'b1 */
+ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100);
+
+ /* Deassert PHY reset */
+ cdns_phy_reg_write(mhdp, PHY_ISO_CMN_CTRL, 0x0001);
+ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0003);
+
+ /* Power state machine registers */
+ for (k = 0; k < num_lanes; k++)
+ cdns_phy_reg_write(mhdp, XCVR_PSM_RCTRL | (k << 9), 0xFEFC);
+
+ /* Assert cmn_macro_pwr_en */
+ cdns_phy_reg_write(mhdp, PHY_PMA_ISO_CMN_CTRL, 0x0013);
+
+ /* wait for cmn_macro_pwr_en_ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_ISO_CMN_CTRL);
+ if (val & (1 << 5))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ DRM_ERROR("PMA ouput macro power up failed\n");
+ return false;
+ }
+
+ /* wait for cmn_ready */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ if (val & (1 << 0))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ DRM_ERROR("PMA output ready failed\n");
+ return false;
+ }
+
+ for (k = 0; k < num_lanes; k++) {
+ cdns_phy_reg_write(mhdp, TX_PSC_A0 | (k << 9), 0x6791);
+ cdns_phy_reg_write(mhdp, TX_PSC_A1 | (k << 9), 0x6790);
+ cdns_phy_reg_write(mhdp, TX_PSC_A2 | (k << 9), 0x0090);
+ cdns_phy_reg_write(mhdp, TX_PSC_A3 | (k << 9), 0x0090);
+
+ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9));
+ val &= 0xFFBB;
+ cdns_phy_reg_write(mhdp, RX_PSC_CAL | (k << 9), val);
+
+ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9));
+ val &= 0xFFBB;
+ cdns_phy_reg_write(mhdp, RX_PSC_A0 | (k << 9), val);
+ }
+ return true;
+}
+
+static int hdmi_phy_cfg_imx8mq(struct cdns_mhdp_device *mhdp,
+ struct drm_display_mode *mode)
+{
+ const struct hdmi_ctrl *p_ctrl_table;
+ const struct hdmi_pll_tuning *p_pll_table;
+ const u32 refclk_freq_khz = 27000;
+ const u8 pclk_in = false;
+ u32 pixel_freq = mode->clock;
+ u32 vco_freq, char_freq;
+ u32 div_total, feedback_factor;
+ u32 i, ret;
+
+ feedback_factor = hdmi_feedback_factor(mhdp);
+
+ char_freq = pixel_freq * feedback_factor / 1000;
+
+ DRM_INFO("Pixel clock: %d KHz, character clock: %d, bpc is %0d-bit.\n",
+ pixel_freq, char_freq, mhdp->video_info.color_depth);
+
+ /* Get right row from the ctrl_table table.
+ * Check if 'pixel_freq_khz' value matches the PIXEL_CLK_FREQ column.
+ * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */
+ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++) {
+ if (feedback_factor == imx8mq_ctrl_table[i].feedback_factor &&
+ pixel_freq == imx8mq_ctrl_table[i].pixel_clk_freq_min) {
+ p_ctrl_table = &imx8mq_ctrl_table[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(imx8mq_ctrl_table)) {
+ DRM_WARN("Pixel clk (%d KHz) not supported, color depth (%0d-bit)\n",
+ pixel_freq, mhdp->video_info.color_depth);
+ return 0;
+ }
+
+ div_total = p_ctrl_table->pll_fb_div_total;
+ vco_freq = refclk_freq_khz * div_total / p_ctrl_table->cmnda_pll0_ip_div;
+
+ /* Get right row from the imx8mq_pll_table table.
+ * Check if vco_freq_khz and feedback_div_total
+ * column matching with imx8mq_pll_table. */
+ for (i = 0; i < ARRAY_SIZE(imx8mq_pll_table); i++) {
+ if (vco_freq == imx8mq_pll_table[i].vco_freq_min &&
+ div_total == imx8mq_pll_table[i].feedback_div_total) {
+ p_pll_table = &imx8mq_pll_table[i];
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(imx8mq_pll_table)) {
+ DRM_WARN("VCO (%d KHz) not supported\n", vco_freq);
+ return 0;
+ }
+ DRM_INFO("VCO frequency is %d KHz\n", vco_freq);
+
+ ret = hdmi_phy_config(mhdp, p_ctrl_table, p_pll_table, pclk_in);
+ if (ret == false)
+ return 0;
+
+ return char_freq;
+}
+
+static int hdmi_phy_power_up(struct cdns_mhdp_device *mhdp)
+{
+ u32 val, i;
+
+ /* set Power State to A2 */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004);
+
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
+
+ /* Wait for Power State A2 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 6))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A2 Ack failed\n");
+ return -1;
+ }
+
+ /* Power up ARC */
+ hdmi_arc_config(mhdp);
+
+ /* Configure PHY in A0 mode (PHY must be in the A0 power
+ * state in order to transmit data)
+ */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0001);
+
+ /* Wait for Power State A0 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 4))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A0 Ack failed\n");
+ return -1;
+ }
+ return 0;
+}
+
+bool cdns_hdmi_phy_mode_valid_imx8mq(struct cdns_mhdp_device *mhdp)
+{
+ u32 rate = mhdp->hdmi.mode_valid->clock;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx8mq_ctrl_table); i++)
+ if (rate == imx8mq_ctrl_table[i].pixel_clk_freq_min)
+ return true;
+ return false;
+}
+
+int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp)
+{
+ struct drm_display_mode *mode = &mhdp->mode;
+ int ret;
+
+ /* Check HDMI FW alive before HDMI PHY init */
+ ret = cdns_mhdp_check_alive(mhdp);
+ if (ret == false) {
+ DRM_ERROR("NO HDMI FW running\n");
+ return -ENXIO;
+ }
+
+ /* Configure PHY */
+ mhdp->hdmi.char_rate = hdmi_phy_cfg_imx8mq(mhdp, mode);
+ if (mhdp->hdmi.char_rate == 0) {
+ DRM_ERROR("failed to set phy pclock\n");
+ return -EINVAL;
+ }
+
+ ret = hdmi_phy_power_up(mhdp);
+ if (ret < 0)
+ return ret;
+
+ hdmi_phy_set_vswing(mhdp);
+
+ return true;
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
index 2dec2e051be6..607d0b34b551 100644
--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
@@ -23,6 +23,15 @@ static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = {
.destroy = drm_encoder_cleanup,
};
+static struct cdns_plat_data imx8mq_hdmi_drv_data = {
+ .plat_name = "imx8mq-hdmi",
+ .bind = cdns_hdmi_bind,
+ .unbind = cdns_hdmi_unbind,
+ .phy_set = cdns_hdmi_phy_set_imx8mq,
+ .phy_video_valid = cdns_hdmi_phy_mode_valid_imx8mq,
+ .lane_mapping = 0xe4,
+};
+
static struct cdns_plat_data imx8mq_dp_drv_data = {
.plat_name = "imx8mq-dp",
.bind = cdns_dp_bind,
@@ -32,6 +41,9 @@ static struct cdns_plat_data imx8mq_dp_drv_data = {
};
static const struct of_device_id cdns_mhdp_imx_dt_ids[] = {
+ { .compatible = "nxp,imx8mq-cdns-hdmi",
+ .data = &imx8mq_hdmi_drv_data
+ },
{ .compatible = "nxp,imx8mq-cdns-dp",
.data = &imx8mq_dp_drv_data
},
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
index 79b1907726db..3305a27bcec7 100644
--- a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
@@ -143,4 +143,6 @@
#define PHY_PMA_ISO_RX_DATA_HI 0xCC17
int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp);
+int cdns_hdmi_phy_set_imx8mq(struct cdns_mhdp_device *mhdp);
+bool cdns_hdmi_phy_mode_valid_imx8mq(struct cdns_mhdp_device *mhdp);
#endif /* _CDNS_MHDP_PHY_H */
--
2.17.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 7/7] dt-bindings: display: Document Cadence MHDP HDMI/DP bindings
From: sandor.yu @ 2020-06-01 6:17 UTC (permalink / raw)
To: a.hajda, narmstrong, Laurent.pinchart, jonas, jernej.skrabec,
heiko, hjc, Sandor.yu, dkos, dri-devel
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, linux-imx
In-Reply-To: <cover.1590982881.git.Sandor.yu@nxp.com>
From: Sandor Yu <Sandor.yu@nxp.com>
Document the bindings used for the Cadence MHDP HDMI/DP bridge.
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
.../bindings/display/bridge/cdns,mhdp.yaml | 46 +++++++++++++++
.../devicetree/bindings/display/imx/mhdp.yaml | 59 +++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml
create mode 100644 Documentation/devicetree/bindings/display/imx/mhdp.yaml
diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml
new file mode 100644
index 000000000000..aa23feba744a
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause))
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/cdns,mhdp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cadence MHDP TX Encoder
+
+maintainers:
+ - Sandor Yu <Sandoryu@nxp.com>
+
+description: |
+ Cadence MHDP Controller supports one or more of the protocols,
+ such as HDMI and DisplayPort.
+ Each protocol requires a different FW binaries.
+
+ This document defines device tree properties for the Cadence MHDP Encoder
+ (CDNS MHDP TX). It doesn't constitue a device tree binding
+ specification by itself but is meant to be referenced by platform-specific
+ device tree bindings.
+
+ When referenced from platform device tree bindings the properties defined in
+ this document are defined as follows. The platform device tree bindings are
+ responsible for defining whether each property is required or optional.
+
+properties:
+ reg:
+ maxItems: 1
+ description: Memory mapped base address and length of the MHDP TX registers.
+
+ interrupts:
+ maxItems: 2
+
+ interrupt-names:
+ - const: plug_in
+ description: Hotplug detect interrupter for cable plugin event.
+ - const: plug_out
+ description: Hotplug detect interrupter for cable plugout event.
+
+ port:
+ type: object
+ description: |
+ The connectivity of the MHDP TX with the rest of the system is
+ expressed in using ports as specified in the device graph bindings defined
+ in Documentation/devicetree/bindings/graph.txt. The numbering of the ports
+ is platform-specific.
diff --git a/Documentation/devicetree/bindings/display/imx/mhdp.yaml b/Documentation/devicetree/bindings/display/imx/mhdp.yaml
new file mode 100644
index 000000000000..17850cfd1cb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/imx/mhdp.yaml
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/mhdp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cadence MHDP Encoder
+
+maintainers:
+ - Sandor Yu <Sandoryu@nxp.com>
+
+description: |
+ The MHDP transmitter is a Cadence HD Display TX controller IP
+ with a companion PHY IP.
+ The MHDP supports one or more of the protocols,
+ such as HDMI(1.4 & 2.0), DisplayPort(1.2).
+ switching between the two modes (HDMI and DisplayPort)
+ requires reloading the appropriate FW
+
+ These DT bindings follow the Cadence MHDP TX bindings defined in
+ Documentation/devicetree/bindings/display/bridge/cdns,mhdp.yaml with the
+ following device-specific properties.
+
+Properties:
+ compatible:
+ enum:
+ - nxp,imx8mq-cdns-hdmi
+ - nxp,imx8mq-cdns-dp
+
+ reg: See cdns,mhdp.yaml.
+
+ interrupts: See cdns,mhdp.yaml.
+
+ interrupt-names: See cdns,mhdp.yaml.
+
+ ports: See cdns,mhdp.yaml.
+
+Required:
+ - compatible
+ - reg
+ - interrupts
+ - interrupt-names
+ - ports
+
+Example:
+ - |
+ mhdp: mhdp@32c00000 {
+ compatible = "nxp,imx8mq-cdns-hdmi";
+ reg = <0x32c00000 0x100000>;
+ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "plug_in", "plug_out";
+
+ ports {
+ mhdp_in: endpoint {
+ remote-endpoint = <&dcss_out>;
+ };
+ };
+ };
--
2.17.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 4/7] drm: imx: mhdp: initial support for i.MX8MQ MHDP Displayport
From: sandor.yu @ 2020-06-01 6:17 UTC (permalink / raw)
To: a.hajda, narmstrong, Laurent.pinchart, jonas, jernej.skrabec,
heiko, hjc, Sandor.yu, dkos, dri-devel
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, linux-imx
In-Reply-To: <cover.1590982881.git.Sandor.yu@nxp.com>
From: Sandor Yu <Sandor.yu@nxp.com>
Initial support for i.MX8MQ MHDP Displayport.
Add MHDP DP PHY configutation.
The features are same as MHDP DP bridge driver.
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
drivers/gpu/drm/imx/Kconfig | 1 +
drivers/gpu/drm/imx/Makefile | 1 +
drivers/gpu/drm/imx/mhdp/Kconfig | 8 +
drivers/gpu/drm/imx/mhdp/Makefile | 4 +
drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c | 390 ++++++++++++++++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c | 130 +++++++
drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h | 146 ++++++++
7 files changed, 680 insertions(+)
create mode 100644 drivers/gpu/drm/imx/mhdp/Kconfig
create mode 100644 drivers/gpu/drm/imx/mhdp/Makefile
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
create mode 100644 drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig
index 6231048aa5aa..4af2f575f04b 100644
--- a/drivers/gpu/drm/imx/Kconfig
+++ b/drivers/gpu/drm/imx/Kconfig
@@ -41,3 +41,4 @@ config DRM_IMX_HDMI
Choose this if you want to use HDMI on i.MX6.
source "drivers/gpu/drm/imx/dcss/Kconfig"
+source "drivers/gpu/drm/imx/mhdp/Kconfig"
diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile
index b644deffe948..0b46c46b19a8 100644
--- a/drivers/gpu/drm/imx/Makefile
+++ b/drivers/gpu/drm/imx/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o
obj-$(CONFIG_DRM_IMX_DCSS) += dcss/
+obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += mhdp/
diff --git a/drivers/gpu/drm/imx/mhdp/Kconfig b/drivers/gpu/drm/imx/mhdp/Kconfig
new file mode 100644
index 000000000000..c9e07a3bf3df
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config DRM_IMX_CDNS_MHDP
+ tristate "NXP i.MX MX8 DRM DP"
+ select DRM_CDNS_MHDP
+ select DRM_CDNS_DP
+ help
+ Choose this if you want to use Displayport on i.MX8.
diff --git a/drivers/gpu/drm/imx/mhdp/Makefile b/drivers/gpu/drm/imx/mhdp/Makefile
new file mode 100644
index 000000000000..4383e689445a
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+cdns_mhdp_imx-objs := cdns-mhdp-imxdrv.o cdns-mhdp-dp-phy.o
+obj-$(CONFIG_DRM_IMX_CDNS_MHDP) += cdns_mhdp_imx.o
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
new file mode 100644
index 000000000000..bb694301984d
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-dp-phy.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cadence Display Port Interface (DP) PHY driver
+ *
+ * Copyright (C) 2019-2020 NXP Semiconductor, Inc.
+ */
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/bridge/cdns-mhdp.h>
+#include "cdns-mhdp-phy.h"
+
+enum dp_link_rate {
+ RATE_1_6 = 162000,
+ RATE_2_1 = 216000,
+ RATE_2_4 = 243000,
+ RATE_2_7 = 270000,
+ RATE_3_2 = 324000,
+ RATE_4_3 = 432000,
+ RATE_5_4 = 540000,
+ RATE_8_1 = 810000,
+};
+
+struct phy_pll_reg {
+ u16 val[7];
+ u32 addr;
+};
+
+static const struct phy_pll_reg phy_pll_27m_cfg[] = {
+ /* 1.62 2.16 2.43 2.7 3.24 4.32 5.4 register address */
+ {{ 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E }, CMN_PLL0_VCOCAL_INIT_TMR },
+ {{ 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B, 0x001B }, CMN_PLL0_VCOCAL_ITER_TMR },
+ {{ 0x30B9, 0x3087, 0x3096, 0x30B4, 0x30B9, 0x3087, 0x30B4 }, CMN_PLL0_VCOCAL_START },
+ {{ 0x0077, 0x009F, 0x00B3, 0x00C7, 0x0077, 0x009F, 0x00C7 }, CMN_PLL0_INTDIV },
+ {{ 0xF9DA, 0xF7CD, 0xF6C7, 0xF5C1, 0xF9DA, 0xF7CD, 0xF5C1 }, CMN_PLL0_FRACDIV },
+ {{ 0x001E, 0x0028, 0x002D, 0x0032, 0x001E, 0x0028, 0x0032 }, CMN_PLL0_HIGH_THR },
+ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_PLL0_DSM_DIAG },
+ {{ 0x0000, 0x1000, 0x1000, 0x1000, 0x0000, 0x1000, 0x1000 }, CMN_PLLSM0_USER_DEF_CTRL },
+ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_OVRD },
+ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBH_OVRD },
+ {{ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }, CMN_DIAG_PLL0_FBL_OVRD },
+ {{ 0x0006, 0x0007, 0x0007, 0x0007, 0x0006, 0x0007, 0x0007 }, CMN_DIAG_PLL0_V2I_TUNE },
+ {{ 0x0043, 0x0043, 0x0043, 0x0042, 0x0043, 0x0043, 0x0042 }, CMN_DIAG_PLL0_CP_TUNE },
+ {{ 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008, 0x0008 }, CMN_DIAG_PLL0_LF_PROG },
+ {{ 0x0100, 0x0001, 0x0001, 0x0001, 0x0100, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE1 },
+ {{ 0x0007, 0x0001, 0x0001, 0x0001, 0x0007, 0x0001, 0x0001 }, CMN_DIAG_PLL0_PTATIS_TUNE2 },
+ {{ 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020 }, CMN_DIAG_PLL0_TEST_MODE},
+ {{ 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016, 0x0016 }, CMN_PSM_CLK_CTRL }
+};
+
+static int link_rate_index(u32 rate)
+{
+ switch (rate) {
+ case RATE_1_6:
+ return 0;
+ case RATE_2_1:
+ return 1;
+ case RATE_2_4:
+ return 2;
+ case RATE_2_7:
+ return 3;
+ case RATE_3_2:
+ return 4;
+ case RATE_4_3:
+ return 5;
+ case RATE_5_4:
+ return 6;
+ default:
+ return -1;
+ }
+}
+
+static void dp_aux_cfg(struct cdns_mhdp_device *mhdp)
+{
+ /* Power up Aux */
+ cdns_phy_reg_write(mhdp, TXDA_CYA_AUXDA_CYA, 1);
+
+ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_1, 0x3);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_DIG_CTRL_REG_2, 36);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0100);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x0300);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_3, 0x0000);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2008);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0x2018);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA018);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030C);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_5, 0x0000);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_4, 0x1001);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA098);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_1, 0xA198);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030d);
+ ndelay(150);
+ cdns_phy_reg_write(mhdp, TX_ANA_CTRL_REG_2, 0x030f);
+}
+
+/* PMA common configuration for 27MHz */
+static void dp_phy_pma_cmn_cfg_27mhz(struct cdns_mhdp_device *mhdp)
+{
+ u32 num_lanes = mhdp->dp.num_lanes;
+ u16 val;
+ int k;
+
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ val &= 0xFFF7;
+ val |= 0x0008;
+ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
+
+ /* Startup state machine registers */
+ cdns_phy_reg_write(mhdp, CMN_SSM_BIAS_TMR, 0x0087);
+ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLEN_TMR, 0x001B);
+ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLPRE_TMR, 0x0036);
+ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLVREF_TMR, 0x001B);
+ cdns_phy_reg_write(mhdp, CMN_PLLSM0_PLLLOCK_TMR, 0x006C);
+
+ /* Current calibration registers */
+ cdns_phy_reg_write(mhdp, CMN_ICAL_INIT_TMR, 0x0044);
+ cdns_phy_reg_write(mhdp, CMN_ICAL_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_ICAL_ADJ_ITER_TMR, 0x0006);
+
+ /* Resistor calibration registers */
+ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_TXPUCAL_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_TXPU_ADJ_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_TXPDCAL_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_TXPD_ADJ_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_RXCAL_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_RXCAL_ITER_TMR, 0x0006);
+ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_INIT_TMR, 0x0022);
+ cdns_phy_reg_write(mhdp, CMN_RX_ADJ_ITER_TMR, 0x0006);
+
+ for (k = 0; k < num_lanes; k = k + 1) {
+ /* Power state machine registers */
+ cdns_phy_reg_write(mhdp, XCVR_PSM_CAL_TMR | (k << 9), 0x016D);
+ cdns_phy_reg_write(mhdp, XCVR_PSM_A0IN_TMR | (k << 9), 0x016D);
+ /* Transceiver control and diagnostic registers */
+ cdns_phy_reg_write(mhdp, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x00A2);
+ cdns_phy_reg_write(mhdp, TX_DIAG_BGREF_PREDRV_DELAY | (k << 9), 0x0097);
+ /* Transmitter receiver detect registers */
+ cdns_phy_reg_write(mhdp, TX_RCVDET_EN_TMR | (k << 9), 0x0A8C);
+ cdns_phy_reg_write(mhdp, TX_RCVDET_ST_TMR | (k << 9), 0x0036);
+ }
+}
+
+static void dp_phy_pma_cmn_pll0_27mhz(struct cdns_mhdp_device *mhdp)
+{
+ u32 num_lanes = mhdp->dp.num_lanes;
+ u32 link_rate = mhdp->dp.rate;
+ u16 val;
+ int index, i, k;
+
+ /*
+ * PLL reference clock source select
+ * for single ended reference clock val |= 0x0030;
+ * for differential clock val |= 0x0000;
+ */
+ val = cdns_phy_reg_read(mhdp, PHY_PMA_CMN_CTRL1);
+ val &= 0xFF8F;
+ cdns_phy_reg_write(mhdp, PHY_PMA_CMN_CTRL1, val);
+
+ /* for differential clock on the refclk_p and refclk_m off chip pins:
+ * CMN_DIAG_ACYA[8]=1'b1
+ */
+ cdns_phy_reg_write(mhdp, CMN_DIAG_ACYA, 0x0100);
+
+ /* DP PLL data rate 0/1 clock divider value */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val &= 0x00FF;
+ if (link_rate <= RATE_2_7)
+ val |= 0x2400;
+ else
+ val |= 0x1200;
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+
+ /* High speed clock 0/1 div */
+ val = cdns_phy_reg_read(mhdp, CMN_DIAG_HSCLK_SEL);
+ val &= 0xFFCC;
+ if (link_rate <= RATE_2_7)
+ val |= 0x0011;
+ cdns_phy_reg_write(mhdp, CMN_DIAG_HSCLK_SEL, val);
+
+ for (k = 0; k < num_lanes; k++) {
+ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)));
+ val = val & 0xCFFF;
+ if (link_rate <= RATE_2_7)
+ val |= 0x1000;
+ cdns_phy_reg_write(mhdp, (XCVR_DIAG_HSCLK_SEL | (k << 9)), val);
+ }
+
+ /* DP PHY PLL 27MHz configuration */
+ index = link_rate_index(link_rate);
+ for (i = 0; i < ARRAY_SIZE(phy_pll_27m_cfg); i++)
+ cdns_phy_reg_write(mhdp, phy_pll_27m_cfg[i].addr, phy_pll_27m_cfg[i].val[index]);
+
+ /* Transceiver control and diagnostic registers */
+ for (k = 0; k < num_lanes; k++) {
+ val = cdns_phy_reg_read(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)));
+ val = val & 0x8FFF;
+ if (link_rate <= RATE_2_7)
+ val |= 0x2000;
+ else
+ val |= 0x1000;
+ cdns_phy_reg_write(mhdp, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), val);
+ }
+
+ for (k = 0; k < num_lanes; k = k + 1) {
+ /* Power state machine registers */
+ cdns_phy_reg_write(mhdp, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC);
+ cdns_phy_reg_write(mhdp, (TX_PSC_A0 | (k << 9)), 0x6799);
+ cdns_phy_reg_write(mhdp, (TX_PSC_A1 | (k << 9)), 0x6798);
+ cdns_phy_reg_write(mhdp, (TX_PSC_A2 | (k << 9)), 0x0098);
+ cdns_phy_reg_write(mhdp, (TX_PSC_A3 | (k << 9)), 0x0098);
+ /* Receiver calibration power state definition register */
+ val = cdns_phy_reg_read(mhdp, RX_PSC_CAL | (k << 9));
+ val &= 0xFFBB;
+ cdns_phy_reg_write(mhdp, (RX_PSC_CAL | (k << 9)), val);
+ val = cdns_phy_reg_read(mhdp, RX_PSC_A0 | (k << 9));
+ val &= 0xFFBB;
+ cdns_phy_reg_write(mhdp, (RX_PSC_A0 | (k << 9)), val);
+ }
+}
+
+static void dp_phy_power_down(struct cdns_mhdp_device *mhdp)
+{
+ u16 val;
+ int i;
+
+ if (!mhdp->power_up)
+ return;
+
+ /* Place the PHY lanes in the A3 power state. */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x8);
+ /* Wait for Power State A3 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 7))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A3 Ack failed\n");
+ return;
+ }
+
+ /* Disable HDP PLL’s data rate and full rate clocks out of PMA. */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val &= ~(1 << 2);
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+ /* Wait for PLL clock gate ACK */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ if (!(val & (1 << 3)))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait PLL clock gate Ack failed\n");
+ return;
+ }
+
+ /* Disable HDP PLL’s for high speed clocks */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val &= ~(1 << 0);
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+ /* Wait for PLL disable ACK */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ if (!(val & (1 << 1)))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait PLL disable Ack failed\n");
+ return;
+ }
+}
+
+static int dp_phy_power_up(struct cdns_mhdp_device *mhdp)
+{
+ u32 val, i;
+
+ /* Enable HDP PLL’s for high speed clocks */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val |= (1 << 0);
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+ /* Wait for PLL ready ACK */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ if (val & (1 << 1))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait PLL Ack failed\n");
+ return -1;
+ }
+
+ /* Enable HDP PLL’s data rate and full rate clocks out of PMA. */
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ val |= (1 << 2);
+ cdns_phy_reg_write(mhdp, PHY_HDP_CLK_CTL, val);
+ /* Wait for PLL clock enable ACK */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_CLK_CTL);
+ if (val & (1 << 3))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait PLL clock enable ACk failed\n");
+ return -1;
+ }
+
+ /* Configure PHY in A2 Mode */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0004);
+ /* Wait for Power State A2 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 6))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A2 Ack failed\n");
+ return -1;
+ }
+
+ /* Configure PHY in A0 mode (PHY must be in the A0 power
+ * state in order to transmit data)
+ */
+ cdns_phy_reg_write(mhdp, PHY_HDP_MODE_CTRL, 0x0101);
+
+ /* Wait for Power State A0 Ack */
+ for (i = 0; i < 10; i++) {
+ val = cdns_phy_reg_read(mhdp, PHY_HDP_MODE_CTRL);
+ if (val & (1 << 4))
+ break;
+ msleep(20);
+ }
+ if (i == 10) {
+ dev_err(mhdp->dev, "Wait A0 Ack failed\n");
+ return -1;
+ }
+
+ mhdp->power_up = true;
+
+ return 0;
+}
+
+int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *mhdp)
+{
+ int ret;
+
+ /* Disable phy clock if PHY in power up state */
+ dp_phy_power_down(mhdp);
+
+ dp_phy_pma_cmn_cfg_27mhz(mhdp);
+
+ dp_phy_pma_cmn_pll0_27mhz(mhdp);
+
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_0, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_1, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_2, 1);
+ cdns_phy_reg_write(mhdp, TX_DIAG_ACYA_3, 1);
+
+ /* PHY power up */
+ ret = dp_phy_power_up(mhdp);
+ if (ret < 0)
+ return ret;
+
+ dp_aux_cfg(mhdp);
+
+ return ret;
+}
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
new file mode 100644
index 000000000000..2dec2e051be6
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-imxdrv.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * copyright (c) 2019-2020 nxp semiconductor, inc.
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/component.h>
+#include <drm/drm_of.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder_slave.h>
+
+#include "cdns-mhdp-phy.h"
+#include "../imx-drm.h"
+
+struct imx_mhdp_device {
+ struct cdns_mhdp_device mhdp;
+ struct drm_encoder encoder;
+};
+
+static const struct drm_encoder_funcs cdns_mhdp_imx_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+static struct cdns_plat_data imx8mq_dp_drv_data = {
+ .plat_name = "imx8mq-dp",
+ .bind = cdns_dp_bind,
+ .unbind = cdns_dp_unbind,
+ .phy_set = cdns_dp_phy_set_imx8mq,
+ .lane_mapping = 0xc6,
+};
+
+static const struct of_device_id cdns_mhdp_imx_dt_ids[] = {
+ { .compatible = "nxp,imx8mq-cdns-dp",
+ .data = &imx8mq_dp_drv_data
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cdns_mhdp_imx_dt_ids);
+
+static int cdns_mhdp_imx_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct cdns_plat_data *plat_data;
+ const struct of_device_id *match;
+ struct drm_device *drm = data;
+ struct drm_encoder *encoder;
+ struct imx_mhdp_device *imx_mhdp;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ imx_mhdp = devm_kzalloc(&pdev->dev, sizeof(*imx_mhdp), GFP_KERNEL);
+ if (!imx_mhdp)
+ return -ENOMEM;
+
+ match = of_match_node(cdns_mhdp_imx_dt_ids, pdev->dev.of_node);
+ plat_data = match->data;
+ encoder = &imx_mhdp->encoder;
+
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ /*
+ * If we failed to find the CRTC(s) which this encoder is
+ * supposed to be connected to, it's because the CRTC has
+ * not been registered yet. Defer probing, and hope that
+ * the required CRTC is added later.
+ */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ drm_encoder_init(drm, encoder, &cdns_mhdp_imx_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
+
+
+ imx_mhdp->mhdp.plat_data = plat_data;
+ imx_mhdp->mhdp.dev = dev;
+ ret = plat_data->bind(pdev, encoder, &imx_mhdp->mhdp);
+ /*
+ * If cdns_mhdp_bind() fails we'll never call cdns_mhdp_unbind(),
+ * which would have called the encoder cleanup. Do it manually.
+ */
+ if (ret < 0)
+ drm_encoder_cleanup(encoder);
+
+ return ret;
+}
+
+static void cdns_mhdp_imx_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct imx_mhdp_device *imx_mhdp = dev_get_drvdata(dev);
+
+ imx_mhdp->mhdp.plat_data->unbind(dev);
+}
+
+static const struct component_ops cdns_mhdp_imx_ops = {
+ .bind = cdns_mhdp_imx_bind,
+ .unbind = cdns_mhdp_imx_unbind,
+};
+
+static int cdns_mhdp_imx_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &cdns_mhdp_imx_ops);
+}
+
+static int cdns_mhdp_imx_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &cdns_mhdp_imx_ops);
+
+ return 0;
+}
+
+static struct platform_driver cdns_mhdp_imx_platform_driver = {
+ .probe = cdns_mhdp_imx_probe,
+ .remove = cdns_mhdp_imx_remove,
+ .driver = {
+ .name = "cdns-mhdp-imx",
+ .of_match_table = cdns_mhdp_imx_dt_ids,
+ },
+};
+
+module_platform_driver(cdns_mhdp_imx_platform_driver);
+
+MODULE_AUTHOR("Sandor YU <sandor.yu@nxp.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cdnsmhdp-imx");
diff --git a/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
new file mode 100644
index 000000000000..79b1907726db
--- /dev/null
+++ b/drivers/gpu/drm/imx/mhdp/cdns-mhdp-phy.h
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2019-2020 NXP Semiconductor, Inc.
+ *
+ */
+#ifndef _CDNS_MHDP_PHY_H
+#define _CDNS_MHDP_PHY_H
+
+#include <drm/bridge/cdns-mhdp.h>
+
+#define CMN_SSM_BIAS_TMR 0x0022
+#define CMN_PLLSM0_PLLEN_TMR 0x0029
+#define CMN_PLLSM0_PLLPRE_TMR 0x002A
+#define CMN_PLLSM0_PLLVREF_TMR 0x002B
+#define CMN_PLLSM0_PLLLOCK_TMR 0x002C
+#define CMN_PLLSM0_USER_DEF_CTRL 0x002F
+#define CMN_PSM_CLK_CTRL 0x0061
+#define CMN_CDIAG_REFCLK_CTRL 0x0062
+#define CMN_PLL0_VCOCAL_START 0x0081
+#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084
+#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085
+#define CMN_PLL0_INTDIV 0x0094
+#define CMN_PLL0_FRACDIV 0x0095
+#define CMN_PLL0_HIGH_THR 0x0096
+#define CMN_PLL0_DSM_DIAG 0x0097
+#define CMN_PLL0_SS_CTRL1 0x0098
+#define CMN_PLL0_SS_CTRL2 0x0099
+#define CMN_ICAL_INIT_TMR 0x00C4
+#define CMN_ICAL_ITER_TMR 0x00C5
+#define CMN_RXCAL_INIT_TMR 0x00D4
+#define CMN_RXCAL_ITER_TMR 0x00D5
+#define CMN_TXPUCAL_CTRL 0x00E0
+#define CMN_TXPUCAL_INIT_TMR 0x00E4
+#define CMN_TXPUCAL_ITER_TMR 0x00E5
+#define CMN_TXPDCAL_CTRL 0x00F0
+#define CMN_TXPDCAL_INIT_TMR 0x00F4
+#define CMN_TXPDCAL_ITER_TMR 0x00F5
+#define CMN_ICAL_ADJ_INIT_TMR 0x0102
+#define CMN_ICAL_ADJ_ITER_TMR 0x0103
+#define CMN_RX_ADJ_INIT_TMR 0x0106
+#define CMN_RX_ADJ_ITER_TMR 0x0107
+#define CMN_TXPU_ADJ_CTRL 0x0108
+#define CMN_TXPU_ADJ_INIT_TMR 0x010A
+#define CMN_TXPU_ADJ_ITER_TMR 0x010B
+#define CMN_TXPD_ADJ_CTRL 0x010c
+#define CMN_TXPD_ADJ_INIT_TMR 0x010E
+#define CMN_TXPD_ADJ_ITER_TMR 0x010F
+#define CMN_DIAG_PLL0_FBH_OVRD 0x01C0
+#define CMN_DIAG_PLL0_FBL_OVRD 0x01C1
+#define CMN_DIAG_PLL0_OVRD 0x01C2
+#define CMN_DIAG_PLL0_TEST_MODE 0x01C4
+#define CMN_DIAG_PLL0_V2I_TUNE 0x01C5
+#define CMN_DIAG_PLL0_CP_TUNE 0x01C6
+#define CMN_DIAG_PLL0_LF_PROG 0x01C7
+#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01C8
+#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01C9
+#define CMN_DIAG_PLL0_INCLK_CTRL 0x01CA
+#define CMN_DIAG_PLL0_PXL_DIVH 0x01CB
+#define CMN_DIAG_PLL0_PXL_DIVL 0x01CC
+#define CMN_DIAG_HSCLK_SEL 0x01E0
+#define CMN_DIAG_PER_CAL_ADJ 0x01EC
+#define CMN_DIAG_CAL_CTRL 0x01ED
+#define CMN_DIAG_ACYA 0x01FF
+#define XCVR_PSM_RCTRL 0x4001
+#define XCVR_PSM_CAL_TMR 0x4002
+#define XCVR_PSM_A0IN_TMR 0x4003
+#define TX_TXCC_CAL_SCLR_MULT_0 0x4047
+#define TX_TXCC_CPOST_MULT_00_0 0x404C
+#define TX_TXCC_MGNFS_MULT_000_0 0x4050
+#define XCVR_DIAG_PLLDRC_CTRL 0x40E0
+#define XCVR_DIAG_PLLDRC_CTRL 0x40E0
+#define XCVR_DIAG_HSCLK_SEL 0x40E1
+#define XCVR_DIAG_BIDI_CTRL 0x40E8
+#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40F2
+#define XCVR_DIAG_LANE_FCM_EN_MGN 0x40F2
+#define TX_PSC_A0 0x4100
+#define TX_PSC_A1 0x4101
+#define TX_PSC_A2 0x4102
+#define TX_PSC_A3 0x4103
+#define TX_RCVDET_CTRL 0x4120
+#define TX_RCVDET_EN_TMR 0x4122
+#define TX_RCVDET_EN_TMR 0x4122
+#define TX_RCVDET_ST_TMR 0x4123
+#define TX_RCVDET_ST_TMR 0x4123
+#define TX_BIST_CTRL 0x4140
+#define TX_BIST_UDDWR 0x4141
+#define TX_DIAG_TX_CTRL 0x41E0
+#define TX_DIAG_TX_DRV 0x41E1
+#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7
+#define TX_DIAG_BGREF_PREDRV_DELAY 0x41E7
+#define XCVR_PSM_RCTRL_1 0x4201
+#define TX_TXCC_CAL_SCLR_MULT_1 0x4247
+#define TX_TXCC_CPOST_MULT_00_1 0x424C
+#define TX_TXCC_MGNFS_MULT_000_1 0x4250
+#define XCVR_DIAG_PLLDRC_CTRL_1 0x42E0
+#define XCVR_DIAG_HSCLK_SEL_1 0x42E1
+#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_1 0x42F2
+#define TX_RCVDET_EN_TMR_1 0x4322
+#define TX_RCVDET_ST_TMR_1 0x4323
+#define TX_DIAG_ACYA_0 0x41FF
+#define TX_DIAG_ACYA_1 0x43FF
+#define TX_DIAG_ACYA_2 0x45FF
+#define TX_DIAG_ACYA_3 0x47FF
+#define TX_ANA_CTRL_REG_1 0x5020
+#define TX_ANA_CTRL_REG_2 0x5021
+#define TXDA_COEFF_CALC 0x5022
+#define TX_DIG_CTRL_REG_1 0x5023
+#define TX_DIG_CTRL_REG_2 0x5024
+#define TXDA_CYA_AUXDA_CYA 0x5025
+#define TX_ANA_CTRL_REG_3 0x5026
+#define TX_ANA_CTRL_REG_4 0x5027
+#define TX_ANA_CTRL_REG_5 0x5029
+#define RX_PSC_A0 0x8000
+#define RX_PSC_CAL 0x8006
+#define PMA_LANE_CFG 0xC000
+#define PIPE_CMN_CTRL1 0xC001
+#define PIPE_CMN_CTRL2 0xC002
+#define PIPE_COM_LOCK_CFG1 0xC003
+#define PIPE_COM_LOCK_CFG2 0xC004
+#define PIPE_RCV_DET_INH 0xC005
+#define PHY_HDP_MODE_CTRL 0xC008
+#define PHY_HDP_CLK_CTL 0xC009
+#define STS 0xC00F
+#define PHY_ISO_CMN_CTRL 0xC010
+#define PHY_ISO_CMN_CTRL 0xC010
+#define PHY_HDP_TX_CTL_L0 0xC408
+#define PHY_DP_TX_CTL 0xC408
+#define PHY_HDP_TX_CTL_L1 0xC448
+#define PHY_HDP_TX_CTL_L2 0xC488
+#define PHY_HDP_TX_CTL_L3 0xC4C8
+#define PHY_PMA_CMN_CTRL1 0xC800
+#define PMA_CMN_CTRL1 0xC800
+#define PHY_PMA_ISO_CMN_CTRL 0xC810
+#define PHY_PMA_ISO_PLL_CTRL1 0xC812
+#define PHY_PMA_ISOLATION_CTRL 0xC81F
+#define PHY_ISOLATION_CTRL 0xC81F
+#define PHY_PMA_ISO_XCVR_CTRL 0xCC11
+#define PHY_PMA_ISO_LINK_MODE 0xCC12
+#define PHY_PMA_ISO_PWRST_CTRL 0xCC13
+#define PHY_PMA_ISO_TX_DATA_LO 0xCC14
+#define PHY_PMA_ISO_TX_DATA_HI 0xCC15
+#define PHY_PMA_ISO_RX_DATA_LO 0xCC16
+#define PHY_PMA_ISO_RX_DATA_HI 0xCC17
+
+int cdns_dp_phy_set_imx8mq(struct cdns_mhdp_device *hdp);
+#endif /* _CDNS_MHDP_PHY_H */
--
2.17.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 1/7] drm/rockchip: prepare common code for cdns and rk dpi/dp driver
From: sandor.yu @ 2020-06-01 6:17 UTC (permalink / raw)
To: a.hajda, narmstrong, Laurent.pinchart, jonas, jernej.skrabec,
heiko, hjc, Sandor.yu, dkos, dri-devel
Cc: linux-rockchip, linux-kernel, linux-arm-kernel, linux-imx
In-Reply-To: <cover.1590982881.git.Sandor.yu@nxp.com>
From: Sandor Yu <Sandor.yu@nxp.com>
- Extracted common fields from cdn_dp_device to a new cdns_mhdp_device
structure which will be used by two separate drivers later on.
- Moved some datatypes (audio_format, audio_info, vic_pxl_encoding_format,
video_info) from cdn-dp-core.c to cdn-dp-reg.h.
- Changed prefixes from cdn_dp to cdns_mhdp
cdn -> cdns to match the other Cadence's drivers
dp -> mhdp to distinguish it from a "just a DP" as the IP underneath
this registers map can be a HDMI (which is internally different,
but the interface for commands, events is pretty much the same).
- Modified cdn-dp-core.c to use the new driver structure and new function
names.
- writel and readl are replaced by cdns_mhdp_bus_write and
cdns_mhdp_bus_read.
Signed-off-by: Damian Kos <dkos@cadence.com>
Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
---
drivers/gpu/drm/rockchip/cdn-dp-core.c | 240 ++++++------
drivers/gpu/drm/rockchip/cdn-dp-core.h | 44 +--
drivers/gpu/drm/rockchip/cdn-dp-reg.c | 488 +++++++++++++------------
drivers/gpu/drm/rockchip/cdn-dp-reg.h | 133 +++++--
4 files changed, 483 insertions(+), 422 deletions(-)
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c
index eed594bd38d3..b6aa21779608 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -26,7 +26,7 @@
#include "rockchip_drm_vop.h"
#define connector_to_dp(c) \
- container_of(c, struct cdn_dp_device, connector)
+ container_of(c, struct cdn_dp_device, mhdp.connector.base)
#define encoder_to_dp(c) \
container_of(c, struct cdn_dp_device, encoder)
@@ -61,17 +61,18 @@ MODULE_DEVICE_TABLE(of, cdn_dp_dt_ids);
static int cdn_dp_grf_write(struct cdn_dp_device *dp,
unsigned int reg, unsigned int val)
{
+ struct device *dev = dp->mhdp.dev;
int ret;
ret = clk_prepare_enable(dp->grf_clk);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to prepare_enable grf clock\n");
+ DRM_DEV_ERROR(dev, "Failed to prepare_enable grf clock\n");
return ret;
}
ret = regmap_write(dp->grf, reg, val);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Could not write to GRF: %d\n", ret);
+ DRM_DEV_ERROR(dev, "Could not write to GRF: %d\n", ret);
return ret;
}
@@ -82,24 +83,25 @@ static int cdn_dp_grf_write(struct cdn_dp_device *dp,
static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
{
+ struct device *dev = dp->mhdp.dev;
int ret;
unsigned long rate;
ret = clk_prepare_enable(dp->pclk);
if (ret < 0) {
- DRM_DEV_ERROR(dp->dev, "cannot enable dp pclk %d\n", ret);
+ DRM_DEV_ERROR(dev, "cannot enable dp pclk %d\n", ret);
goto err_pclk;
}
ret = clk_prepare_enable(dp->core_clk);
if (ret < 0) {
- DRM_DEV_ERROR(dp->dev, "cannot enable core_clk %d\n", ret);
+ DRM_DEV_ERROR(dev, "cannot enable core_clk %d\n", ret);
goto err_core_clk;
}
- ret = pm_runtime_get_sync(dp->dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- DRM_DEV_ERROR(dp->dev, "cannot get pm runtime %d\n", ret);
+ DRM_DEV_ERROR(dev, "cannot get pm runtime %d\n", ret);
goto err_pm_runtime_get;
}
@@ -112,18 +114,18 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
rate = clk_get_rate(dp->core_clk);
if (!rate) {
- DRM_DEV_ERROR(dp->dev, "get clk rate failed\n");
+ DRM_DEV_ERROR(dev, "get clk rate failed\n");
ret = -EINVAL;
goto err_set_rate;
}
- cdn_dp_set_fw_clk(dp, rate);
- cdn_dp_clock_reset(dp);
+ cdns_mhdp_set_fw_clk(&dp->mhdp, rate);
+ cdns_mhdp_clock_reset(&dp->mhdp);
return 0;
err_set_rate:
- pm_runtime_put(dp->dev);
+ pm_runtime_put(dev);
err_pm_runtime_get:
clk_disable_unprepare(dp->core_clk);
err_core_clk:
@@ -134,7 +136,7 @@ static int cdn_dp_clk_enable(struct cdn_dp_device *dp)
static void cdn_dp_clk_disable(struct cdn_dp_device *dp)
{
- pm_runtime_put_sync(dp->dev);
+ pm_runtime_put_sync(dp->mhdp.dev);
clk_disable_unprepare(dp->pclk);
clk_disable_unprepare(dp->core_clk);
}
@@ -167,7 +169,7 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count)
u8 value;
*sink_count = 0;
- ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
+ ret = drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_SINK_COUNT, &value, 1);
if (ret)
return ret;
@@ -191,12 +193,13 @@ static struct cdn_dp_port *cdn_dp_connected_port(struct cdn_dp_device *dp)
static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
{
+ struct device *dev = dp->mhdp.dev;
unsigned long timeout = jiffies + msecs_to_jiffies(CDN_DPCD_TIMEOUT_MS);
struct cdn_dp_port *port;
u8 sink_count = 0;
if (dp->active_port < 0 || dp->active_port >= dp->ports) {
- DRM_DEV_ERROR(dp->dev, "active_port is wrong!\n");
+ DRM_DEV_ERROR(dev, "active_port is wrong!\n");
return false;
}
@@ -218,7 +221,7 @@ static bool cdn_dp_check_sink_connection(struct cdn_dp_device *dp)
usleep_range(5000, 10000);
}
- DRM_DEV_ERROR(dp->dev, "Get sink capability timed out\n");
+ DRM_DEV_ERROR(dev, "Get sink capability timed out\n");
return false;
}
@@ -260,7 +263,8 @@ static int cdn_dp_connector_get_modes(struct drm_connector *connector)
mutex_lock(&dp->lock);
edid = dp->edid;
if (edid) {
- DRM_DEV_DEBUG_KMS(dp->dev, "got edid: width[%d] x height[%d]\n",
+ DRM_DEV_DEBUG_KMS(dp->mhdp.dev,
+ "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
dp->sink_has_audio = drm_detect_monitor_audio(edid);
@@ -278,7 +282,8 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct cdn_dp_device *dp = connector_to_dp(connector);
- struct drm_display_info *display_info = &dp->connector.display_info;
+ struct drm_display_info *display_info =
+ &dp->mhdp.connector.base.display_info;
u32 requested, actual, rate, sink_max, source_max = 0;
u8 lanes, bpc;
@@ -301,11 +306,11 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
requested = mode->clock * bpc * 3 / 1000;
source_max = dp->lanes;
- sink_max = drm_dp_max_lane_count(dp->dpcd);
+ sink_max = drm_dp_max_lane_count(dp->mhdp.dp.dpcd);
lanes = min(source_max, sink_max);
- source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
- sink_max = drm_dp_max_link_rate(dp->dpcd);
+ source_max = CDNS_DP_MAX_LINK_RATE;
+ sink_max = drm_dp_max_link_rate(dp->mhdp.dp.dpcd);
rate = min(source_max, sink_max);
actual = rate * lanes / 100;
@@ -314,7 +319,7 @@ static int cdn_dp_connector_mode_valid(struct drm_connector *connector,
actual = actual * 8 / 10;
if (requested > actual) {
- DRM_DEV_DEBUG_KMS(dp->dev,
+ DRM_DEV_DEBUG_KMS(dp->mhdp.dev,
"requested=%d, actual=%d, clock=%d\n",
requested, actual, mode->clock);
return MODE_CLOCK_HIGH;
@@ -334,59 +339,62 @@ static int cdn_dp_firmware_init(struct cdn_dp_device *dp)
const u32 *iram_data, *dram_data;
const struct firmware *fw = dp->fw;
const struct cdn_firmware_header *hdr;
+ struct device *dev = dp->mhdp.dev;
hdr = (struct cdn_firmware_header *)fw->data;
if (fw->size != le32_to_cpu(hdr->size_bytes)) {
- DRM_DEV_ERROR(dp->dev, "firmware is invalid\n");
+ DRM_DEV_ERROR(dev, "firmware is invalid\n");
return -EINVAL;
}
iram_data = (const u32 *)(fw->data + hdr->header_size);
dram_data = (const u32 *)(fw->data + hdr->header_size + hdr->iram_size);
- ret = cdn_dp_load_firmware(dp, iram_data, hdr->iram_size,
- dram_data, hdr->dram_size);
+ ret = cdns_mhdp_load_firmware(&dp->mhdp, iram_data, hdr->iram_size,
+ dram_data, hdr->dram_size);
if (ret)
return ret;
- ret = cdn_dp_set_firmware_active(dp, true);
+ ret = cdns_mhdp_set_firmware_active(&dp->mhdp, true);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "active ucpu failed: %d\n", ret);
+ DRM_DEV_ERROR(dev, "active ucpu failed: %d\n", ret);
return ret;
}
- return cdn_dp_event_config(dp);
+ return cdns_mhdp_event_config(&dp->mhdp);
}
static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp)
{
+ struct cdns_mhdp_device *mhdp = &dp->mhdp;
int ret;
if (!cdn_dp_check_sink_connection(dp))
return -ENODEV;
- ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
+ ret = drm_dp_dpcd_read(&mhdp->dp.aux, DP_DPCD_REV, mhdp->dp.dpcd,
DP_RECEIVER_CAP_SIZE);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "Failed to get caps %d\n", ret);
return ret;
}
kfree(dp->edid);
- dp->edid = drm_do_get_edid(&dp->connector,
- cdn_dp_get_edid_block, dp);
+ dp->edid = drm_do_get_edid(&mhdp->connector.base,
+ cdns_mhdp_get_edid_block, mhdp);
return 0;
}
static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
{
+ struct device *dev = dp->mhdp.dev;
union extcon_property_value property;
int ret;
if (!port->phy_enabled) {
ret = phy_power_on(port->phy);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "phy power on failed: %d\n",
+ DRM_DEV_ERROR(dev, "phy power on failed: %d\n",
ret);
goto err_phy;
}
@@ -396,28 +404,28 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
ret = cdn_dp_grf_write(dp, GRF_SOC_CON26,
DPTX_HPD_SEL_MASK | DPTX_HPD_SEL);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to write HPD_SEL %d\n", ret);
+ DRM_DEV_ERROR(dev, "Failed to write HPD_SEL %d\n", ret);
goto err_power_on;
}
- ret = cdn_dp_get_hpd_status(dp);
+ ret = cdns_mhdp_read_hpd(&dp->mhdp);
if (ret <= 0) {
if (!ret)
- DRM_DEV_ERROR(dp->dev, "hpd does not exist\n");
+ DRM_DEV_ERROR(dev, "hpd does not exist\n");
goto err_power_on;
}
ret = extcon_get_property(port->extcon, EXTCON_DISP_DP,
EXTCON_PROP_USB_TYPEC_POLARITY, &property);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "get property failed\n");
+ DRM_DEV_ERROR(dev, "get property failed\n");
goto err_power_on;
}
port->lanes = cdn_dp_get_port_lanes(port);
- ret = cdn_dp_set_host_cap(dp, port->lanes, property.intval);
+ ret = cdns_mhdp_set_host_cap(&dp->mhdp, property.intval);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "set host capabilities failed: %d\n",
+ DRM_DEV_ERROR(dev, "set host capabilities failed: %d\n",
ret);
goto err_power_on;
}
@@ -427,7 +435,7 @@ static int cdn_dp_enable_phy(struct cdn_dp_device *dp, struct cdn_dp_port *port)
err_power_on:
if (phy_power_off(port->phy))
- DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret);
+ DRM_DEV_ERROR(dev, "phy power off failed: %d", ret);
else
port->phy_enabled = false;
@@ -445,7 +453,8 @@ static int cdn_dp_disable_phy(struct cdn_dp_device *dp,
if (port->phy_enabled) {
ret = phy_power_off(port->phy);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "phy power off failed: %d", ret);
+ DRM_DEV_ERROR(dp->mhdp.dev,
+ "phy power off failed: %d", ret);
return ret;
}
}
@@ -469,16 +478,16 @@ static int cdn_dp_disable(struct cdn_dp_device *dp)
ret = cdn_dp_grf_write(dp, GRF_SOC_CON26,
DPTX_HPD_SEL_MASK | DPTX_HPD_DEL);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to clear hpd sel %d\n",
+ DRM_DEV_ERROR(dp->mhdp.dev, "Failed to clear hpd sel %d\n",
ret);
return ret;
}
- cdn_dp_set_firmware_active(dp, false);
+ cdns_mhdp_set_firmware_active(&dp->mhdp, false);
cdn_dp_clk_disable(dp);
dp->active = false;
- dp->max_lanes = 0;
- dp->max_rate = 0;
+ dp->mhdp.dp.rate = 0;
+ dp->mhdp.dp.num_lanes = 0;
if (!dp->connected) {
kfree(dp->edid);
dp->edid = NULL;
@@ -491,11 +500,11 @@ static int cdn_dp_enable(struct cdn_dp_device *dp)
{
int ret, i, lanes;
struct cdn_dp_port *port;
+ struct device *dev = dp->mhdp.dev;
port = cdn_dp_connected_port(dp);
if (!port) {
- DRM_DEV_ERROR(dp->dev,
- "Can't enable without connection\n");
+ DRM_DEV_ERROR(dev, "Can't enable without connection\n");
return -ENODEV;
}
@@ -508,7 +517,7 @@ static int cdn_dp_enable(struct cdn_dp_device *dp)
ret = cdn_dp_firmware_init(dp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "firmware init failed: %d", ret);
+ DRM_DEV_ERROR(dp->mhdp.dev, "firmware init failed: %d", ret);
goto err_clk_disable;
}
@@ -542,8 +551,9 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *adjusted)
{
struct cdn_dp_device *dp = encoder_to_dp(encoder);
- struct drm_display_info *display_info = &dp->connector.display_info;
- struct video_info *video = &dp->video_info;
+ struct drm_display_info *display_info =
+ &dp->mhdp.connector.base.display_info;
+ struct video_info *video = &dp->mhdp.video_info;
switch (display_info->bpc) {
case 10:
@@ -561,20 +571,20 @@ static void cdn_dp_encoder_mode_set(struct drm_encoder *encoder,
video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
- memcpy(&dp->mode, adjusted, sizeof(*mode));
+ memcpy(&dp->mhdp.mode, adjusted, sizeof(*mode));
}
static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
{
u8 link_status[DP_LINK_STATUS_SIZE];
struct cdn_dp_port *port = cdn_dp_connected_port(dp);
- u8 sink_lanes = drm_dp_max_lane_count(dp->dpcd);
+ u8 sink_lanes = drm_dp_max_lane_count(dp->mhdp.dp.dpcd);
- if (!port || !dp->max_rate || !dp->max_lanes)
+ if (!port || !dp->mhdp.dp.rate || !dp->mhdp.dp.num_lanes)
return false;
- if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
- DP_LINK_STATUS_SIZE)) {
+ if (drm_dp_dpcd_read(&dp->mhdp.dp.aux, DP_LANE0_1_STATUS, link_status,
+ DP_LINK_STATUS_SIZE)) {
DRM_ERROR("Failed to get link status\n");
return false;
}
@@ -586,15 +596,16 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp)
static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
{
struct cdn_dp_device *dp = encoder_to_dp(encoder);
+ struct device *dev = dp->mhdp.dev;
int ret, val;
- ret = drm_of_encoder_active_endpoint_id(dp->dev->of_node, encoder);
+ ret = drm_of_encoder_active_endpoint_id(dev->of_node, encoder);
if (ret < 0) {
- DRM_DEV_ERROR(dp->dev, "Could not get vop id, %d", ret);
+ DRM_DEV_ERROR(dev, "Could not get vop id, %d", ret);
return;
}
- DRM_DEV_DEBUG_KMS(dp->dev, "vop %s output to cdn-dp\n",
+ DRM_DEV_DEBUG_KMS(dev, "vop %s output to cdn-dp\n",
(ret) ? "LIT" : "BIG");
if (ret)
val = DP_SEL_VOP_LIT | (DP_SEL_VOP_LIT << 16);
@@ -609,33 +620,33 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder)
ret = cdn_dp_enable(dp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to enable encoder %d\n",
+ DRM_DEV_ERROR(dev, "Failed to enable encoder %d\n",
ret);
goto out;
}
if (!cdn_dp_check_link_status(dp)) {
- ret = cdn_dp_train_link(dp);
+ ret = cdns_mhdp_train_link(&dp->mhdp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed link train %d\n", ret);
+ DRM_DEV_ERROR(dev, "Failed link train %d\n", ret);
goto out;
}
}
- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+ ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_IDLE);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
+ DRM_DEV_ERROR(dev, "Failed to idle video %d\n", ret);
goto out;
}
- ret = cdn_dp_config_video(dp);
+ ret = cdns_mhdp_config_video(&dp->mhdp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to config video %d\n", ret);
+ DRM_DEV_ERROR(dev, "Failed to config video %d\n", ret);
goto out;
}
- ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
+ ret = cdns_mhdp_set_video_status(&dp->mhdp, CONTROL_VIDEO_VALID);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
+ DRM_DEV_ERROR(dev, "Failed to valid video %d\n", ret);
goto out;
}
out:
@@ -651,7 +662,8 @@ static void cdn_dp_encoder_disable(struct drm_encoder *encoder)
if (dp->active) {
ret = cdn_dp_disable(dp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to disable encoder %d\n",
+ DRM_DEV_ERROR(dp->mhdp.dev,
+ "Failed to disable encoder %d\n",
ret);
}
}
@@ -695,7 +707,7 @@ static const struct drm_encoder_funcs cdn_dp_encoder_funcs = {
static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
{
- struct device *dev = dp->dev;
+ struct device *dev = dp->mhdp.dev;
struct device_node *np = dev->of_node;
struct platform_device *pdev = to_platform_device(dev);
struct resource *res;
@@ -707,10 +719,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- dp->regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(dp->regs)) {
+ dp->mhdp.regs_base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(dp->mhdp.regs_base)) {
DRM_DEV_ERROR(dev, "ioremap reg failed\n");
- return PTR_ERR(dp->regs);
+ return PTR_ERR(dp->mhdp.regs_base);
}
dp->core_clk = devm_clk_get(dev, "core-clk");
@@ -725,10 +737,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
return PTR_ERR(dp->pclk);
}
- dp->spdif_clk = devm_clk_get(dev, "spdif");
- if (IS_ERR(dp->spdif_clk)) {
+ dp->mhdp.spdif_clk = devm_clk_get(dev, "spdif");
+ if (IS_ERR(dp->mhdp.spdif_clk)) {
DRM_DEV_ERROR(dev, "cannot get spdif_clk\n");
- return PTR_ERR(dp->spdif_clk);
+ return PTR_ERR(dp->mhdp.spdif_clk);
}
dp->grf_clk = devm_clk_get(dev, "grf");
@@ -737,10 +749,10 @@ static int cdn_dp_parse_dt(struct cdn_dp_device *dp)
return PTR_ERR(dp->grf_clk);
}
- dp->spdif_rst = devm_reset_control_get(dev, "spdif");
- if (IS_ERR(dp->spdif_rst)) {
+ dp->mhdp.spdif_rst = devm_reset_control_get(dev, "spdif");
+ if (IS_ERR(dp->mhdp.spdif_rst)) {
DRM_DEV_ERROR(dev, "no spdif reset control found\n");
- return PTR_ERR(dp->spdif_rst);
+ return PTR_ERR(dp->mhdp.spdif_rst);
}
dp->dptx_rst = devm_reset_control_get(dev, "dptx");
@@ -787,7 +799,7 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data,
audio.format = AFMT_I2S;
break;
case HDMI_SPDIF:
- audio.format = AFMT_SPDIF;
+ audio.format = AFMT_SPDIF_INT;
break;
default:
DRM_DEV_ERROR(dev, "Invalid format %d\n", daifmt->fmt);
@@ -795,9 +807,9 @@ static int cdn_dp_audio_hw_params(struct device *dev, void *data,
goto out;
}
- ret = cdn_dp_audio_config(dp, &audio);
+ ret = cdns_mhdp_audio_config(&dp->mhdp, &audio);
if (!ret)
- dp->audio_info = audio;
+ dp->mhdp.audio_info = audio;
out:
mutex_unlock(&dp->lock);
@@ -813,9 +825,9 @@ static void cdn_dp_audio_shutdown(struct device *dev, void *data)
if (!dp->active)
goto out;
- ret = cdn_dp_audio_stop(dp, &dp->audio_info);
+ ret = cdns_mhdp_audio_stop(&dp->mhdp, &dp->mhdp.audio_info);
if (!ret)
- dp->audio_info.format = AFMT_UNUSED;
+ dp->mhdp.audio_info.format = AFMT_UNUSED;
out:
mutex_unlock(&dp->lock);
}
@@ -832,7 +844,7 @@ static int cdn_dp_audio_digital_mute(struct device *dev, void *data,
goto out;
}
- ret = cdn_dp_audio_mute(dp, enable);
+ ret = cdns_mhdp_audio_mute(&dp->mhdp, enable);
out:
mutex_unlock(&dp->lock);
@@ -844,7 +856,8 @@ static int cdn_dp_audio_get_eld(struct device *dev, void *data,
{
struct cdn_dp_device *dp = dev_get_drvdata(dev);
- memcpy(buf, dp->connector.eld, min(sizeof(dp->connector.eld), len));
+ memcpy(buf, dp->mhdp.connector.base.eld,
+ min(sizeof(dp->mhdp.connector.base.eld), len));
return 0;
}
@@ -866,11 +879,11 @@ static int cdn_dp_audio_codec_init(struct cdn_dp_device *dp,
.max_i2s_channels = 8,
};
- dp->audio_pdev = platform_device_register_data(
- dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
- &codec_data, sizeof(codec_data));
+ dp->mhdp.audio_pdev = platform_device_register_data(
+ dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
+ &codec_data, sizeof(codec_data));
- return PTR_ERR_OR_ZERO(dp->audio_pdev);
+ return PTR_ERR_OR_ZERO(dp->mhdp.audio_pdev);
}
static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
@@ -878,6 +891,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
int ret;
unsigned long timeout = jiffies + msecs_to_jiffies(CDN_FW_TIMEOUT_MS);
unsigned long sleep = 1000;
+ struct device *dev = dp->mhdp.dev;
WARN_ON(!mutex_is_locked(&dp->lock));
@@ -888,13 +902,13 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
mutex_unlock(&dp->lock);
while (time_before(jiffies, timeout)) {
- ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dp->dev);
+ ret = request_firmware(&dp->fw, CDN_DP_FIRMWARE, dev);
if (ret == -ENOENT) {
msleep(sleep);
sleep *= 2;
continue;
} else if (ret) {
- DRM_DEV_ERROR(dp->dev,
+ DRM_DEV_ERROR(dev,
"failed to request firmware: %d\n", ret);
goto out;
}
@@ -904,7 +918,7 @@ static int cdn_dp_request_firmware(struct cdn_dp_device *dp)
goto out;
}
- DRM_DEV_ERROR(dp->dev, "Timed out trying to load firmware\n");
+ DRM_DEV_ERROR(dev, "Timed out trying to load firmware\n");
ret = -ETIMEDOUT;
out:
mutex_lock(&dp->lock);
@@ -915,8 +929,9 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
{
struct cdn_dp_device *dp = container_of(work, struct cdn_dp_device,
event_work);
- struct drm_connector *connector = &dp->connector;
+ struct drm_connector *connector = &dp->mhdp.connector.base;
enum drm_connector_status old_status;
+ struct device *dev = dp->mhdp.dev;
int ret;
@@ -933,44 +948,45 @@ static void cdn_dp_pd_event_work(struct work_struct *work)
/* Not connected, notify userspace to disable the block */
if (!cdn_dp_connected_port(dp)) {
- DRM_DEV_INFO(dp->dev, "Not connected. Disabling cdn\n");
+ DRM_DEV_INFO(dev, "Not connected. Disabling cdn\n");
dp->connected = false;
/* Connected but not enabled, enable the block */
} else if (!dp->active) {
- DRM_DEV_INFO(dp->dev, "Connected, not enabled. Enabling cdn\n");
+ DRM_DEV_INFO(dev, "Connected, not enabled. Enabling cdn\n");
ret = cdn_dp_enable(dp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Enable dp failed %d\n", ret);
+ DRM_DEV_ERROR(dev, "Enable dp failed %d\n", ret);
dp->connected = false;
}
/* Enabled and connected to a dongle without a sink, notify userspace */
} else if (!cdn_dp_check_sink_connection(dp)) {
- DRM_DEV_INFO(dp->dev, "Connected without sink. Assert hpd\n");
+ DRM_DEV_INFO(dev, "Connected without sink. Assert hpd\n");
dp->connected = false;
/* Enabled and connected with a sink, re-train if requested */
} else if (!cdn_dp_check_link_status(dp)) {
- unsigned int rate = dp->max_rate;
- unsigned int lanes = dp->max_lanes;
- struct drm_display_mode *mode = &dp->mode;
+ unsigned int rate = dp->mhdp.dp.rate;
+ unsigned int lanes = dp->mhdp.dp.num_lanes;
+ struct drm_display_mode *mode = &dp->mhdp.mode;
- DRM_DEV_INFO(dp->dev, "Connected with sink. Re-train link\n");
- ret = cdn_dp_train_link(dp);
+ DRM_DEV_INFO(dev, "Connected with sink. Re-train link\n");
+ ret = cdns_mhdp_train_link(&dp->mhdp);
if (ret) {
dp->connected = false;
- DRM_DEV_ERROR(dp->dev, "Train link failed %d\n", ret);
+ DRM_DEV_ERROR(dev, "Train link failed %d\n", ret);
goto out;
}
/* If training result is changed, update the video config */
if (mode->clock &&
- (rate != dp->max_rate || lanes != dp->max_lanes)) {
- ret = cdn_dp_config_video(dp);
+ (rate != dp->mhdp.dp.rate ||
+ lanes != dp->mhdp.dp.num_lanes)) {
+ ret = cdns_mhdp_config_video(&dp->mhdp);
if (ret) {
dp->connected = false;
- DRM_DEV_ERROR(dp->dev,
+ DRM_DEV_ERROR(dev,
"Failed to config video %d\n",
ret);
}
@@ -1039,7 +1055,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
drm_encoder_helper_add(encoder, &cdn_dp_encoder_helper_funcs);
- connector = &dp->connector;
+ connector = &dp->mhdp.connector.base;
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->dpms = DRM_MODE_DPMS_OFF;
@@ -1063,7 +1079,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
port = dp->port[i];
port->event_nb.notifier_call = cdn_dp_pd_event;
- ret = devm_extcon_register_notifier(dp->dev, port->extcon,
+ ret = devm_extcon_register_notifier(dp->mhdp.dev, port->extcon,
EXTCON_DISP_DP,
&port->event_nb);
if (ret) {
@@ -1090,7 +1106,7 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data)
{
struct cdn_dp_device *dp = dev_get_drvdata(dev);
struct drm_encoder *encoder = &dp->encoder;
- struct drm_connector *connector = &dp->connector;
+ struct drm_connector *connector = &dp->mhdp.connector.base;
cancel_work_sync(&dp->event_work);
cdn_dp_encoder_disable(encoder);
@@ -1150,7 +1166,7 @@ static int cdn_dp_probe(struct platform_device *pdev)
dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
if (!dp)
return -ENOMEM;
- dp->dev = dev;
+ dp->mhdp.dev = dev;
match = of_match_node(cdn_dp_dt_ids, pdev->dev.of_node);
dp_data = (struct cdn_dp_data *)match->data;
@@ -1194,8 +1210,8 @@ static int cdn_dp_remove(struct platform_device *pdev)
{
struct cdn_dp_device *dp = platform_get_drvdata(pdev);
- platform_device_unregister(dp->audio_pdev);
- cdn_dp_suspend(dp->dev);
+ platform_device_unregister(dp->mhdp.audio_pdev);
+ cdn_dp_suspend(dp->mhdp.dev);
component_del(&pdev->dev, &cdn_dp_component_ops);
return 0;
@@ -1205,7 +1221,7 @@ static void cdn_dp_shutdown(struct platform_device *pdev)
{
struct cdn_dp_device *dp = platform_get_drvdata(pdev);
- cdn_dp_suspend(dp->dev);
+ cdn_dp_suspend(dp->mhdp.dev);
}
static const struct dev_pm_ops cdn_dp_pm_ops = {
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 81ac9b658a70..d5dcb75b2398 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -11,39 +11,11 @@
#include <drm/drm_panel.h>
#include <drm/drm_probe_helper.h>
+#include "cdn-dp-reg.h"
#include "rockchip_drm_drv.h"
#define MAX_PHY 2
-enum audio_format {
- AFMT_I2S = 0,
- AFMT_SPDIF = 1,
- AFMT_UNUSED,
-};
-
-struct audio_info {
- enum audio_format format;
- int sample_rate;
- int channels;
- int sample_width;
-};
-
-enum vic_pxl_encoding_format {
- PXL_RGB = 0x1,
- YCBCR_4_4_4 = 0x2,
- YCBCR_4_2_2 = 0x4,
- YCBCR_4_2_0 = 0x8,
- Y_ONLY = 0x10,
-};
-
-struct video_info {
- bool h_sync_polarity;
- bool v_sync_polarity;
- bool interlaced;
- int color_depth;
- enum vic_pxl_encoding_format color_fmt;
-};
-
struct cdn_firmware_header {
u32 size_bytes; /* size of the entire header+image(s) in bytes */
u32 header_size; /* size of just the header in bytes */
@@ -62,12 +34,9 @@ struct cdn_dp_port {
};
struct cdn_dp_device {
- struct device *dev;
+ struct cdns_mhdp_device mhdp;
struct drm_device *drm_dev;
- struct drm_connector connector;
struct drm_encoder encoder;
- struct drm_display_mode mode;
- struct platform_device *audio_pdev;
struct work_struct event_work;
struct edid *edid;
@@ -77,29 +46,20 @@ struct cdn_dp_device {
bool suspended;
const struct firmware *fw; /* cdn dp firmware */
- unsigned int fw_version; /* cdn fw version */
bool fw_loaded;
- void __iomem *regs;
struct regmap *grf;
struct clk *core_clk;
struct clk *pclk;
- struct clk *spdif_clk;
struct clk *grf_clk;
- struct reset_control *spdif_rst;
struct reset_control *dptx_rst;
struct reset_control *apb_rst;
struct reset_control *core_rst;
- struct audio_info audio_info;
- struct video_info video_info;
struct cdn_dp_port *port[MAX_PHY];
u8 ports;
- u8 max_lanes;
- unsigned int max_rate;
u8 lanes;
int active_port;
- u8 dpcd[DP_RECEIVER_CAP_SIZE];
bool sink_has_audio;
};
#endif /* _CDN_DP_CORE_H */
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 7361c07cb4a7..f7ccf93fe5e1 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -4,6 +4,7 @@
* Author: Chris Zhong <zyw@rock-chips.com>
*/
+#include <asm/unaligned.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
@@ -14,19 +15,29 @@
#include "cdn-dp-core.h"
#include "cdn-dp-reg.h"
-#define CDN_DP_SPDIF_CLK 200000000
+#define CDNS_DP_SPDIF_CLK 200000000
#define FW_ALIVE_TIMEOUT_US 1000000
#define MAILBOX_RETRY_US 1000
#define MAILBOX_TIMEOUT_US 5000000
#define LINK_TRAINING_RETRY_MS 20
#define LINK_TRAINING_TIMEOUT_MS 500
-void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk)
+u32 cdns_mhdp_bus_read(struct cdns_mhdp_device *mhdp, u32 offset)
{
- writel(clk / 1000000, dp->regs + SW_CLK_H);
+ return readl(mhdp->regs_base + offset);
}
-void cdn_dp_clock_reset(struct cdn_dp_device *dp)
+void cdns_mhdp_bus_write(u32 val, struct cdns_mhdp_device *mhdp, u32 offset)
+{
+ writel(val, mhdp->regs_base + offset);
+}
+
+void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk)
+{
+ cdns_mhdp_bus_write(clk / 1000000, mhdp, SW_CLK_H);
+}
+
+void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp)
{
u32 val;
@@ -42,16 +53,16 @@ void cdn_dp_clock_reset(struct cdn_dp_device *dp)
DPTX_SYS_CLK_EN |
CFG_DPTX_VIF_CLK_RSTN_EN |
CFG_DPTX_VIF_CLK_EN;
- writel(val, dp->regs + SOURCE_DPTX_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_DPTX_CAR);
val = SOURCE_PHY_RSTN_EN | SOURCE_PHY_CLK_EN;
- writel(val, dp->regs + SOURCE_PHY_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_PHY_CAR);
val = SOURCE_PKT_SYS_RSTN_EN |
SOURCE_PKT_SYS_CLK_EN |
SOURCE_PKT_DATA_RSTN_EN |
SOURCE_PKT_DATA_CLK_EN;
- writel(val, dp->regs + SOURCE_PKT_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_PKT_CAR);
val = SPDIF_CDR_CLK_RSTN_EN |
SPDIF_CDR_CLK_EN |
@@ -59,53 +70,52 @@ void cdn_dp_clock_reset(struct cdn_dp_device *dp)
SOURCE_AIF_SYS_CLK_EN |
SOURCE_AIF_CLK_RSTN_EN |
SOURCE_AIF_CLK_EN;
- writel(val, dp->regs + SOURCE_AIF_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_AIF_CAR);
val = SOURCE_CIPHER_SYSTEM_CLK_RSTN_EN |
SOURCE_CIPHER_SYS_CLK_EN |
SOURCE_CIPHER_CHAR_CLK_RSTN_EN |
SOURCE_CIPHER_CHAR_CLK_EN;
- writel(val, dp->regs + SOURCE_CIPHER_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_CIPHER_CAR);
val = SOURCE_CRYPTO_SYS_CLK_RSTN_EN |
SOURCE_CRYPTO_SYS_CLK_EN;
- writel(val, dp->regs + SOURCE_CRYPTO_CAR);
+ cdns_mhdp_bus_write(val, mhdp, SOURCE_CRYPTO_CAR);
/* enable Mailbox and PIF interrupt */
- writel(0, dp->regs + APB_INT_MASK);
+ cdns_mhdp_bus_write(0, mhdp, APB_INT_MASK);
}
-static int cdn_dp_mailbox_read(struct cdn_dp_device *dp)
+static int mhdp_mailbox_read(struct cdns_mhdp_device *mhdp)
{
int val, ret;
- ret = readx_poll_timeout(readl, dp->regs + MAILBOX_EMPTY_ADDR,
+ ret = readx_poll_timeout(readl, mhdp->regs_base + MAILBOX_EMPTY_ADDR,
val, !val, MAILBOX_RETRY_US,
MAILBOX_TIMEOUT_US);
if (ret < 0)
return ret;
- return readl(dp->regs + MAILBOX0_RD_DATA) & 0xff;
+ return cdns_mhdp_bus_read(mhdp, MAILBOX0_RD_DATA) & 0xff;
}
-static int cdp_dp_mailbox_write(struct cdn_dp_device *dp, u8 val)
+static int mhdp_mailbox_write(struct cdns_mhdp_device *mhdp, u8 val)
{
int ret, full;
- ret = readx_poll_timeout(readl, dp->regs + MAILBOX_FULL_ADDR,
+ ret = readx_poll_timeout(readl, mhdp->regs_base + MAILBOX_FULL_ADDR,
full, !full, MAILBOX_RETRY_US,
MAILBOX_TIMEOUT_US);
if (ret < 0)
return ret;
- writel(val, dp->regs + MAILBOX0_WR_DATA);
+ cdns_mhdp_bus_write(val, mhdp, MAILBOX0_WR_DATA);
return 0;
}
-static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
- u8 module_id, u8 opcode,
- u16 req_size)
+int cdns_mhdp_mailbox_validate_receive(struct cdns_mhdp_device *mhdp,
+ u8 module_id, u8 opcode, u16 req_size)
{
u32 mbox_size, i;
u8 header[4];
@@ -113,14 +123,14 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
/* read the header of the message */
for (i = 0; i < 4; i++) {
- ret = cdn_dp_mailbox_read(dp);
+ ret = mhdp_mailbox_read(mhdp);
if (ret < 0)
return ret;
header[i] = ret;
}
- mbox_size = (header[2] << 8) | header[3];
+ mbox_size = get_unaligned_be16(header + 2);
if (opcode != header[0] || module_id != header[1] ||
req_size != mbox_size) {
@@ -129,7 +139,7 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
* clear the mailbox by reading its contents.
*/
for (i = 0; i < mbox_size; i++)
- if (cdn_dp_mailbox_read(dp) < 0)
+ if (mhdp_mailbox_read(mhdp) < 0)
break;
return -EINVAL;
@@ -138,14 +148,14 @@ static int cdn_dp_mailbox_validate_receive(struct cdn_dp_device *dp,
return 0;
}
-static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
- u8 *buff, u16 buff_size)
+int cdns_mhdp_mailbox_read_receive(struct cdns_mhdp_device *mhdp,
+ u8 *buff, u16 buff_size)
{
u32 i;
int ret;
for (i = 0; i < buff_size; i++) {
- ret = cdn_dp_mailbox_read(dp);
+ ret = mhdp_mailbox_read(mhdp);
if (ret < 0)
return ret;
@@ -155,25 +165,24 @@ static int cdn_dp_mailbox_read_receive(struct cdn_dp_device *dp,
return 0;
}
-static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
- u8 opcode, u16 size, u8 *message)
+int cdns_mhdp_mailbox_send(struct cdns_mhdp_device *mhdp, u8 module_id,
+ u8 opcode, u16 size, u8 *message)
{
u8 header[4];
int ret, i;
header[0] = opcode;
header[1] = module_id;
- header[2] = (size >> 8) & 0xff;
- header[3] = size & 0xff;
+ put_unaligned_be16(size, header + 2);
for (i = 0; i < 4; i++) {
- ret = cdp_dp_mailbox_write(dp, header[i]);
+ ret = mhdp_mailbox_write(mhdp, header[i]);
if (ret)
return ret;
}
for (i = 0; i < size; i++) {
- ret = cdp_dp_mailbox_write(dp, message[i]);
+ ret = mhdp_mailbox_write(mhdp, message[i]);
if (ret)
return ret;
}
@@ -181,146 +190,136 @@ static int cdn_dp_mailbox_send(struct cdn_dp_device *dp, u8 module_id,
return 0;
}
-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val)
{
- u8 msg[6];
-
- msg[0] = (addr >> 8) & 0xff;
- msg[1] = addr & 0xff;
- msg[2] = (val >> 24) & 0xff;
- msg[3] = (val >> 16) & 0xff;
- msg[4] = (val >> 8) & 0xff;
- msg[5] = val & 0xff;
- return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_REGISTER,
- sizeof(msg), msg);
+ u8 msg[8];
+
+ put_unaligned_be32(addr, msg);
+ put_unaligned_be32(val, msg + 4);
+
+ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL,
+ GENERAL_WRITE_REGISTER, sizeof(msg), msg);
}
-static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr,
- u8 start_bit, u8 bits_no, u32 val)
+int cdns_mhdp_reg_write_bit(struct cdns_mhdp_device *mhdp, u16 addr,
+ u8 start_bit, u8 bits_no, u32 val)
{
u8 field[8];
- field[0] = (addr >> 8) & 0xff;
- field[1] = addr & 0xff;
+ put_unaligned_be16(addr, field);
field[2] = start_bit;
field[3] = bits_no;
- field[4] = (val >> 24) & 0xff;
- field[5] = (val >> 16) & 0xff;
- field[6] = (val >> 8) & 0xff;
- field[7] = val & 0xff;
+ put_unaligned_be32(val, field + 4);
- return cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_FIELD,
- sizeof(field), field);
+ return cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_FIELD, sizeof(field), field);
}
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
+ u32 addr, u8 *data, u16 len)
{
u8 msg[5], reg[5];
int ret;
- msg[0] = (len >> 8) & 0xff;
- msg[1] = len & 0xff;
- msg[2] = (addr >> 16) & 0xff;
- msg[3] = (addr >> 8) & 0xff;
- msg[4] = addr & 0xff;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_DPCD,
- sizeof(msg), msg);
+ put_unaligned_be16(len, msg);
+ put_unaligned_be24(addr, msg + 2);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD, sizeof(msg), msg);
if (ret)
goto err_dpcd_read;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_READ_DPCD,
- sizeof(reg) + len);
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_DPCD,
+ sizeof(reg) + len);
if (ret)
goto err_dpcd_read;
- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_dpcd_read;
- ret = cdn_dp_mailbox_read_receive(dp, data, len);
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, data, len);
err_dpcd_read:
return ret;
}
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value)
{
u8 msg[6], reg[5];
int ret;
- msg[0] = 0;
- msg[1] = 1;
- msg[2] = (addr >> 16) & 0xff;
- msg[3] = (addr >> 8) & 0xff;
- msg[4] = addr & 0xff;
+ put_unaligned_be16(1, msg);
+ put_unaligned_be24(addr, msg + 2);
msg[5] = value;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
- sizeof(msg), msg);
+
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_DPCD, sizeof(msg), msg);
if (ret)
goto err_dpcd_write;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_WRITE_DPCD, sizeof(reg));
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_WRITE_DPCD, sizeof(reg));
if (ret)
goto err_dpcd_write;
- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
goto err_dpcd_write;
- if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+ if (addr != get_unaligned_be24(reg + 2))
ret = -EINVAL;
err_dpcd_write:
if (ret)
- DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "dpcd write failed: %d\n", ret);
return ret;
}
-int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
- u32 i_size, const u32 *d_mem, u32 d_size)
+int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem,
+ u32 i_size, const u32 *d_mem, u32 d_size)
{
u32 reg;
int i, ret;
/* reset ucpu before load firmware*/
- writel(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
- dp->regs + APB_CTRL);
+ cdns_mhdp_bus_write(APB_IRAM_PATH | APB_DRAM_PATH | APB_XT_RESET,
+ mhdp, APB_CTRL);
for (i = 0; i < i_size; i += 4)
- writel(*i_mem++, dp->regs + ADDR_IMEM + i);
+ cdns_mhdp_bus_write(*i_mem++, mhdp, ADDR_IMEM + i);
for (i = 0; i < d_size; i += 4)
- writel(*d_mem++, dp->regs + ADDR_DMEM + i);
+ cdns_mhdp_bus_write(*d_mem++, mhdp, ADDR_DMEM + i);
/* un-reset ucpu */
- writel(0, dp->regs + APB_CTRL);
+ cdns_mhdp_bus_write(0, mhdp, APB_CTRL);
/* check the keep alive register to make sure fw working */
- ret = readx_poll_timeout(readl, dp->regs + KEEP_ALIVE,
+ ret = readx_poll_timeout(readl, mhdp->regs_base + KEEP_ALIVE,
reg, reg, 2000, FW_ALIVE_TIMEOUT_US);
if (ret < 0) {
- DRM_DEV_ERROR(dp->dev, "failed to loaded the FW reg = %x\n",
+ DRM_DEV_ERROR(mhdp->dev, "failed to loaded the FW reg = %x\n",
reg);
return -EINVAL;
}
- reg = readl(dp->regs + VER_L) & 0xff;
- dp->fw_version = reg;
- reg = readl(dp->regs + VER_H) & 0xff;
- dp->fw_version |= reg << 8;
- reg = readl(dp->regs + VER_LIB_L_ADDR) & 0xff;
- dp->fw_version |= reg << 16;
- reg = readl(dp->regs + VER_LIB_H_ADDR) & 0xff;
- dp->fw_version |= reg << 24;
+ reg = cdns_mhdp_bus_read(mhdp, VER_L) & 0xff;
+ mhdp->fw_version = reg;
+ reg = cdns_mhdp_bus_read(mhdp, VER_H) & 0xff;
+ mhdp->fw_version |= reg << 8;
+ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_L_ADDR) & 0xff;
+ mhdp->fw_version |= reg << 16;
+ reg = cdns_mhdp_bus_read(mhdp, VER_LIB_H_ADDR) & 0xff;
+ mhdp->fw_version |= reg << 24;
- DRM_DEV_DEBUG(dp->dev, "firmware version: %x\n", dp->fw_version);
+ DRM_DEV_DEBUG(mhdp->dev, "firmware version: %x\n", mhdp->fw_version);
return 0;
}
-int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable)
+int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable)
{
u8 msg[5];
int ret, i;
@@ -332,14 +331,14 @@ int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable)
msg[4] = enable ? FW_ACTIVE : FW_STANDBY;
for (i = 0; i < sizeof(msg); i++) {
- ret = cdp_dp_mailbox_write(dp, msg[i]);
+ ret = mhdp_mailbox_write(mhdp, msg[i]);
if (ret)
goto err_set_firmware_active;
}
/* read the firmware state */
for (i = 0; i < sizeof(msg); i++) {
- ret = cdn_dp_mailbox_read(dp);
+ ret = mhdp_mailbox_read(mhdp);
if (ret < 0)
goto err_set_firmware_active;
@@ -350,17 +349,17 @@ int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable)
err_set_firmware_active:
if (ret < 0)
- DRM_DEV_ERROR(dp->dev, "set firmware active failed\n");
+ DRM_DEV_ERROR(mhdp->dev, "set firmware active failed\n");
return ret;
}
-int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip)
+int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip)
{
u8 msg[8];
int ret;
- msg[0] = CDN_DP_MAX_LINK_RATE;
- msg[1] = lanes | SCRAMBLER_EN;
+ msg[0] = drm_dp_link_rate_to_bw_code(mhdp->dp.rate);
+ msg[1] = mhdp->dp.num_lanes | SCRAMBLER_EN;
msg[2] = VOLTAGE_LEVEL_2;
msg[3] = PRE_EMPHASIS_LEVEL_3;
msg[4] = PTS1 | PTS2 | PTS3 | PTS4;
@@ -368,73 +367,73 @@ int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip)
msg[6] = flip ? LANE_MAPPING_FLIPPED : LANE_MAPPING_NORMAL;
msg[7] = ENHANCED;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
- DPTX_SET_HOST_CAPABILITIES,
- sizeof(msg), msg);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_SET_HOST_CAPABILITIES,
+ sizeof(msg), msg);
if (ret)
goto err_set_host_cap;
- ret = cdn_dp_reg_write(dp, DP_AUX_SWAP_INVERSION_CONTROL,
- AUX_HOST_INVERT);
+ ret = cdns_mhdp_reg_write(mhdp, DP_AUX_SWAP_INVERSION_CONTROL,
+ AUX_HOST_INVERT);
err_set_host_cap:
if (ret)
- DRM_DEV_ERROR(dp->dev, "set host cap failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "set host cap failed: %d\n", ret);
return ret;
}
-int cdn_dp_event_config(struct cdn_dp_device *dp)
+int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp)
{
u8 msg[5];
int ret;
memset(msg, 0, sizeof(msg));
- msg[0] = DPTX_EVENT_ENABLE_HPD | DPTX_EVENT_ENABLE_TRAINING;
+ msg[0] = MHDP_EVENT_ENABLE_HPD | MHDP_EVENT_ENABLE_TRAINING;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_ENABLE_EVENT,
- sizeof(msg), msg);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_ENABLE_EVENT, sizeof(msg), msg);
if (ret)
- DRM_DEV_ERROR(dp->dev, "set event config failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "set event config failed: %d\n", ret);
return ret;
}
-u32 cdn_dp_get_event(struct cdn_dp_device *dp)
+u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp)
{
- return readl(dp->regs + SW_EVENTS0);
+ return cdns_mhdp_bus_read(mhdp, SW_EVENTS0);
}
-int cdn_dp_get_hpd_status(struct cdn_dp_device *dp)
+int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp)
{
u8 status;
int ret;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_HPD_STATE,
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_GENERAL, GENERAL_GET_HPD_STATE,
0, NULL);
if (ret)
goto err_get_hpd;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_HPD_STATE, sizeof(status));
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_GENERAL,
+ GENERAL_GET_HPD_STATE, sizeof(status));
if (ret)
goto err_get_hpd;
- ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, &status, sizeof(status));
if (ret)
goto err_get_hpd;
return status;
err_get_hpd:
- DRM_DEV_ERROR(dp->dev, "get hpd status failed: %d\n", ret);
+ DRM_ERROR("read hpd failed: %d\n", ret);
return ret;
}
-int cdn_dp_get_edid_block(void *data, u8 *edid,
+int cdns_mhdp_get_edid_block(void *data, u8 *edid,
unsigned int block, size_t length)
{
- struct cdn_dp_device *dp = data;
+ struct cdns_mhdp_device *mhdp = data;
u8 msg[2], reg[2], i;
int ret;
@@ -442,22 +441,23 @@ int cdn_dp_get_edid_block(void *data, u8 *edid,
msg[0] = block / 2;
msg[1] = block % 2;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_GET_EDID,
- sizeof(msg), msg);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_GET_EDID, sizeof(msg), msg);
if (ret)
continue;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_GET_EDID,
- sizeof(reg) + length);
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp,
+ MB_MODULE_ID_DP_TX,
+ DPTX_GET_EDID,
+ sizeof(reg) + length);
if (ret)
continue;
- ret = cdn_dp_mailbox_read_receive(dp, reg, sizeof(reg));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, reg, sizeof(reg));
if (ret)
continue;
- ret = cdn_dp_mailbox_read_receive(dp, edid, length);
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, edid, length);
if (ret)
continue;
@@ -466,13 +466,13 @@ int cdn_dp_get_edid_block(void *data, u8 *edid,
}
if (ret)
- DRM_DEV_ERROR(dp->dev, "get block[%d] edid failed: %d\n", block,
- ret);
+ DRM_DEV_ERROR(mhdp->dev, "get block[%d] edid failed: %d\n",
+ block, ret);
return ret;
}
-static int cdn_dp_training_start(struct cdn_dp_device *dp)
+static int cdns_mhdp_training_start(struct cdns_mhdp_device *mhdp)
{
unsigned long timeout;
u8 msg, event[2];
@@ -481,26 +481,27 @@ static int cdn_dp_training_start(struct cdn_dp_device *dp)
msg = LINK_TRAINING_RUN;
/* start training */
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
- sizeof(msg), &msg);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_TRAINING_CONTROL,
+ sizeof(msg), &msg);
if (ret)
goto err_training_start;
timeout = jiffies + msecs_to_jiffies(LINK_TRAINING_TIMEOUT_MS);
while (time_before(jiffies, timeout)) {
msleep(LINK_TRAINING_RETRY_MS);
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
- DPTX_READ_EVENT, 0, NULL);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_EVENT, 0, NULL);
if (ret)
goto err_training_start;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_READ_EVENT,
- sizeof(event));
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp,
+ MB_MODULE_ID_DP_TX,
+ DPTX_READ_EVENT,
+ sizeof(event));
if (ret)
goto err_training_start;
- ret = cdn_dp_mailbox_read_receive(dp, event, sizeof(event));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, event, sizeof(event));
if (ret)
goto err_training_start;
@@ -511,77 +512,77 @@ static int cdn_dp_training_start(struct cdn_dp_device *dp)
ret = -ETIMEDOUT;
err_training_start:
- DRM_DEV_ERROR(dp->dev, "training failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "training failed: %d\n", ret);
return ret;
}
-static int cdn_dp_get_training_status(struct cdn_dp_device *dp)
+static int cdns_mhdp_get_training_status(struct cdns_mhdp_device *mhdp)
{
u8 status[10];
int ret;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
- 0, NULL);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_READ_LINK_STAT,
+ 0, NULL);
if (ret)
goto err_get_training_status;
- ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
- DPTX_READ_LINK_STAT,
- sizeof(status));
+ ret = cdns_mhdp_mailbox_validate_receive(mhdp, MB_MODULE_ID_DP_TX,
+ DPTX_READ_LINK_STAT,
+ sizeof(status));
if (ret)
goto err_get_training_status;
- ret = cdn_dp_mailbox_read_receive(dp, status, sizeof(status));
+ ret = cdns_mhdp_mailbox_read_receive(mhdp, status, sizeof(status));
if (ret)
goto err_get_training_status;
- dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]);
- dp->max_lanes = status[1];
+ mhdp->dp.rate = drm_dp_bw_code_to_link_rate(status[0]);
+ mhdp->dp.num_lanes = status[1];
err_get_training_status:
if (ret)
- DRM_DEV_ERROR(dp->dev, "get training status failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "get training status failed: %d\n", ret);
return ret;
}
-int cdn_dp_train_link(struct cdn_dp_device *dp)
+int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp)
{
int ret;
- ret = cdn_dp_training_start(dp);
+ ret = cdns_mhdp_training_start(mhdp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "Failed to start training %d\n", ret);
return ret;
}
- ret = cdn_dp_get_training_status(dp);
+ ret = cdns_mhdp_get_training_status(mhdp);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "Failed to get training stat %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "Failed to get training stat %d\n", ret);
return ret;
}
- DRM_DEV_DEBUG_KMS(dp->dev, "rate:0x%x, lanes:%d\n", dp->max_rate,
- dp->max_lanes);
+ DRM_DEV_DEBUG_KMS(mhdp->dev, "rate:0x%x, lanes:%d\n", mhdp->dp.rate,
+ mhdp->dp.num_lanes);
return ret;
}
-int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active)
+int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active)
{
u8 msg;
int ret;
msg = !!active;
- ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
- sizeof(msg), &msg);
+ ret = cdns_mhdp_mailbox_send(mhdp, MB_MODULE_ID_DP_TX, DPTX_SET_VIDEO,
+ sizeof(msg), &msg);
if (ret)
- DRM_DEV_ERROR(dp->dev, "set video status failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "set video status failed: %d\n", ret);
return ret;
}
-static int cdn_dp_get_msa_misc(struct video_info *video,
- struct drm_display_mode *mode)
+static int mhdp_get_msa_misc(struct video_info *video,
+ struct drm_display_mode *mode)
{
u32 msa_misc;
u8 val[2] = {0};
@@ -627,10 +628,10 @@ static int cdn_dp_get_msa_misc(struct video_info *video,
return msa_misc;
}
-int cdn_dp_config_video(struct cdn_dp_device *dp)
+int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp)
{
- struct video_info *video = &dp->video_info;
- struct drm_display_mode *mode = &dp->mode;
+ struct video_info *video = &mhdp->video_info;
+ struct drm_display_mode *mode = &mhdp->mode;
u64 symbol;
u32 val, link_rate, rem;
u8 bit_per_pix, tu_size_reg = TU_SIZE;
@@ -639,13 +640,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
(video->color_depth * 2) : (video->color_depth * 3);
- link_rate = dp->max_rate / 1000;
+ link_rate = mhdp->dp.rate / 1000;
- ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
+ ret = cdns_mhdp_reg_write(mhdp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
if (ret)
goto err_config_video;
- ret = cdn_dp_reg_write(dp, HSYNC2VSYNC_POL_CTRL, 0);
+ ret = cdns_mhdp_reg_write(mhdp, HSYNC2VSYNC_POL_CTRL, 0);
if (ret)
goto err_config_video;
@@ -659,13 +660,13 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
do {
tu_size_reg += 2;
symbol = tu_size_reg * mode->clock * bit_per_pix;
- do_div(symbol, dp->max_lanes * link_rate * 8);
+ do_div(symbol, mhdp->dp.num_lanes * link_rate * 8);
rem = do_div(symbol, 1000);
if (tu_size_reg > 64) {
ret = -EINVAL;
- DRM_DEV_ERROR(dp->dev,
+ DRM_DEV_ERROR(mhdp->dev,
"tu error, clk:%d, lanes:%d, rate:%d\n",
- mode->clock, dp->max_lanes, link_rate);
+ mode->clock, mhdp->dp.num_lanes, link_rate);
goto err_config_video;
}
} while ((symbol <= 1) || (tu_size_reg - symbol < 4) ||
@@ -673,16 +674,16 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
val = symbol + (tu_size_reg << 8);
val |= TU_CNT_RST_EN;
- ret = cdn_dp_reg_write(dp, DP_FRAMER_TU, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_TU, val);
if (ret)
goto err_config_video;
/* set the FIFO Buffer size */
val = div_u64(mode->clock * (symbol + 1), 1000) + link_rate;
- val /= (dp->max_lanes * link_rate);
+ val /= (mhdp->dp.num_lanes * link_rate);
val = div_u64(8 * (symbol + 1), bit_per_pix) - val;
val += 2;
- ret = cdn_dp_reg_write(dp, DP_VC_TABLE(15), val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_VC_TABLE(15), val);
switch (video->color_depth) {
case 6:
@@ -703,136 +704,136 @@ int cdn_dp_config_video(struct cdn_dp_device *dp)
};
val += video->color_fmt << 8;
- ret = cdn_dp_reg_write(dp, DP_FRAMER_PXL_REPR, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_PXL_REPR, val);
if (ret)
goto err_config_video;
val = video->h_sync_polarity ? DP_FRAMER_SP_HSP : 0;
val |= video->v_sync_polarity ? DP_FRAMER_SP_VSP : 0;
- ret = cdn_dp_reg_write(dp, DP_FRAMER_SP, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_FRAMER_SP, val);
if (ret)
goto err_config_video;
val = (mode->hsync_start - mode->hdisplay) << 16;
val |= mode->htotal - mode->hsync_end;
- ret = cdn_dp_reg_write(dp, DP_FRONT_BACK_PORCH, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_FRONT_BACK_PORCH, val);
if (ret)
goto err_config_video;
val = mode->hdisplay * bit_per_pix / 8;
- ret = cdn_dp_reg_write(dp, DP_BYTE_COUNT, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_BYTE_COUNT, val);
if (ret)
goto err_config_video;
val = mode->htotal | ((mode->htotal - mode->hsync_start) << 16);
- ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_0, val);
+ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_0, val);
if (ret)
goto err_config_video;
val = mode->hsync_end - mode->hsync_start;
val |= (mode->hdisplay << 16) | (video->h_sync_polarity << 15);
- ret = cdn_dp_reg_write(dp, MSA_HORIZONTAL_1, val);
+ ret = cdns_mhdp_reg_write(mhdp, MSA_HORIZONTAL_1, val);
if (ret)
goto err_config_video;
val = mode->vtotal;
val |= (mode->vtotal - mode->vsync_start) << 16;
- ret = cdn_dp_reg_write(dp, MSA_VERTICAL_0, val);
+ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_0, val);
if (ret)
goto err_config_video;
val = mode->vsync_end - mode->vsync_start;
val |= (mode->vdisplay << 16) | (video->v_sync_polarity << 15);
- ret = cdn_dp_reg_write(dp, MSA_VERTICAL_1, val);
+ ret = cdns_mhdp_reg_write(mhdp, MSA_VERTICAL_1, val);
if (ret)
goto err_config_video;
- val = cdn_dp_get_msa_misc(video, mode);
- ret = cdn_dp_reg_write(dp, MSA_MISC, val);
+ val = mhdp_get_msa_misc(video, mode);
+ ret = cdns_mhdp_reg_write(mhdp, MSA_MISC, val);
if (ret)
goto err_config_video;
- ret = cdn_dp_reg_write(dp, STREAM_CONFIG, 1);
+ ret = cdns_mhdp_reg_write(mhdp, STREAM_CONFIG, 1);
if (ret)
goto err_config_video;
val = mode->hsync_end - mode->hsync_start;
val |= mode->hdisplay << 16;
- ret = cdn_dp_reg_write(dp, DP_HORIZONTAL, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_HORIZONTAL, val);
if (ret)
goto err_config_video;
val = mode->vdisplay;
val |= (mode->vtotal - mode->vsync_start) << 16;
- ret = cdn_dp_reg_write(dp, DP_VERTICAL_0, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_0, val);
if (ret)
goto err_config_video;
val = mode->vtotal;
- ret = cdn_dp_reg_write(dp, DP_VERTICAL_1, val);
+ ret = cdns_mhdp_reg_write(mhdp, DP_VERTICAL_1, val);
if (ret)
goto err_config_video;
- ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 2, 1, 0);
+ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 2, 1, 0);
err_config_video:
if (ret)
- DRM_DEV_ERROR(dp->dev, "config video failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "config video failed: %d\n", ret);
return ret;
}
-int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio)
+int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp, struct audio_info *audio)
{
int ret;
- ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0);
+ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, 0);
if (ret) {
- DRM_DEV_ERROR(dp->dev, "audio stop failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "audio stop failed: %d\n", ret);
return ret;
}
- writel(0, dp->regs + SPDIF_CTRL_ADDR);
+ cdns_mhdp_bus_write(0, mhdp, SPDIF_CTRL_ADDR);
/* clearn the audio config and reset */
- writel(0, dp->regs + AUDIO_SRC_CNTL);
- writel(0, dp->regs + AUDIO_SRC_CNFG);
- writel(AUDIO_SW_RST, dp->regs + AUDIO_SRC_CNTL);
- writel(0, dp->regs + AUDIO_SRC_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNFG);
+ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, AUDIO_SRC_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, AUDIO_SRC_CNTL);
/* reset smpl2pckt component */
- writel(0, dp->regs + SMPL2PKT_CNTL);
- writel(AUDIO_SW_RST, dp->regs + SMPL2PKT_CNTL);
- writel(0, dp->regs + SMPL2PKT_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
+ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, SMPL2PKT_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, SMPL2PKT_CNTL);
/* reset FIFO */
- writel(AUDIO_SW_RST, dp->regs + FIFO_CNTL);
- writel(0, dp->regs + FIFO_CNTL);
+ cdns_mhdp_bus_write(AUDIO_SW_RST, mhdp, FIFO_CNTL);
+ cdns_mhdp_bus_write(0, mhdp, FIFO_CNTL);
- if (audio->format == AFMT_SPDIF)
- clk_disable_unprepare(dp->spdif_clk);
+ if (audio->format == AFMT_SPDIF_INT)
+ clk_disable_unprepare(mhdp->spdif_clk);
return 0;
}
-int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable)
+int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable)
{
- int ret;
+ int ret = true;
- ret = cdn_dp_reg_write_bit(dp, DP_VB_ID, 4, 1, enable);
+ ret = cdns_mhdp_reg_write_bit(mhdp, DP_VB_ID, 4, 1, enable);
if (ret)
- DRM_DEV_ERROR(dp->dev, "audio mute failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "audio mute failed: %d\n", ret);
return ret;
}
-static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
- struct audio_info *audio)
+static void cdns_mhdp_audio_config_i2s(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio)
{
int sub_pckt_num = 1, i2s_port_en_val = 0xf, i;
u32 val;
if (audio->channels == 2) {
- if (dp->max_lanes == 1)
+ if (mhdp->dp.num_lanes == 1)
sub_pckt_num = 2;
else
sub_pckt_num = 4;
@@ -842,15 +843,15 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
i2s_port_en_val = 3;
}
- writel(0x0, dp->regs + SPDIF_CTRL_ADDR);
+ cdns_mhdp_bus_write(0x0, mhdp, SPDIF_CTRL_ADDR);
- writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
val = MAX_NUM_CH(audio->channels);
val |= NUM_OF_I2S_PORTS(audio->channels);
val |= AUDIO_TYPE_LPCM;
val |= CFG_SUB_PCKT_NUM(sub_pckt_num);
- writel(val, dp->regs + SMPL2PKT_CNFG);
+ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
if (audio->sample_width == 16)
val = 0;
@@ -862,7 +863,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
val |= AUDIO_CH_NUM(audio->channels);
val |= I2S_DEC_PORT_EN(i2s_port_en_val);
val |= TRANS_SMPL_WIDTH_32;
- writel(val, dp->regs + AUDIO_SRC_CNFG);
+ cdns_mhdp_bus_write(val, mhdp, AUDIO_SRC_CNFG);
for (i = 0; i < (audio->channels + 1) / 2; i++) {
if (audio->sample_width == 16)
@@ -871,7 +872,7 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
val = (0x0b << 8) | (0x0b << 20);
val |= ((2 * i) << 4) | ((2 * i + 1) << 16);
- writel(val, dp->regs + STTS_BIT_CH(i));
+ cdns_mhdp_bus_write(val, mhdp, STTS_BIT_CH(i));
}
switch (audio->sample_rate) {
@@ -905,56 +906,57 @@ static void cdn_dp_audio_config_i2s(struct cdn_dp_device *dp,
break;
}
val |= 4;
- writel(val, dp->regs + COM_CH_STTS_BITS);
+ cdns_mhdp_bus_write(val, mhdp, COM_CH_STTS_BITS);
- writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
- writel(I2S_DEC_START, dp->regs + AUDIO_SRC_CNTL);
+ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
+ cdns_mhdp_bus_write(I2S_DEC_START, mhdp, AUDIO_SRC_CNTL);
}
-static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp)
+static void cdns_mhdp_audio_config_spdif(struct cdns_mhdp_device *mhdp)
{
u32 val;
- writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL);
+ cdns_mhdp_bus_write(SYNC_WR_TO_CH_ZERO, mhdp, FIFO_CNTL);
val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4);
- writel(val, dp->regs + SMPL2PKT_CNFG);
- writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL);
+ cdns_mhdp_bus_write(val, mhdp, SMPL2PKT_CNFG);
+ cdns_mhdp_bus_write(SMPL2PKT_EN, mhdp, SMPL2PKT_CNTL);
val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS;
- writel(val, dp->regs + SPDIF_CTRL_ADDR);
+ cdns_mhdp_bus_write(val, mhdp, SPDIF_CTRL_ADDR);
- clk_prepare_enable(dp->spdif_clk);
- clk_set_rate(dp->spdif_clk, CDN_DP_SPDIF_CLK);
+ clk_prepare_enable(mhdp->spdif_clk);
+ clk_set_rate(mhdp->spdif_clk, CDNS_DP_SPDIF_CLK);
}
-int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio)
+int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio)
{
int ret;
/* reset the spdif clk before config */
- if (audio->format == AFMT_SPDIF) {
- reset_control_assert(dp->spdif_rst);
- reset_control_deassert(dp->spdif_rst);
+ if (audio->format == AFMT_SPDIF_INT) {
+ reset_control_assert(mhdp->spdif_rst);
+ reset_control_deassert(mhdp->spdif_rst);
}
- ret = cdn_dp_reg_write(dp, CM_LANE_CTRL, LANE_REF_CYC);
+ ret = cdns_mhdp_reg_write(mhdp, CM_LANE_CTRL, LANE_REF_CYC);
if (ret)
goto err_audio_config;
- ret = cdn_dp_reg_write(dp, CM_CTRL, 0);
+ ret = cdns_mhdp_reg_write(mhdp, CM_CTRL, 0);
if (ret)
goto err_audio_config;
if (audio->format == AFMT_I2S)
- cdn_dp_audio_config_i2s(dp, audio);
- else if (audio->format == AFMT_SPDIF)
- cdn_dp_audio_config_spdif(dp);
+ cdns_mhdp_audio_config_i2s(mhdp, audio);
+ else if (audio->format == AFMT_SPDIF_INT)
+ cdns_mhdp_audio_config_spdif(mhdp);
- ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
+ ret = cdns_mhdp_reg_write(mhdp, AUDIO_PACK_CONTROL, AUDIO_PACK_EN);
err_audio_config:
if (ret)
- DRM_DEV_ERROR(dp->dev, "audio config failed: %d\n", ret);
+ DRM_DEV_ERROR(mhdp->dev, "audio config failed: %d\n", ret);
return ret;
}
diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index 441248b7a79e..3c1235ff8e1c 100644
--- a/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -7,6 +7,8 @@
#ifndef _CDN_DP_REG_H
#define _CDN_DP_REG_H
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
#include <linux/bitops.h>
#define ADDR_IMEM 0x10000
@@ -308,17 +310,22 @@
#define MB_SIZE_LSB_ID 3
#define MB_DATA_ID 4
-#define MB_MODULE_ID_DP_TX 0x01
+#define MB_MODULE_ID_DP_TX 0x01
+#define MB_MODULE_ID_HDMI_TX 0x03
#define MB_MODULE_ID_HDCP_TX 0x07
#define MB_MODULE_ID_HDCP_RX 0x08
#define MB_MODULE_ID_HDCP_GENERAL 0x09
-#define MB_MODULE_ID_GENERAL 0x0a
+#define MB_MODULE_ID_GENERAL 0x0A
/* general opcode */
#define GENERAL_MAIN_CONTROL 0x01
#define GENERAL_TEST_ECHO 0x02
#define GENERAL_BUS_SETTINGS 0x03
#define GENERAL_TEST_ACCESS 0x04
+#define GENERAL_WRITE_REGISTER 0x05
+#define GENERAL_WRITE_FIELD 0x06
+#define GENERAL_READ_REGISTER 0x07
+#define GENERAL_GET_HPD_STATE 0x11
#define DPTX_SET_POWER_MNG 0x00
#define DPTX_SET_HOST_CAPABILITIES 0x01
@@ -342,8 +349,8 @@
#define FW_STANDBY 0
#define FW_ACTIVE 1
-#define DPTX_EVENT_ENABLE_HPD BIT(0)
-#define DPTX_EVENT_ENABLE_TRAINING BIT(1)
+#define MHDP_EVENT_ENABLE_HPD BIT(0)
+#define MHDP_EVENT_ENABLE_TRAINING BIT(1)
#define LINK_TRAINING_NOT_ACTIVE 0
#define LINK_TRAINING_RUN 1
@@ -387,7 +394,7 @@
#define HDCP_TX_IS_RECEIVER_ID_VALID_EVENT BIT(7)
#define TU_SIZE 30
-#define CDN_DP_MAX_LINK_RATE DP_LINK_BW_5_4
+#define CDNS_DP_MAX_LINK_RATE 540000
/* audio */
#define AUDIO_PACK_EN BIT(8)
@@ -451,24 +458,100 @@ enum vic_bt_type {
BT_709 = 0x1,
};
-void cdn_dp_clock_reset(struct cdn_dp_device *dp);
-
-void cdn_dp_set_fw_clk(struct cdn_dp_device *dp, unsigned long clk);
-int cdn_dp_load_firmware(struct cdn_dp_device *dp, const u32 *i_mem,
- u32 i_size, const u32 *d_mem, u32 d_size);
-int cdn_dp_set_firmware_active(struct cdn_dp_device *dp, bool enable);
-int cdn_dp_set_host_cap(struct cdn_dp_device *dp, u8 lanes, bool flip);
-int cdn_dp_event_config(struct cdn_dp_device *dp);
-u32 cdn_dp_get_event(struct cdn_dp_device *dp);
-int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
-int cdn_dp_get_edid_block(void *dp, u8 *edid,
- unsigned int block, size_t length);
-int cdn_dp_train_link(struct cdn_dp_device *dp);
-int cdn_dp_set_video_status(struct cdn_dp_device *dp, int active);
-int cdn_dp_config_video(struct cdn_dp_device *dp);
-int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
-int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
-int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+enum audio_format {
+ AFMT_I2S = 0,
+ AFMT_SPDIF_INT = 1,
+ AFMT_SPDIF_EXT = 2,
+ AFMT_UNUSED,
+};
+
+struct audio_info {
+ enum audio_format format;
+ int sample_rate;
+ int channels;
+ int sample_width;
+ int connector_type;
+};
+
+enum vic_pxl_encoding_format {
+ PXL_RGB = 0x1,
+ YCBCR_4_4_4 = 0x2,
+ YCBCR_4_2_2 = 0x4,
+ YCBCR_4_2_0 = 0x8,
+ Y_ONLY = 0x10,
+};
+
+struct video_info {
+ bool h_sync_polarity;
+ bool v_sync_polarity;
+ bool interlaced;
+ int color_depth;
+ enum vic_pxl_encoding_format color_fmt;
+};
+
+struct cdns_mhdp_bridge {
+ struct cdns_mhdp_device *mhdp;
+ struct drm_bridge base;
+ int pbn;
+ int8_t stream_id;
+ struct cdns_mhdp_connector *connector;
+ bool is_active;
+};
+
+struct cdns_mhdp_connector {
+ struct drm_connector base;
+ struct cdns_mhdp_bridge *bridge;
+};
+
+struct cdns_mhdp_device {
+ struct device *dev;
+ struct cdns_mhdp_connector connector;
+
+ void __iomem *regs_base;
+ struct reset_control *spdif_rst;
+
+ struct video_info video_info;
+ struct drm_display_mode mode;
+
+ struct platform_device *audio_pdev;
+ struct audio_info audio_info;
+ struct clk *spdif_clk;
+
+ unsigned int fw_version;
+
+ union {
+ struct _dp_data {
+ u32 rate;
+ u8 num_lanes;
+ struct drm_dp_aux aux;
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ } dp;
+ };
+};
+
+int cdns_mhdp_reg_read(struct cdns_mhdp_device *mhdp, u32 addr);
+int cdns_mhdp_reg_write(struct cdns_mhdp_device *mhdp, u32 addr, u32 val);
+void cdns_mhdp_clock_reset(struct cdns_mhdp_device *mhdp);
+void cdns_mhdp_set_fw_clk(struct cdns_mhdp_device *mhdp, unsigned long clk);
+int cdns_mhdp_load_firmware(struct cdns_mhdp_device *mhdp, const u32 *i_mem,
+ u32 i_size, const u32 *d_mem, u32 d_size);
+int cdns_mhdp_set_firmware_active(struct cdns_mhdp_device *mhdp, bool enable);
+int cdns_mhdp_set_host_cap(struct cdns_mhdp_device *mhdp, bool flip);
+int cdns_mhdp_event_config(struct cdns_mhdp_device *mhdp);
+u32 cdns_mhdp_get_event(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_read_hpd(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_dpcd_write(struct cdns_mhdp_device *mhdp, u32 addr, u8 value);
+int cdns_mhdp_dpcd_read(struct cdns_mhdp_device *mhdp,
+ u32 addr, u8 *data, u16 len);
+int cdns_mhdp_get_edid_block(void *mhdp, u8 *edid,
+ unsigned int block, size_t length);
+int cdns_mhdp_train_link(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_set_video_status(struct cdns_mhdp_device *mhdp, int active);
+int cdns_mhdp_config_video(struct cdns_mhdp_device *mhdp);
+int cdns_mhdp_audio_stop(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio);
+int cdns_mhdp_audio_mute(struct cdns_mhdp_device *mhdp, bool enable);
+int cdns_mhdp_audio_config(struct cdns_mhdp_device *mhdp,
+ struct audio_info *audio);
+
#endif /* _CDN_DP_REG_H */
--
2.17.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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox