mbox series

[00/49] DRM driver for Hikey 970

Message ID cover.1597833138.git.mchehab+huawei@kernel.org (mailing list archive)
Headers show
Series DRM driver for Hikey 970 | expand

Message

Mauro Carvalho Chehab Aug. 19, 2020, 11:45 a.m. UTC
This patch series port the out-of-tree driver for Hikey 970 (which
should also support Hikey 960) from the official 96boards tree:

   https://github.com/96boards-hikey/linux/tree/hikey970-v4.9

Based on his history, this driver seems to be originally written
for Kernel 4.4, and was later ported to Kernel 4.9. The original
driver used to depend on ION (from Kernel 4.4) and had its own
implementation for FB dev API.

As I need to preserve the original history (with has patches from
both HiSilicon and from Linaro),  I'm starting from the original
patch applied there. The remaining patches are incremental,
and port this driver to work with upstream Kernel.

This driver doesn't depend on any firmware or on any special
userspace code. It works as-is with both X11 and Wayland.

Yet, I'm submitting it via staging due to the following reasons:

- It depends on the LDO3 power supply, which is provided by
  a regulator driver that it is currently on staging;
- Due to legal reasons, I need to preserve the authorship of
  each one responsbile for each patch. So, I need to start from
  the original patch from Kernel 4.4;
- There are still some problems I need to figure out how to solve:
   - The adv7535 can't get EDID data. Maybe it is a timing issue,
     but it requires more research to be sure about how to solve it;
   - The driver only accept resolutions on a defined list, as there's
     a known bug that this driver may have troubles with random
     resolutions. Probably due to a bug at the pixel clock settings;
   - Sometimes (at least with 1080p), it generates LDI underflow
     errors, which in turn causes the DRM to stop working. That
     happens for example when using gdm on Wayland and
     gnome on X11;
   - Probably related to the previous issue, when the monitor
     suspends due to DPMS, it doesn't return back to life.

So, IMO, the best is to keep it on staging for a while, until those
remaining bugs gets solved.

I added this series, together with the regulator driver and
a few other patches (including a hack to fix a Kernel 5.8 
regression at WiFi ) at:

	https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master


Chen Feng (1):
  staging: hikey9xx: Add hisilicon DRM driver for hikey960/970

John Stultz (1):
  staging: hikey9xx/gpu: port it to work with Kernel v4.9

Liwei Cai (2):
  staging: hikey9xx/gpu: solve tearing issue of display
  staging: hikey9xx/gpu: resolve the performance issue by interrupt
    mechanism

Mauro Carvalho Chehab (38):
  staging: hikey9xx/gpu: get rid of adv7535 fork
  staging: hikey9xx/gpu: rename the Kirin9xx namespace
  staging: hikey9xx/gpu: get rid of kirin9xx_fbdev.c
  staging: hikey9xx/gpu: get rid of some ifdefs
  staging: hikey9xx/gpu: rename the config option for Kirin970
  staging: hikey9xx/gpu: change the includes to reflect upstream
  staging: hikey9xx/gpu: port driver to upstream kAPIs
  staging: hikey9xx/gpu: add a copy of set_reg() function there
  staging: hikey9xx/gpu: get rid of ION headers
  staging: hikey9xx/gpu: add support for using a reserved CMA memory
  staging: hikey9xx/gpu: cleanup encoder attach logic
  staging: hikey9xx/gpu: Change the logic which sets the burst mode
  staging: hikey9xx/gpu: fix the DRM setting logic
  staging: hikey9xx/gpu: do some code cleanups
  staging: hikey9xx/gpu: use default GEM_CMA fops
  staging: hikey9xx/gpu: place vblank enable/disable at the right place
  staging: hikey9xx/gpu: remove an uneeded hack
  staging: hikey9xx/gpu: add a possible implementation for
    atomic_disable
  staging: hikey9xx/gpu: register connector
  staging: hikey9xx/gpu: fix driver name
  staging: hikey9xx/gpu: get rid of iommu_format
  staging: hikey9xx/gpu: re-work the mode validation code
  staging: hikey9xx/gpu: add support for enable/disable ldo3 regulator
  staging: hikey9xx/gpu: add SPMI headers
  staging: hikey9xx/gpu: solve most coding style issues
  staging: hikey9xx/gpu: don't use iommu code
  staging: hikey9xx/gpu: add kirin9xx driver to the building system
  staging: hikey9xx/gpu: get rid of typedefs
  staging: hikey9xx/gpu: get rid of input/output macros
  staging: hikey9xx/gpu: get rid of some unused data
  staging: hikey9xx/gpu: place common definitions at kirin9xx_dpe.h
  staging: hikey9xx/gpu: get rid of DRM_HISI_KIRIN970
  dts: hisilicon: hi3670.dtsi: add I2C settings
  dts: hikey970-pinctrl.dtsi: add missing pinctrl settings
  dt: hisilicon: add support for the PMIC found on Hikey 970
  dts: add support for Hikey 970 DRM
  staging: hikey9xx/gpu: drop kirin9xx_pwm
  dt: display: Add binds for the DPE and DSI controller for Kirin
    960/970

Xiubin Zhang (7):
  staging: hikey9xx/gpu: add support to hikey970 HDMI and panel
  staging: hikey9xx/gpu: Solve SR Cannot Display Problems.
  staging: hikey9xx/gpu: Solve HDMI compatibility Problem.
  staging: hikey9xx/gpu: Support MIPI DSI 3 lanes for hikey970.
  staging: hikey9xx/gpu: Solve SR test reset problem for hikey970.
  staging: hikey9xx/gpu: add debug prints for this driver
  staging: hikey9xx/gpu: Add support 10.1 inch special HDMI displays.

 .../display/hisilicon,hi3660-dpe.yaml         |   99 +
 .../display/hisilicon,hi3660-dsi.yaml         |  102 +
 .../boot/dts/hisilicon/hi3670-hikey970.dts    |   56 +-
 arch/arm64/boot/dts/hisilicon/hi3670.dtsi     |   77 +
 .../boot/dts/hisilicon/hikey970-drm.dtsi      |   93 +
 .../boot/dts/hisilicon/hikey970-pinctrl.dtsi  |  548 +++-
 .../boot/dts/hisilicon/hikey970-pmic.dtsi     |  197 ++
 drivers/staging/hikey9xx/Kconfig              |    3 +
 drivers/staging/hikey9xx/Makefile             |    1 +
 drivers/staging/hikey9xx/gpu/Kconfig          |   22 +
 drivers/staging/hikey9xx/gpu/Makefile         |    9 +
 drivers/staging/hikey9xx/gpu/kirin960_defs.c  |  378 +++
 .../staging/hikey9xx/gpu/kirin960_dpe_reg.h   |  233 ++
 drivers/staging/hikey9xx/gpu/kirin970_defs.c  |  381 +++
 .../staging/hikey9xx/gpu/kirin970_dpe_reg.h   | 1188 ++++++++
 drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h   | 2437 +++++++++++++++++
 .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.c     | 1178 ++++++++
 .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.h     |  286 ++
 .../staging/hikey9xx/gpu/kirin9xx_drm_drv.c   |  368 +++
 .../staging/hikey9xx/gpu/kirin9xx_drm_drv.h   |   57 +
 .../staging/hikey9xx/gpu/kirin9xx_drm_dss.c   | 1063 +++++++
 .../hikey9xx/gpu/kirin9xx_drm_overlay_utils.c | 1005 +++++++
 .../hikey9xx/gpu/kirin9xx_dw_drm_dsi.c        | 2132 ++++++++++++++
 .../hikey9xx/gpu/kirin9xx_dw_dsi_reg.h        |  146 +
 .../staging/hikey9xx/gpu/kirin9xx_fb_panel.h  |  191 ++
 25 files changed, 12229 insertions(+), 21 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dpe.yaml
 create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dsi.yaml
 create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-drm.dtsi
 create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-pmic.dtsi
 create mode 100644 drivers/staging/hikey9xx/gpu/Kconfig
 create mode 100644 drivers/staging/hikey9xx/gpu/Makefile
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_defs.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_dpe_reg.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_defs.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_dpe_reg.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h
 create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_fb_panel.h

Comments

Sam Ravnborg Aug. 19, 2020, 3:21 p.m. UTC | #1
Hi Mauro.

On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:
> This patch series port the out-of-tree driver for Hikey 970 (which
> should also support Hikey 960) from the official 96boards tree:
> 
>    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> 
> Based on his history, this driver seems to be originally written
> for Kernel 4.4, and was later ported to Kernel 4.9. The original
> driver used to depend on ION (from Kernel 4.4) and had its own
> implementation for FB dev API.
> 
> As I need to preserve the original history (with has patches from
> both HiSilicon and from Linaro),  I'm starting from the original
> patch applied there. The remaining patches are incremental,
> and port this driver to work with upstream Kernel.
> 
> This driver doesn't depend on any firmware or on any special
> userspace code. It works as-is with both X11 and Wayland.
> 
> Yet, I'm submitting it via staging due to the following reasons:
> 
> - It depends on the LDO3 power supply, which is provided by
>   a regulator driver that it is currently on staging;
> - Due to legal reasons, I need to preserve the authorship of
>   each one responsbile for each patch. So, I need to start from
>   the original patch from Kernel 4.4;
> - There are still some problems I need to figure out how to solve:
>    - The adv7535 can't get EDID data. Maybe it is a timing issue,
>      but it requires more research to be sure about how to solve it;
>    - The driver only accept resolutions on a defined list, as there's
>      a known bug that this driver may have troubles with random
>      resolutions. Probably due to a bug at the pixel clock settings;
>    - Sometimes (at least with 1080p), it generates LDI underflow
>      errors, which in turn causes the DRM to stop working. That
>      happens for example when using gdm on Wayland and
>      gnome on X11;
>    - Probably related to the previous issue, when the monitor
>      suspends due to DPMS, it doesn't return back to life.
> 
> So, IMO, the best is to keep it on staging for a while, until those
> remaining bugs gets solved.
> 
> I added this series, together with the regulator driver and
> a few other patches (including a hack to fix a Kernel 5.8 
> regression at WiFi ) at:
> 
> 	https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master
> 
> 
> Chen Feng (1):
>   staging: hikey9xx: Add hisilicon DRM driver for hikey960/970
> 
> John Stultz (1):
>   staging: hikey9xx/gpu: port it to work with Kernel v4.9
> 
> Liwei Cai (2):
>   staging: hikey9xx/gpu: solve tearing issue of display
>   staging: hikey9xx/gpu: resolve the performance issue by interrupt
>     mechanism
> 
> Mauro Carvalho Chehab (38):
>   staging: hikey9xx/gpu: get rid of adv7535 fork
Very good - I was in my mind starting a rant why we needed a fork of
this driver, but I see it gets deleted again.

I do acknowledge you need to preserve history and all -
but this patchset is not easy to review.

Could you follow-up with a review-able set of patches as a follow-up
for this?
I spotted some wrong bridge handling in one patch but I do not know if
this got changed in a later patch. And I lost the motivation to go
looking for it.


>   staging: hikey9xx/gpu: rename the Kirin9xx namespace
>   staging: hikey9xx/gpu: get rid of kirin9xx_fbdev.c
>   staging: hikey9xx/gpu: get rid of some ifdefs
>   staging: hikey9xx/gpu: rename the config option for Kirin970
>   staging: hikey9xx/gpu: change the includes to reflect upstream
>   staging: hikey9xx/gpu: port driver to upstream kAPIs
>   staging: hikey9xx/gpu: add a copy of set_reg() function there
>   staging: hikey9xx/gpu: get rid of ION headers
>   staging: hikey9xx/gpu: add support for using a reserved CMA memory
>   staging: hikey9xx/gpu: cleanup encoder attach logic
>   staging: hikey9xx/gpu: Change the logic which sets the burst mode
>   staging: hikey9xx/gpu: fix the DRM setting logic
>   staging: hikey9xx/gpu: do some code cleanups
>   staging: hikey9xx/gpu: use default GEM_CMA fops
>   staging: hikey9xx/gpu: place vblank enable/disable at the right place
>   staging: hikey9xx/gpu: remove an uneeded hack
>   staging: hikey9xx/gpu: add a possible implementation for
>     atomic_disable
>   staging: hikey9xx/gpu: register connector
>   staging: hikey9xx/gpu: fix driver name
>   staging: hikey9xx/gpu: get rid of iommu_format
>   staging: hikey9xx/gpu: re-work the mode validation code
>   staging: hikey9xx/gpu: add support for enable/disable ldo3 regulator
>   staging: hikey9xx/gpu: add SPMI headers
>   staging: hikey9xx/gpu: solve most coding style issues
>   staging: hikey9xx/gpu: don't use iommu code
>   staging: hikey9xx/gpu: add kirin9xx driver to the building system
>   staging: hikey9xx/gpu: get rid of typedefs
>   staging: hikey9xx/gpu: get rid of input/output macros
>   staging: hikey9xx/gpu: get rid of some unused data
>   staging: hikey9xx/gpu: place common definitions at kirin9xx_dpe.h
>   staging: hikey9xx/gpu: get rid of DRM_HISI_KIRIN970
>   dts: hisilicon: hi3670.dtsi: add I2C settings
>   dts: hikey970-pinctrl.dtsi: add missing pinctrl settings
>   dt: hisilicon: add support for the PMIC found on Hikey 970
>   dts: add support for Hikey 970 DRM
>   staging: hikey9xx/gpu: drop kirin9xx_pwm
>   dt: display: Add binds for the DPE and DSI controller for Kirin
>     960/970
> 
> Xiubin Zhang (7):
>   staging: hikey9xx/gpu: add support to hikey970 HDMI and panel
>   staging: hikey9xx/gpu: Solve SR Cannot Display Problems.
>   staging: hikey9xx/gpu: Solve HDMI compatibility Problem.
>   staging: hikey9xx/gpu: Support MIPI DSI 3 lanes for hikey970.
>   staging: hikey9xx/gpu: Solve SR test reset problem for hikey970.
>   staging: hikey9xx/gpu: add debug prints for this driver
>   staging: hikey9xx/gpu: Add support 10.1 inch special HDMI displays.
> 
>  .../display/hisilicon,hi3660-dpe.yaml         |   99 +
>  .../display/hisilicon,hi3660-dsi.yaml         |  102 +
>  .../boot/dts/hisilicon/hi3670-hikey970.dts    |   56 +-
>  arch/arm64/boot/dts/hisilicon/hi3670.dtsi     |   77 +
>  .../boot/dts/hisilicon/hikey970-drm.dtsi      |   93 +
>  .../boot/dts/hisilicon/hikey970-pinctrl.dtsi  |  548 +++-
>  .../boot/dts/hisilicon/hikey970-pmic.dtsi     |  197 ++
>  drivers/staging/hikey9xx/Kconfig              |    3 +
>  drivers/staging/hikey9xx/Makefile             |    1 +
>  drivers/staging/hikey9xx/gpu/Kconfig          |   22 +
>  drivers/staging/hikey9xx/gpu/Makefile         |    9 +
>  drivers/staging/hikey9xx/gpu/kirin960_defs.c  |  378 +++
>  .../staging/hikey9xx/gpu/kirin960_dpe_reg.h   |  233 ++
>  drivers/staging/hikey9xx/gpu/kirin970_defs.c  |  381 +++
>  .../staging/hikey9xx/gpu/kirin970_dpe_reg.h   | 1188 ++++++++
>  drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h   | 2437 +++++++++++++++++
>  .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.c     | 1178 ++++++++
>  .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.h     |  286 ++
>  .../staging/hikey9xx/gpu/kirin9xx_drm_drv.c   |  368 +++
>  .../staging/hikey9xx/gpu/kirin9xx_drm_drv.h   |   57 +
>  .../staging/hikey9xx/gpu/kirin9xx_drm_dss.c   | 1063 +++++++
>  .../hikey9xx/gpu/kirin9xx_drm_overlay_utils.c | 1005 +++++++
>  .../hikey9xx/gpu/kirin9xx_dw_drm_dsi.c        | 2132 ++++++++++++++
>  .../hikey9xx/gpu/kirin9xx_dw_dsi_reg.h        |  146 +
>  .../staging/hikey9xx/gpu/kirin9xx_fb_panel.h  |  191 ++
>  25 files changed, 12229 insertions(+), 21 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dpe.yaml
>  create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dsi.yaml

Patch that intropduce new bindings must following the submitting patches
guidelines for bindings. For once the subject is "dt-bindings: bla bla".

	Sam

>  create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-drm.dtsi
>  create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-pmic.dtsi
>  create mode 100644 drivers/staging/hikey9xx/gpu/Kconfig
>  create mode 100644 drivers/staging/hikey9xx/gpu/Makefile
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_defs.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_dpe_reg.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_defs.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_dpe_reg.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h
>  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_fb_panel.h
> 
> -- 
> 2.26.2
> 
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Laurent Pinchart Aug. 19, 2020, 3:30 p.m. UTC | #2
On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:
> Hi Mauro.
> 
> On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:
> > This patch series port the out-of-tree driver for Hikey 970 (which
> > should also support Hikey 960) from the official 96boards tree:
> > 
> >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > 
> > Based on his history, this driver seems to be originally written
> > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > driver used to depend on ION (from Kernel 4.4) and had its own
> > implementation for FB dev API.
> > 
> > As I need to preserve the original history (with has patches from
> > both HiSilicon and from Linaro),  I'm starting from the original
> > patch applied there. The remaining patches are incremental,
> > and port this driver to work with upstream Kernel.
> > 
> > This driver doesn't depend on any firmware or on any special
> > userspace code. It works as-is with both X11 and Wayland.
> > 
> > Yet, I'm submitting it via staging due to the following reasons:
> > 
> > - It depends on the LDO3 power supply, which is provided by
> >   a regulator driver that it is currently on staging;
> > - Due to legal reasons, I need to preserve the authorship of
> >   each one responsbile for each patch. So, I need to start from
> >   the original patch from Kernel 4.4;
> > - There are still some problems I need to figure out how to solve:
> >    - The adv7535 can't get EDID data. Maybe it is a timing issue,
> >      but it requires more research to be sure about how to solve it;
> >    - The driver only accept resolutions on a defined list, as there's
> >      a known bug that this driver may have troubles with random
> >      resolutions. Probably due to a bug at the pixel clock settings;
> >    - Sometimes (at least with 1080p), it generates LDI underflow
> >      errors, which in turn causes the DRM to stop working. That
> >      happens for example when using gdm on Wayland and
> >      gnome on X11;
> >    - Probably related to the previous issue, when the monitor
> >      suspends due to DPMS, it doesn't return back to life.
> > 
> > So, IMO, the best is to keep it on staging for a while, until those
> > remaining bugs gets solved.
> > 
> > I added this series, together with the regulator driver and
> > a few other patches (including a hack to fix a Kernel 5.8 
> > regression at WiFi ) at:
> > 
> > 	https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master
> > 
> > 
> > Chen Feng (1):
> >   staging: hikey9xx: Add hisilicon DRM driver for hikey960/970
> > 
> > John Stultz (1):
> >   staging: hikey9xx/gpu: port it to work with Kernel v4.9
> > 
> > Liwei Cai (2):
> >   staging: hikey9xx/gpu: solve tearing issue of display
> >   staging: hikey9xx/gpu: resolve the performance issue by interrupt
> >     mechanism
> > 
> > Mauro Carvalho Chehab (38):
> >   staging: hikey9xx/gpu: get rid of adv7535 fork
> Very good - I was in my mind starting a rant why we needed a fork of
> this driver, but I see it gets deleted again.
> 
> I do acknowledge you need to preserve history and all -
> but this patchset is not easy to review.

Why do we need to preserve history ? Adding relevant Signed-off-by and
Co-developed-by should be enough, shouldn't it ? Having a public branch
that contains the history is useful if anyone is interested, but I don't
think it's required in mainline.

> Could you follow-up with a review-able set of patches as a follow-up
> for this?
> I spotted some wrong bridge handling in one patch but I do not know if
> this got changed in a later patch. And I lost the motivation to go
> looking for it.
> 
> >   staging: hikey9xx/gpu: rename the Kirin9xx namespace
> >   staging: hikey9xx/gpu: get rid of kirin9xx_fbdev.c
> >   staging: hikey9xx/gpu: get rid of some ifdefs
> >   staging: hikey9xx/gpu: rename the config option for Kirin970
> >   staging: hikey9xx/gpu: change the includes to reflect upstream
> >   staging: hikey9xx/gpu: port driver to upstream kAPIs
> >   staging: hikey9xx/gpu: add a copy of set_reg() function there
> >   staging: hikey9xx/gpu: get rid of ION headers
> >   staging: hikey9xx/gpu: add support for using a reserved CMA memory
> >   staging: hikey9xx/gpu: cleanup encoder attach logic
> >   staging: hikey9xx/gpu: Change the logic which sets the burst mode
> >   staging: hikey9xx/gpu: fix the DRM setting logic
> >   staging: hikey9xx/gpu: do some code cleanups
> >   staging: hikey9xx/gpu: use default GEM_CMA fops
> >   staging: hikey9xx/gpu: place vblank enable/disable at the right place
> >   staging: hikey9xx/gpu: remove an uneeded hack
> >   staging: hikey9xx/gpu: add a possible implementation for
> >     atomic_disable
> >   staging: hikey9xx/gpu: register connector
> >   staging: hikey9xx/gpu: fix driver name
> >   staging: hikey9xx/gpu: get rid of iommu_format
> >   staging: hikey9xx/gpu: re-work the mode validation code
> >   staging: hikey9xx/gpu: add support for enable/disable ldo3 regulator
> >   staging: hikey9xx/gpu: add SPMI headers
> >   staging: hikey9xx/gpu: solve most coding style issues
> >   staging: hikey9xx/gpu: don't use iommu code
> >   staging: hikey9xx/gpu: add kirin9xx driver to the building system
> >   staging: hikey9xx/gpu: get rid of typedefs
> >   staging: hikey9xx/gpu: get rid of input/output macros
> >   staging: hikey9xx/gpu: get rid of some unused data
> >   staging: hikey9xx/gpu: place common definitions at kirin9xx_dpe.h
> >   staging: hikey9xx/gpu: get rid of DRM_HISI_KIRIN970
> >   dts: hisilicon: hi3670.dtsi: add I2C settings
> >   dts: hikey970-pinctrl.dtsi: add missing pinctrl settings
> >   dt: hisilicon: add support for the PMIC found on Hikey 970
> >   dts: add support for Hikey 970 DRM
> >   staging: hikey9xx/gpu: drop kirin9xx_pwm
> >   dt: display: Add binds for the DPE and DSI controller for Kirin
> >     960/970
> > 
> > Xiubin Zhang (7):
> >   staging: hikey9xx/gpu: add support to hikey970 HDMI and panel
> >   staging: hikey9xx/gpu: Solve SR Cannot Display Problems.
> >   staging: hikey9xx/gpu: Solve HDMI compatibility Problem.
> >   staging: hikey9xx/gpu: Support MIPI DSI 3 lanes for hikey970.
> >   staging: hikey9xx/gpu: Solve SR test reset problem for hikey970.
> >   staging: hikey9xx/gpu: add debug prints for this driver
> >   staging: hikey9xx/gpu: Add support 10.1 inch special HDMI displays.
> > 
> >  .../display/hisilicon,hi3660-dpe.yaml         |   99 +
> >  .../display/hisilicon,hi3660-dsi.yaml         |  102 +
> >  .../boot/dts/hisilicon/hi3670-hikey970.dts    |   56 +-
> >  arch/arm64/boot/dts/hisilicon/hi3670.dtsi     |   77 +
> >  .../boot/dts/hisilicon/hikey970-drm.dtsi      |   93 +
> >  .../boot/dts/hisilicon/hikey970-pinctrl.dtsi  |  548 +++-
> >  .../boot/dts/hisilicon/hikey970-pmic.dtsi     |  197 ++
> >  drivers/staging/hikey9xx/Kconfig              |    3 +
> >  drivers/staging/hikey9xx/Makefile             |    1 +
> >  drivers/staging/hikey9xx/gpu/Kconfig          |   22 +
> >  drivers/staging/hikey9xx/gpu/Makefile         |    9 +
> >  drivers/staging/hikey9xx/gpu/kirin960_defs.c  |  378 +++
> >  .../staging/hikey9xx/gpu/kirin960_dpe_reg.h   |  233 ++
> >  drivers/staging/hikey9xx/gpu/kirin970_defs.c  |  381 +++
> >  .../staging/hikey9xx/gpu/kirin970_dpe_reg.h   | 1188 ++++++++
> >  drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h   | 2437 +++++++++++++++++
> >  .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.c     | 1178 ++++++++
> >  .../hikey9xx/gpu/kirin9xx_drm_dpe_utils.h     |  286 ++
> >  .../staging/hikey9xx/gpu/kirin9xx_drm_drv.c   |  368 +++
> >  .../staging/hikey9xx/gpu/kirin9xx_drm_drv.h   |   57 +
> >  .../staging/hikey9xx/gpu/kirin9xx_drm_dss.c   | 1063 +++++++
> >  .../hikey9xx/gpu/kirin9xx_drm_overlay_utils.c | 1005 +++++++
> >  .../hikey9xx/gpu/kirin9xx_dw_drm_dsi.c        | 2132 ++++++++++++++
> >  .../hikey9xx/gpu/kirin9xx_dw_dsi_reg.h        |  146 +
> >  .../staging/hikey9xx/gpu/kirin9xx_fb_panel.h  |  191 ++
> >  25 files changed, 12229 insertions(+), 21 deletions(-)
> >  create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dpe.yaml
> >  create mode 100644 Documentation/devicetree/bindings/display/hisilicon,hi3660-dsi.yaml
> 
> Patch that intropduce new bindings must following the submitting patches
> guidelines for bindings. For once the subject is "dt-bindings: bla bla".
> 
> >  create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-drm.dtsi
> >  create mode 100644 arch/arm64/boot/dts/hisilicon/hikey970-pmic.dtsi
> >  create mode 100644 drivers/staging/hikey9xx/gpu/Kconfig
> >  create mode 100644 drivers/staging/hikey9xx/gpu/Makefile
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_defs.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin960_dpe_reg.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_defs.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin970_dpe_reg.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h
> >  create mode 100644 drivers/staging/hikey9xx/gpu/kirin9xx_fb_panel.h
John Stultz Aug. 19, 2020, 7:52 p.m. UTC | #3
On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
> On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:
> > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:
> > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > should also support Hikey 960) from the official 96boards tree:
> > >
> > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > >
> > > Based on his history, this driver seems to be originally written
> > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > implementation for FB dev API.
> > >
> > > As I need to preserve the original history (with has patches from
> > > both HiSilicon and from Linaro),  I'm starting from the original
> > > patch applied there. The remaining patches are incremental,
> > > and port this driver to work with upstream Kernel.
> > >
...
> > > - Due to legal reasons, I need to preserve the authorship of
> > >   each one responsbile for each patch. So, I need to start from
> > >   the original patch from Kernel 4.4;
...
> > I do acknowledge you need to preserve history and all -
> > but this patchset is not easy to review.
>
> Why do we need to preserve history ? Adding relevant Signed-off-by and
> Co-developed-by should be enough, shouldn't it ? Having a public branch
> that contains the history is useful if anyone is interested, but I don't
> think it's required in mainline.

Yea. I concur with Laurent here. I'm not sure what legal reasoning you
have on this but preserving the "absolute" history here is actively
detrimental for review and understanding of the patch set.

Preserving Authorship, Signed-off-by lines and adding Co-developed-by
lines should be sufficient to provide both atribution credit and DCO
history.

thanks
-john
John Stultz Aug. 19, 2020, 9:13 p.m. UTC | #4
On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
<mchehab+huawei@kernel.org> wrote:
> Yet, I'm submitting it via staging due to the following reasons:
>
> - It depends on the LDO3 power supply, which is provided by
>   a regulator driver that it is currently on staging;
> - Due to legal reasons, I need to preserve the authorship of
>   each one responsbile for each patch. So, I need to start from
>   the original patch from Kernel 4.4;
> - There are still some problems I need to figure out how to solve:
>    - The adv7535 can't get EDID data. Maybe it is a timing issue,
>      but it requires more research to be sure about how to solve it;

I've seen this on the HiKey960 as well. There is a patch to the
adv7533 driver I have to add a mdelay that seems to consistently
resolve the timing problem.  At some point I mentioned it to one of
the maintainers who seems open to having it added, but it seemed silly
to submit it until there was a upstream driver that needed such a
change.  So I think that patch can be submitted as a follow on to this
(hopefully cleaned up) series.

>    - The driver only accept resolutions on a defined list, as there's
>      a known bug that this driver may have troubles with random
>      resolutions. Probably due to a bug at the pixel clock settings;

So, yes, the SoC clks can't generate proper signals for HDMI
frequencies (apparently it's not an issue for panels). There is a
fixed set that we can get "close enough" that most monitors will work,
but its always a bit iffy (some monitors are strict in what they
take).

On the kirin driver, we were able to do a calculation to figure out if
the generated frequency would be close enough:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c#n615

I suspect we could do something similar for the hikey960/70, but I've
not really had time to dig in deeply there.

Personally, I don't see the allow-list as a problematic short term
solution, and again, not sure its worth pushing to staging for.

>    - Sometimes (at least with 1080p), it generates LDI underflow
>      errors, which in turn causes the DRM to stop working. That
>      happens for example when using gdm on Wayland and
>      gnome on X11;

Interestingly, I've not seen this on HiKey960 (at least with
Android/Surfaceflinger). The original HiKey board does have the
trouble where at 1080p the screen sometimes comes up horizontally
offset due to the LDI underflow, but the patches to address it have
been worse then the problem, so we reverted those.

>    - Probably related to the previous issue, when the monitor
>      suspends due to DPMS, it doesn't return back to life.
>

I don't believe I see this on HiKey960. But if it's the LDI issue on
the 970 that may explain it.


> So, IMO, the best is to keep it on staging for a while, until those
> remaining bugs gets solved.

I'm not sure I see all of these as compelling for pushing it in via
staging. And I suspect in the process of submitting the patches for
review folks may find the cause of some of the problems you list here.


> I added this series, together with the regulator driver and
> a few other patches (including a hack to fix a Kernel 5.8
> regression at WiFi ) at:
>
>         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master
>
>
> Chen Feng (1):
>   staging: hikey9xx: Add hisilicon DRM driver for hikey960/970
>
> John Stultz (1):
>   staging: hikey9xx/gpu: port it to work with Kernel v4.9

Nit: This is a display driver and has little to do with the GPU (other
then it will eventually live in drivers/gpu/drm/...), so I might
suggest using more conventional subject prefix,  "drm: hisilicon:"

thanks
-john
Sam Ravnborg Aug. 19, 2020, 9:25 p.m. UTC | #5
Hi John.

> > So, IMO, the best is to keep it on staging for a while, until those
> > remaining bugs gets solved.
> 
> I'm not sure I see all of these as compelling for pushing it in via
> staging. And I suspect in the process of submitting the patches for
> review folks may find the cause of some of the problems you list here.

There is a tendency to forget drivers in staging, and with the almost
constant refactoring that happens in the drm drivers we would end up
fixing this driver when a bot trigger an error.
So IMO we need very good reasons to go in via staging.

	Sam
John Stultz Aug. 19, 2020, 9:36 p.m. UTC | #6
On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
<mchehab+huawei@kernel.org> wrote:
> So, IMO, the best is to keep it on staging for a while, until those
> remaining bugs gets solved.
>
> I added this series, together with the regulator driver and
> a few other patches (including a hack to fix a Kernel 5.8
> regression at WiFi ) at:
>
>         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master

Sorry, one more small request: Could you create a branch that only has
the DRM driver changes in it?

The reason I ask, is that since the HiKey960 isn't affected by the
majority of the problems you listed as motivation for going through
staging. So if we can validate that your tree works fine on HiKey960,
the series can be cleaned up and submitted properly upstream to enable
that SoC, and the outstanding 970 issues can be worked out afterwards
against mainline.

thanks
-john
John Stultz Aug. 20, 2020, 2:01 a.m. UTC | #7
On Wed, Aug 19, 2020 at 2:36 PM John Stultz <john.stultz@linaro.org> wrote:
>
> On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
> <mchehab+huawei@kernel.org> wrote:
> > So, IMO, the best is to keep it on staging for a while, until those
> > remaining bugs gets solved.
> >
> > I added this series, together with the regulator driver and
> > a few other patches (including a hack to fix a Kernel 5.8
> > regression at WiFi ) at:
> >
> >         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master
>
> Sorry, one more small request: Could you create a branch that only has
> the DRM driver changes in it?
>
> The reason I ask, is that since the HiKey960 isn't affected by the
> majority of the problems you listed as motivation for going through
> staging. So if we can validate that your tree works fine on HiKey960,
> the series can be cleaned up and submitted properly upstream to enable
> that SoC, and the outstanding 970 issues can be worked out afterwards
> against mainline.

Just as a heads up, I tried testing your tree with my HiKey960, and
after fixing the compat string inconsistency, the drivers seem to load
properly. However the drm_hwcomposer seems to have some trouble with
the driver:
01-01 00:12:41.456   345   345 E hwc-drm-display-compositor: Commit
test failed for display 0, FIXME
01-01 00:12:41.456   345   345 E hwc-drm-two: Failed to apply the
frame composition ret=-22
01-01 00:12:41.456   351   351 E HWComposer:
presentAndGetReleaseFences: present failed for display 0: BadParameter
(4)

I'll dig in a bit further as to why, but wanted to give you a heads up.

thanks
-john
John Stultz Aug. 20, 2020, 3:28 a.m. UTC | #8
On Wed, Aug 19, 2020 at 7:01 PM John Stultz <john.stultz@linaro.org> wrote:
>
> On Wed, Aug 19, 2020 at 2:36 PM John Stultz <john.stultz@linaro.org> wrote:
> >
> > On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
> > <mchehab+huawei@kernel.org> wrote:
> > > So, IMO, the best is to keep it on staging for a while, until those
> > > remaining bugs gets solved.
> > >
> > > I added this series, together with the regulator driver and
> > > a few other patches (including a hack to fix a Kernel 5.8
> > > regression at WiFi ) at:
> > >
> > >         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master
> >
> > Sorry, one more small request: Could you create a branch that only has
> > the DRM driver changes in it?
> >
> > The reason I ask, is that since the HiKey960 isn't affected by the
> > majority of the problems you listed as motivation for going through
> > staging. So if we can validate that your tree works fine on HiKey960,
> > the series can be cleaned up and submitted properly upstream to enable
> > that SoC, and the outstanding 970 issues can be worked out afterwards
> > against mainline.
>
> Just as a heads up, I tried testing your tree with my HiKey960, and
> after fixing the compat string inconsistency, the drivers seem to load
> properly. However the drm_hwcomposer seems to have some trouble with
> the driver:
> 01-01 00:12:41.456   345   345 E hwc-drm-display-compositor: Commit
> test failed for display 0, FIXME
> 01-01 00:12:41.456   345   345 E hwc-drm-two: Failed to apply the
> frame composition ret=-22
> 01-01 00:12:41.456   351   351 E HWComposer:
> presentAndGetReleaseFences: present failed for display 0: BadParameter
> (4)
>
> I'll dig in a bit further as to why, but wanted to give you a heads up.

Ok, I've mostly gotten it sorted out:
  - You're missing a few color formats.
  - And I re-discovered a crash that was already fixed in my tree.

I'll send those patches in a few here.

That said even with the patches I've got on top of your series, I
still see a few issues:
1) I'm seeing red-blue swap with your driver.  I need to dig a bit to
see what the difference is, I know gralloc has a config option for
this, and maybe the version of the driver I'm carrying has it wrong?
2) Performance is noticeably worse. Whereas with my tree, I see close
to 60fps (that clk issue we mentioned earlier is why it's not exactly
60) in most tests, but with yours it mostly hovers around 30some fps,
occasionally speeding up to 40 and then back down.

Obviously with some work I suspect we'll be able to sort these out,
but I also do feel that the set you're starting with for upstreaming
is pretty old. The driver I'm carrying was heavily refactored around
5.0 to share code with the existing kirin driver, in the hopes of
making usptreaming easier, and it seems a shame to throw that out and
focus your efforts on the older tree.

But to be fair, I've not had time to upstream the driver myself, and
it's obviously your choice on how you spend your time.  I am really
excited to see your efforts here, regardless of which driver you end
up pushing.

thanks
-john
Mauro Carvalho Chehab Aug. 20, 2020, 6:34 a.m. UTC | #9
Em Wed, 19 Aug 2020 14:13:05 -0700
John Stultz <john.stultz@linaro.org> escreveu:

> On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
> <mchehab+huawei@kernel.org> wrote:
> > Yet, I'm submitting it via staging due to the following reasons:
> >
> > - It depends on the LDO3 power supply, which is provided by
> >   a regulator driver that it is currently on staging;
> > - Due to legal reasons, I need to preserve the authorship of
> >   each one responsbile for each patch. So, I need to start from
> >   the original patch from Kernel 4.4;
> > - There are still some problems I need to figure out how to solve:
> >    - The adv7535 can't get EDID data. Maybe it is a timing issue,
> >      but it requires more research to be sure about how to solve it;  
> 
> I've seen this on the HiKey960 as well. There is a patch to the
> adv7533 driver I have to add a mdelay that seems to consistently
> resolve the timing problem.  At some point I mentioned it to one of
> the maintainers who seems open to having it added, but it seemed silly
> to submit it until there was a upstream driver that needed such a
> change.  So I think that patch can be submitted as a follow on to this
> (hopefully cleaned up) series.

Yeah, I saw that mdelay() patch on your tree. While it should be cheap
to add a mdelay() or udelay_range() there, I'm not sure if this is just a 
timing issue or if something else is missing. The 4.9 driver does some 
extra setups on adv7535 (see the enclosed patch).

I'm wondering if something from that is missing. Btw, IMHO, one
interesting setting on the downstream code is support for colorbar test.
This was helpful when I was making this driver work upstream, as it
could be useful for someone trying to make some other DRM driver using it.

> 
> >    - The driver only accept resolutions on a defined list, as there's
> >      a known bug that this driver may have troubles with random
> >      resolutions. Probably due to a bug at the pixel clock settings;  
> 
> So, yes, the SoC clks can't generate proper signals for HDMI
> frequencies (apparently it's not an issue for panels). There is a
> fixed set that we can get "close enough" that most monitors will work,
> but its always a bit iffy (some monitors are strict in what they
> take).

There is an extra logic for Kirin 620 that seems to be validating
the frequencies that would be set to the clocks. If they're out of
the range, it would return MODE_BAD. I suspect that something similar
would be needed for Kirin 970 and 960. With that regards, 970 is
different from 960. I actually tried to write a patch for it, but
I guess there are still some things missing on my code, for 970.

This patch:

	https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commit/4614415770b33e27a9f15c7dde20895fb750592f

Splits the part of the code which calculates the clk_Hz from the
part which sets it. So, now, dss_calculate_clock() checks the
pixel clock frequency and adjusts it, if needed. 

I have another patch that were checking if the code modified the
clock_Hz at dss_crtc_mode_fixup(). With that, it would be easy to
return MODE_INVALID if something bad happens. However, such
patch didn't work as I would expect, so I ended dropping it.

> On the kirin driver, we were able to do a calculation to figure out if
> the generated frequency would be close enough:
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c#n615
> 
> I suspect we could do something similar for the hikey960/70, but I've
> not really had time to dig in deeply there.

Yeah, I tried something similar to that. Maybe it will work properly
for Hikey 960, but Hikey 970 has a much more complex code to setup
the pixel clock. On 960, just one clock is set:

	clk_set_rate(ctx->dss_pxl0_clk, clk_Hz);

While, on 970, pxl0 is always set to the same frequency (144 MHz),
at least for pixel clocks up to 255 MHz[1], but the code do some
complex calculus to setup PLL7 registers using the clock frequency. 

[1] pixel clocks above 255 MHz will be rejected anyway, as they'll
return MODE_BAD due to the checks if a mode is valid.

> 
> Personally, I don't see the allow-list as a problematic short term
> solution, and again, not sure its worth pushing to staging for.

Yeah, I guess we can live with that for a while.

> 
> >    - Sometimes (at least with 1080p), it generates LDI underflow
> >      errors, which in turn causes the DRM to stop working. That
> >      happens for example when using gdm on Wayland and
> >      gnome on X11;  
> 
> Interestingly, I've not seen this on HiKey960 (at least with
> Android/Surfaceflinger).

Here, it happens all the time when the monitor returns back from DPMS
suspend. It also happens on some specific cases (X11 x Wayland), 
console mode + startx, etc.

It sounds to me that it occurs when the driver tries to setup a new
resolution. Maybe something needs to be disabled before changing res
(and re-enabled afterwards).

> The original HiKey board does have the
> trouble where at 1080p the screen sometimes comes up horizontally
> offset due to the LDI underflow, but the patches to address it have
> been worse then the problem, so we reverted those.

The "visible" effect on my monitor is that the image disappears, 
and the monitor complains that there's an "Invalid Mode" at the HDMI
signal.

> 
> >    - Probably related to the previous issue, when the monitor
> >      suspends due to DPMS, it doesn't return back to life.
> >  
> 
> I don't believe I see this on HiKey960. But if it's the LDI issue on
> the 970 that may explain it.

Yes, when this happens, I get LDI underflow messages.

> > So, IMO, the best is to keep it on staging for a while, until those
> > remaining bugs gets solved.  
> 
> I'm not sure I see all of these as compelling for pushing it in via
> staging. And I suspect in the process of submitting the patches for
> review folks may find the cause of some of the problems you list here.

Yeah, I hope we can get rid of at least part of those during the
review process ;-)

Yet, I'd like to preserve the history. As the enclosed patch shows,
maybe there are some hidden gems at the previously existing code.

I may also have done something wrong when converting the driver.
So, preserving the history seems to be a good idea to me.

One of the advantages of using staging is that the full history
will be preserved at the incremental code at staging, while the
final patch moving the code out of staging will have all the changes
folded together into a single patch.

That makes easier for people to do a final review on it.

Also, some subsystem maintainers don't like the idea of merging the
entire history on their subsystems.

> Nit: This is a display driver and has little to do with the GPU (other
> then it will eventually live in drivers/gpu/drm/...), so I might
> suggest using more conventional subject prefix,  "drm: hisilicon:"

Ah, OK! I'll change the prefix at the next versions of this series.

Thanks,
Mauro

HACK: Some changes from downstream to adv7511 code

diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index a9bb734366ae..1440c08caf42 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -333,6 +333,7 @@ struct adv7511 {
 
 	struct regmap *regmap;
 	struct regmap *regmap_cec;
+	struct regmap *regmap_packet;
 	enum drm_connector_status status;
 	bool powered;
 
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index d59b37a2ae23..c9fbcb40f962 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -325,6 +325,153 @@ static void adv7511_set_link_config(struct adv7511 *adv7511,
 	adv7511->rgb = config->input_colorspace == HDMI_COLORSPACE_RGB;
 }
 
+static void adv7511_dsi_config_tgen(struct adv7511 *adv7511)
+{
+	struct drm_display_mode *mode = &adv7511->curr_mode;
+	unsigned int hsw, hfp, hbp, vsw, vfp, vbp;
+#ifndef TEST_COLORBAR_DISPLAY
+	u8 clock_div_by_lanes[] = { 6, 4, 3 }; /* 2, 3, 4 lanes */
+#endif
+
+	hsw = mode->hsync_end - mode->hsync_start;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hbp = mode->htotal - mode->hsync_end;
+	vsw = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+
+#ifdef TEST_COLORBAR_DISPLAY
+	/* set pixel clock auto mode */
+	regmap_write(adv7511->regmap_cec, 0x16,
+			0x00);
+#else
+	/* set pixel clock divider mode */
+	regmap_write(adv7511->regmap_cec, 0x16,
+			clock_div_by_lanes[adv7511->num_dsi_lanes - 2] << 3);
+#endif
+
+	/* horizontal porch params */
+	regmap_write(adv7511->regmap_cec, 0x28, mode->htotal >> 4);
+	regmap_write(adv7511->regmap_cec, 0x29, (mode->htotal << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x2a, hsw >> 4);
+	regmap_write(adv7511->regmap_cec, 0x2b, (hsw << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x2c, hfp >> 4);
+	regmap_write(adv7511->regmap_cec, 0x2d, (hfp << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x2e, hbp >> 4);
+	regmap_write(adv7511->regmap_cec, 0x2f, (hbp << 4) & 0xff);
+
+	/* vertical porch params */
+	regmap_write(adv7511->regmap_cec, 0x30, mode->vtotal >> 4);
+	regmap_write(adv7511->regmap_cec, 0x31, (mode->vtotal << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x32, vsw >> 4);
+	regmap_write(adv7511->regmap_cec, 0x33, (vsw << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x34, vfp >> 4);
+	regmap_write(adv7511->regmap_cec, 0x35, (vfp << 4) & 0xff);
+	regmap_write(adv7511->regmap_cec, 0x36, vbp >> 4);
+	regmap_write(adv7511->regmap_cec, 0x37, (vbp << 4) & 0xff);
+}
+
+static void hikey970_hack_dpms(struct adv7511 *adv7511)
+{
+
+DRM_ERROR("Calling %s\n", __func__);
+
+	struct mipi_dsi_device *dsi = adv7511->dsi;
+
+	adv7511_dsi_config_tgen(adv7511);
+
+	/* set number of dsi lanes */
+	regmap_write(adv7511->regmap_cec, 0x1c, dsi->lanes << 4);
+
+#ifdef TEST_COLORBAR_DISPLAY
+	/* reset internal timing generator */
+	regmap_write(adv7511->regmap_cec, 0x27, 0xcb);
+	regmap_write(adv7511->regmap_cec, 0x27, 0x8b);
+	regmap_write(adv7511->regmap_cec, 0x27, 0xcb);
+#else
+	/* disable internal timing generator */
+	regmap_write(adv7511->regmap_cec, 0x27, 0x0b);
+#endif
+
+
+	/* enable hdmi */
+	regmap_write(adv7511->regmap_cec, 0x03, 0x89);
+#ifdef TEST_COLORBAR_DISPLAY
+	/*enable test mode */
+	regmap_write(adv7511->regmap_cec, 0x55, 0x80);//display colorbar
+#else
+	/* disable test mode */
+	regmap_write(adv7511->regmap_cec, 0x55, 0x00);
+#endif
+	/* disable test mode */
+	//regmap_write(adv7511->regmap_cec, 0x55, 0x00);
+	/* SPD */
+	{
+		static const unsigned char spd_if[] = {
+			0x83, 0x01, 25, 0x00,
+			'L', 'i', 'n', 'a', 'r', 'o', 0, 0,
+			'9', '6', 'b', 'o', 'a', 'r', 'd', 's',
+			':', 'H', 'i', 'k', 'e', 'y', 0, 0,
+		};
+		int n;
+
+		for (n = 0; n < sizeof(spd_if); n++)
+			regmap_write(adv7511->regmap_packet, n, spd_if[n]);
+
+		/* enable send SPD */
+		regmap_update_bits(adv7511->regmap, 0x40, BIT(6), BIT(6));
+	}
+
+	/* force audio */
+	/* hide Audio infoframe updates */
+	regmap_update_bits(adv7511->regmap, 0x4a, BIT(5), BIT(5));
+
+	/* i2s, internal mclk, mclk-256 */
+	regmap_update_bits(adv7511->regmap, 0x0a, 0x1f, 1);
+	regmap_update_bits(adv7511->regmap, 0x0b, 0xe0, 0);
+	/* enable i2s, use i2s format, sample rate from i2s */
+	regmap_update_bits(adv7511->regmap, 0x0c, 0xc7, BIT(2));
+	/* 16 bit audio */
+	regmap_update_bits(adv7511->regmap, 0x0d, 0xff, 16);
+	/* 16-bit audio */
+	regmap_update_bits(adv7511->regmap, 0x14, 0x0f, 2 << 4);
+	/* 48kHz */
+	regmap_update_bits(adv7511->regmap, 0x15, 0xf0, 2 << 4);
+	/* enable N/CTS, enable Audio sample packets */
+	regmap_update_bits(adv7511->regmap, 0x44, BIT(5), BIT(5));
+	/* N = 6144 */
+	regmap_write(adv7511->regmap, 1, (6144 >> 16) & 0xf);
+	regmap_write(adv7511->regmap, 2, (6144 >> 8) & 0xff);
+	regmap_write(adv7511->regmap, 3, (6144) & 0xff);
+	/* automatic cts */
+	regmap_update_bits(adv7511->regmap, 0x0a, BIT(7), 0);
+	/* enable N/CTS */
+	regmap_update_bits(adv7511->regmap, 0x44, BIT(6), BIT(6));
+	/* not copyrighted */
+	regmap_update_bits(adv7511->regmap, 0x12, BIT(5), BIT(5));
+
+	/* left source */
+	regmap_update_bits(adv7511->regmap, 0x0e, 7 << 3, 0);
+	/* right source */
+	regmap_update_bits(adv7511->regmap, 0x0e, 7 << 0, 1);
+	/* number of channels: sect 4.5.4: set to 0 */
+	regmap_update_bits(adv7511->regmap, 0x73, 7, 1);
+	/* number of channels: sect 4.5.4: set to 0 */
+	regmap_update_bits(adv7511->regmap, 0x73, 0xf0, 1 << 4);
+	/* sample rate: 48kHz */
+	regmap_update_bits(adv7511->regmap, 0x74, 7 << 2, 3 << 2);
+	/* channel allocation reg: sect 4.5.4: set to 0 */
+	regmap_update_bits(adv7511->regmap, 0x76, 0xff, 0);
+	/* enable audio infoframes */
+	regmap_update_bits(adv7511->regmap, 0x44, BIT(3), BIT(3));
+
+	/* AV mute disable */
+	regmap_update_bits(adv7511->regmap, 0x4b, BIT(7) | BIT(6), BIT(7));
+
+	/* use Audio infoframe updated info */
+	regmap_update_bits(adv7511->regmap, 0x4a, BIT(5), 0);
+}
+
 static void __adv7511_power_on(struct adv7511 *adv7511)
 {
 	adv7511->current_edid_segment = -1;
@@ -367,8 +514,10 @@ static void adv7511_power_on(struct adv7511 *adv7511)
 	 */
 	regcache_sync(adv7511->regmap);
 
-	if (adv7511->type == ADV7533 || adv7511->type == ADV7535)
+	if (adv7511->type == ADV7533 || adv7511->type == ADV7535) {
 		adv7533_dsi_power_on(adv7511);
+		hikey970_hack_dpms(adv7511);
+	}
 	adv7511->powered = true;
 }
 
@@ -981,8 +1130,10 @@ static int adv7511_init_regulators(struct adv7511 *adv)
 		adv->supplies[i].supply = supply_names[i];
 
 	ret = devm_regulator_bulk_get(dev, adv->num_supplies, adv->supplies);
-	if (ret)
+	if (ret) {
+		dev_err(dev, "Can't get all regulators\n");
 		return ret;
+	}
 
 	return regulator_bulk_enable(adv->num_supplies, adv->supplies);
 }
@@ -1022,6 +1173,14 @@ static const struct regmap_config adv7511_cec_regmap_config = {
 	.volatile_reg = adv7511_cec_register_volatile,
 };
 
+static const struct regmap_config adv7533_packet_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = 0xff,
+	.cache_type = REGCACHE_RBTREE,
+};
+
 static int adv7511_init_cec_regmap(struct adv7511 *adv)
 {
 	int ret;
@@ -1045,6 +1204,14 @@ static int adv7511_init_cec_regmap(struct adv7511 *adv)
 			goto err;
 	}
 
+	adv->regmap_packet = devm_regmap_init_i2c(adv->i2c_packet,
+						      &adv7533_packet_regmap_config);
+	if (IS_ERR(adv->regmap_packet)) {
+		ret = PTR_ERR(adv->regmap_packet);
+		goto err;
+	}
+
+
 	return 0;
 err:
 	i2c_unregister_device(adv->i2c_cec);
@@ -1168,8 +1335,8 @@ static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
 		return ret;
 
 	ret = adv7511_init_regulators(adv7511);
-	if (ret != -EPROBE_DEFER) {
-		dev_err(dev, "failed to init regulators\n");
+	if (ret && ret != -EPROBE_DEFER) {
+		dev_err(dev, "failed to init regulators: %d\n", ret);
 		return ret;
 	}
Mauro Carvalho Chehab Aug. 20, 2020, 6:40 a.m. UTC | #10
Em Wed, 19 Aug 2020 23:25:51 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> Hi John.
> 
> > > So, IMO, the best is to keep it on staging for a while, until those
> > > remaining bugs gets solved.  
> > 
> > I'm not sure I see all of these as compelling for pushing it in via
> > staging. And I suspect in the process of submitting the patches for
> > review folks may find the cause of some of the problems you list here.  
> 
> There is a tendency to forget drivers in staging, and with the almost
> constant refactoring that happens in the drm drivers we would end up
> fixing this driver when a bot trigger an error.
> So IMO we need very good reasons to go in via staging.

My plan is to have this driver upstream for 5.10, and getting it
out of staging by Kernel 5.11. So, I doubt that the DRM kAPIs would
change a lot during those 2 Kernel cycles.

In any case, I'm also fine to have a final patch at the end of this
series moving it out of staging. The only thing that, IMHO, prevents
it to be out of staging is the LDI underflow. Right now, if no input
events reach the driver, DPMS will put the monitor to suspend, and
it never returns back from life. I bet that, once we discover the
root cause, the fix would be just a couple of lines, but identifying
where the problem is can take a while.

Thanks,
Mauro
Mauro Carvalho Chehab Aug. 20, 2020, 7:03 a.m. UTC | #11
Em Wed, 19 Aug 2020 12:52:06 -0700
John Stultz <john.stultz@linaro.org> escreveu:

> On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
> > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:  
> > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:  
> > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > should also support Hikey 960) from the official 96boards tree:
> > > >
> > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > >
> > > > Based on his history, this driver seems to be originally written
> > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > implementation for FB dev API.
> > > >
> > > > As I need to preserve the original history (with has patches from
> > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > patch applied there. The remaining patches are incremental,
> > > > and port this driver to work with upstream Kernel.
> > > >  
> ...
> > > > - Due to legal reasons, I need to preserve the authorship of
> > > >   each one responsbile for each patch. So, I need to start from
> > > >   the original patch from Kernel 4.4;  
> ...
> > > I do acknowledge you need to preserve history and all -
> > > but this patchset is not easy to review.  
> >
> > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > that contains the history is useful if anyone is interested, but I don't
> > think it's required in mainline.  
> 
> Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> have on this but preserving the "absolute" history here is actively
> detrimental for review and understanding of the patch set.
> 
> Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> lines should be sufficient to provide both atribution credit and DCO
> history.

I'm not convinced that, from legal standpoint, folding things would
be enough. See, there are at least 3 legal systems involved here
among the different patch authors:

	- civil law;
	- common law;
	- customary law + common law.

Merging stuff altogether from different law systems can be problematic,
and trying to discuss this with experienced IP property lawyers will
for sure take a lot of time and efforts. I also bet that different
lawyers will have different opinions, because laws are subject to 
interpretation. With that matter I'm not aware of any court rules 
with regards to folded patches. So, it sounds to me that folding 
patches is something that has yet to be proofed in courts around
the globe.

At least for US legal system, it sounds that the Country of
origin of a patch is relevant, as they have a concept of
"national technology" that can be subject to export regulations.

From my side, I really prefer to play safe and stay out of any such
legal discussions.

Thanks,
Mauro
Michel Dänzer Aug. 20, 2020, 7:21 a.m. UTC | #12
On 2020-08-19 10:48 p.m., Sam Ravnborg wrote:
> Hi Mauro.
> 
> It seems my review comments failed to reach dri-devel - likely due to
> the size of the mail.

Right, some e-mails in this thread went through the dri-devel moderation
queue due to their sizes. This mail of yours did as well, because you
didn't trim the quoted text (hint, hint).
Mauro Carvalho Chehab Aug. 20, 2020, 7:21 a.m. UTC | #13
Hi Sam,

Em Wed, 19 Aug 2020 22:48:00 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> Hi Mauro.
> 
> It seems my review comments failed to reach dri-devel - likely due to
> the size of the mail.

Probably. It reached here properly.

> Link:
> https://lore.kernel.org/linux-devicetree/20200819173558.GA3733@ravnborg.org/
> 
> I my review feedback I refer to checkpatch a few time.
> For drivers/gpu/ we have some nice tooling support.
> One thing our tooling does for us is running checkpatch every time
> we apply a patch.
> 
>     checkpatch -q --emacs --strict --show-types
> 
> So we expect patches to be more or less checkpatch --strict clean.
> 
> "more or less" - as common sense also plays a role.
> And sometimes checkpatch is just wrong.
> 
> Just in case you wondered why checkpatch --strict was requested.

We also use checkpatch --strict for media as a reference,
ignoring the things that would make things worse during review :-)

I'll run checkpatch here and ensure that the coding style
issues will be properly addressed.

Thanks,
Mauro
Mauro Carvalho Chehab Aug. 20, 2020, 7:48 a.m. UTC | #14
Em Wed, 19 Aug 2020 20:28:44 -0700
John Stultz <john.stultz@linaro.org> escreveu:

> On Wed, Aug 19, 2020 at 7:01 PM John Stultz <john.stultz@linaro.org> wrote:
> >
> > On Wed, Aug 19, 2020 at 2:36 PM John Stultz <john.stultz@linaro.org> wrote:  
> > >
> > > On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
> > > <mchehab+huawei@kernel.org> wrote:  
> > > > So, IMO, the best is to keep it on staging for a while, until those
> > > > remaining bugs gets solved.
> > > >
> > > > I added this series, together with the regulator driver and
> > > > a few other patches (including a hack to fix a Kernel 5.8
> > > > regression at WiFi ) at:
> > > >
> > > >         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master  
> > >
> > > Sorry, one more small request: Could you create a branch that only has
> > > the DRM driver changes in it?
> > >
> > > The reason I ask, is that since the HiKey960 isn't affected by the
> > > majority of the problems you listed as motivation for going through
> > > staging. So if we can validate that your tree works fine on HiKey960,
> > > the series can be cleaned up and submitted properly upstream to enable
> > > that SoC, and the outstanding 970 issues can be worked out afterwards
> > > against mainline.  
> >
> > Just as a heads up, I tried testing your tree with my HiKey960, and
> > after fixing the compat string inconsistency, the drivers seem to load
> > properly. However the drm_hwcomposer seems to have some trouble with
> > the driver:
> > 01-01 00:12:41.456   345   345 E hwc-drm-display-compositor: Commit
> > test failed for display 0, FIXME
> > 01-01 00:12:41.456   345   345 E hwc-drm-two: Failed to apply the
> > frame composition ret=-22
> > 01-01 00:12:41.456   351   351 E HWComposer:
> > presentAndGetReleaseFences: present failed for display 0: BadParameter
> > (4)
> >
> > I'll dig in a bit further as to why, but wanted to give you a heads up.  
> 
> Ok, I've mostly gotten it sorted out:
>   - You're missing a few color formats.
>   - And I re-discovered a crash that was already fixed in my tree.
> 
> I'll send those patches in a few here.

Thank you for the patches! I'll test them with Hikey 970 in order to
be sure they're compatible also with such SoC.

> 
> That said even with the patches I've got on top of your series, I
> still see a few issues:

> 1) I'm seeing red-blue swap with your driver.  I need to dig a bit to
> see what the difference is, I know gralloc has a config option for
> this, and maybe the version of the driver I'm carrying has it wrong?

There are some settings at adv7535 with regards to the colormap.
The 4.9 fork of it has some different settings. Maybe it could
be somehow related to it.

I have here a Hikey 960, but didn't test it yet.

> 2) Performance is noticeably worse. Whereas with my tree, I see close
> to 60fps (that clk issue we mentioned earlier is why it's not exactly
> 60) in most tests, but with yours it mostly hovers around 30some fps,
> occasionally speeding up to 40 and then back down.

That's weird, but it could be due to some settings related to CMA, IOMMU
and/or AFBC.

> Obviously with some work I suspect we'll be able to sort these out,
> but I also do feel that the set you're starting with for upstreaming
> is pretty old. The driver I'm carrying was heavily refactored around
> 5.0 to share code with the existing kirin driver, in the hopes of
> making usptreaming easier, and it seems a shame to throw that out and
> focus your efforts on the older tree.
> 
> But to be fair, I've not had time to upstream the driver myself, and
> it's obviously your choice on how you spend your time.  I am really
> excited to see your efforts here, regardless of which driver you end
> up pushing.

On a quick look I've done, besides not having support for Hikey 970,
the code on your tree seems to have less settings than the original
one for Hikey 960. Yet, it should take some time to figure out what
those extra settings are doing.

Once I get this driver merged, and have USB support working fine[1],
my plan is to compare the version from your tree, and compare
with the one I have, in order to cleanup some stuff, check performance
and do some other optimizations.

-

[1] this is a little OOT here: USB has been a challenge. Depending
on the build, I'm getting an NMI interrupt error when the USB3
stack is loaded (usually at dwc3). The error is ESR_ELx_AET_UC.
Unfortunately, it doesn't point to where this error is generated,
making very hard to debug it.

Thanks,
Mauro
Mauro Carvalho Chehab Aug. 20, 2020, 8:04 a.m. UTC | #15
Em Wed, 19 Aug 2020 14:36:52 -0700
John Stultz <john.stultz@linaro.org> escreveu:

> On Wed, Aug 19, 2020 at 4:46 AM Mauro Carvalho Chehab
> <mchehab+huawei@kernel.org> wrote:
> > So, IMO, the best is to keep it on staging for a while, until those
> > remaining bugs gets solved.
> >
> > I added this series, together with the regulator driver and
> > a few other patches (including a hack to fix a Kernel 5.8
> > regression at WiFi ) at:
> >
> >         https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/master  
> 
> Sorry, one more small request: Could you create a branch that only has
> the DRM driver changes in it?
> 
> The reason I ask, is that since the HiKey960 isn't affected by the
> majority of the problems you listed as motivation for going through
> staging. So if we can validate that your tree works fine on HiKey960,
> the series can be cleaned up and submitted properly upstream to enable
> that SoC, and the outstanding 970 issues can be worked out afterwards
> against mainline.

Well, if support for HiKey 960 is OK, I guess what we can do is to not 
push the patch with DT bindings for hikey970. We should probably fix
the color swap thing at the driver first.

From my side, provided that the history is preserved, I don't mind
if this is merged:

- via staging tree;
- at dri-devel tree;
- or having a the historic patchsets merged at /staging, with
  a follow up patch moving it from staging/ into /gpu/drm/.

Thanks,
Mauro
Laurent Pinchart Aug. 20, 2020, 10:02 a.m. UTC | #16
Hi Mauro,

On Thu, Aug 20, 2020 at 09:03:26AM +0200, Mauro Carvalho Chehab wrote:
> Em Wed, 19 Aug 2020 12:52:06 -0700 John Stultz escreveu:
> > On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart wrote:
> > > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:  
> > > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:  
> > > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > > should also support Hikey 960) from the official 96boards tree:
> > > > >
> > > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > > >
> > > > > Based on his history, this driver seems to be originally written
> > > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > > implementation for FB dev API.
> > > > >
> > > > > As I need to preserve the original history (with has patches from
> > > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > > patch applied there. The remaining patches are incremental,
> > > > > and port this driver to work with upstream Kernel.
> > > > >  
> > ...
> > > > > - Due to legal reasons, I need to preserve the authorship of
> > > > >   each one responsbile for each patch. So, I need to start from
> > > > >   the original patch from Kernel 4.4;  
> > ...
> > > > I do acknowledge you need to preserve history and all -
> > > > but this patchset is not easy to review.  
> > >
> > > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > > that contains the history is useful if anyone is interested, but I don't
> > > think it's required in mainline.  
> > 
> > Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> > have on this but preserving the "absolute" history here is actively
> > detrimental for review and understanding of the patch set.
> > 
> > Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> > lines should be sufficient to provide both atribution credit and DCO
> > history.
> 
> I'm not convinced that, from legal standpoint, folding things would
> be enough. See, there are at least 3 legal systems involved here
> among the different patch authors:
> 
> 	- civil law;
> 	- common law;
> 	- customary law + common law.
> 
> Merging stuff altogether from different law systems can be problematic,
> and trying to discuss this with experienced IP property lawyers will
> for sure take a lot of time and efforts. I also bet that different
> lawyers will have different opinions, because laws are subject to 
> interpretation. With that matter I'm not aware of any court rules 
> with regards to folded patches. So, it sounds to me that folding 
> patches is something that has yet to be proofed in courts around
> the globe.
> 
> At least for US legal system, it sounds that the Country of
> origin of a patch is relevant, as they have a concept of
> "national technology" that can be subject to export regulations.
> 
> From my side, I really prefer to play safe and stay out of any such
> legal discussions.

Let's be serious for a moment. If you think there are legal issues in
taking GPL-v2.0-only patches and squashing them while retaining
authorship information through tags, the Linux kernel if *full* of that.
You also routinely modify patches that you commit to the media subsystem
to fix "small issues".

The country of origin argument makes no sense either, the kernel code
base if full of code coming from pretty much all country on the planet.

Keeping the patches separate make this hard to review. Please squash
them.
Mauro Carvalho Chehab Aug. 20, 2020, 2:06 p.m. UTC | #17
Em Wed, 19 Aug 2020 19:35:58 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

I'm already handling the other comments from your review (I'll send a
more complete comment about them after finishing), but I have a doubt
what you meant about this:

> +static int kirin_drm_bind(struct device *dev)
> > +{
> > +	struct drm_driver *driver = &kirin_drm_driver;
> > +	struct drm_device *drm_dev;
> > +	struct kirin_drm_private *priv;
> > +	int ret;
> > +
> > +	drm_dev = drm_dev_alloc(driver, dev);
> > +	if (!drm_dev)
> > +		return -ENOMEM;
> > +
> > +	ret = kirin_drm_kms_init(drm_dev);
> > +	if (ret)
> > +		goto err_drm_dev_unref;
> > +
> > +	ret = drm_dev_register(drm_dev, 0);  
> There is better ways to do this. See drm_drv.c for the code example.

Not sure if I understood your comment here. The drm_drv.c example also calls 
drm_dev_register().

The only difference is that it calls it inside driver_probe(), while
on this driver, it uses:

	static const struct component_master_ops kirin_drm_ops = {
		.bind = kirin_drm_bind,
		.unbind = kirin_drm_unbind,
	};

	static int kirin_drm_platform_probe(struct platform_device *pdev)
	{
...
		drm_of_component_match_add(dev, &match, compare_of, remote);
...
	return component_master_add_with_match(dev, &kirin_drm_ops, match);
}

Are you meaning that I should get rid of this component API and call
kirin_drm_bind() from kirin_drm_platform_probe()?

Thanks,
Mauro
Sam Ravnborg Aug. 20, 2020, 2:48 p.m. UTC | #18
Hi Mauro.

On Thu, Aug 20, 2020 at 04:06:49PM +0200, Mauro Carvalho Chehab wrote:
> Em Wed, 19 Aug 2020 19:35:58 +0200
> Sam Ravnborg <sam@ravnborg.org> escreveu:
> 
> I'm already handling the other comments from your review (I'll send a
> more complete comment about them after finishing),
If you get back only on things you do not understand or do not agree on
that would be fine. The rest should be visible in the changelog on the
updated patch - no need to do extra work here.

> but I have a doubt what you meant about this:
> 
> > +static int kirin_drm_bind(struct device *dev)
> > > +{
> > > +	struct drm_driver *driver = &kirin_drm_driver;
> > > +	struct drm_device *drm_dev;
> > > +	struct kirin_drm_private *priv;
> > > +	int ret;
> > > +
> > > +	drm_dev = drm_dev_alloc(driver, dev);
> > > +	if (!drm_dev)
> > > +		return -ENOMEM;
> > > +
> > > +	ret = kirin_drm_kms_init(drm_dev);
> > > +	if (ret)
> > > +		goto err_drm_dev_unref;
> > > +
> > > +	ret = drm_dev_register(drm_dev, 0);  
> > There is better ways to do this. See drm_drv.c for the code example.
> 
> Not sure if I understood your comment here. The drm_drv.c example also calls 
> drm_dev_register().

This is indeed not obvious from my comments but what I wnated to say is
that the driver should embed drm_device in some struct,
maybe in "struct kirin_drm_private".

This should also be part of the referenced example.

I hope this clarifies it.

	Sam

> 
> The only difference is that it calls it inside driver_probe(), while
> on this driver, it uses:
> 
> 	static const struct component_master_ops kirin_drm_ops = {
> 		.bind = kirin_drm_bind,
> 		.unbind = kirin_drm_unbind,
> 	};
> 
> 	static int kirin_drm_platform_probe(struct platform_device *pdev)
> 	{
> ...
> 		drm_of_component_match_add(dev, &match, compare_of, remote);
> ...
> 	return component_master_add_with_match(dev, &kirin_drm_ops, match);
> }
> 
> Are you meaning that I should get rid of this component API and call
> kirin_drm_bind() from kirin_drm_platform_probe()?
> 
> Thanks,
> Mauro
Mauro Carvalho Chehab Aug. 20, 2020, 3:13 p.m. UTC | #19
Em Thu, 20 Aug 2020 16:48:08 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> Hi Mauro.
> 
> On Thu, Aug 20, 2020 at 04:06:49PM +0200, Mauro Carvalho Chehab wrote:
> > Em Wed, 19 Aug 2020 19:35:58 +0200
> > Sam Ravnborg <sam@ravnborg.org> escreveu:
> > 
> > I'm already handling the other comments from your review (I'll send a
> > more complete comment about them after finishing),  
> If you get back only on things you do not understand or do not agree on
> that would be fine. The rest should be visible in the changelog on the
> updated patch - no need to do extra work here.
> 
> > but I have a doubt what you meant about this:
> >   
> > > +static int kirin_drm_bind(struct device *dev)  
> > > > +{
> > > > +	struct drm_driver *driver = &kirin_drm_driver;
> > > > +	struct drm_device *drm_dev;
> > > > +	struct kirin_drm_private *priv;
> > > > +	int ret;
> > > > +
> > > > +	drm_dev = drm_dev_alloc(driver, dev);
> > > > +	if (!drm_dev)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	ret = kirin_drm_kms_init(drm_dev);
> > > > +	if (ret)
> > > > +		goto err_drm_dev_unref;
> > > > +
> > > > +	ret = drm_dev_register(drm_dev, 0);    
> > > There is better ways to do this. See drm_drv.c for the code example.  
> > 
> > Not sure if I understood your comment here. The drm_drv.c example also calls 
> > drm_dev_register().  
> 
> This is indeed not obvious from my comments but what I wnated to say is
> that the driver should embed drm_device in some struct,
> maybe in "struct kirin_drm_private".
> 
> This should also be part of the referenced example.
> 
> I hope this clarifies it.

Yeah. I was already doing those changes ;-) 

Something like the enclosed patch, right?

Btw, I'm not sure if the error handling part is ok, as I didn't check
what the devm stuff does at the subsystem. 

-

On a separate question, I was unable to use the helper macros,
as it sounds that there's no macro with this:

	.dumb_create		= drm_gem_cma_dumb_create_internal,

The existing DRM_GEM_CMA_VMAP_DRIVER_OPS uses, instead
drm_gem_cma_dumb_create(). I'm not sure if this driver can use
such function instead.

Thanks,
Mauro

staging: hikey9xx/gpu: use drm_managed interface
    
Use a more modern design for the driver binding logic by
using drm_managed and getting rid of drm->dev_private.
    
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
index c7736f4d74b7..600c89605cc0 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
@@ -29,12 +29,13 @@
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
+#include <drm/drm_managed.h>
 
 #include "kirin9xx_drm_drv.h"
 
 static int kirin_drm_kms_cleanup(struct drm_device *dev)
 {
-	struct kirin_drm_private *priv = dev->dev_private;
+	struct kirin_drm_private *priv = to_drm_private(dev);
 	static struct kirin_dc_ops const *dc_ops;
 
 	if (priv->fbdev)
@@ -45,15 +46,13 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev)
 	drm_kms_helper_poll_fini(dev);
 	dc_ops->cleanup(dev);
 	drm_mode_config_cleanup(dev);
-	devm_kfree(dev->dev, priv);
-	dev->dev_private = NULL;
 
 	return 0;
 }
 
 static void kirin_fbdev_output_poll_changed(struct drm_device *dev)
 {
-	struct kirin_drm_private *priv = dev->dev_private;
+	struct kirin_drm_private *priv = to_drm_private(dev);
 
 	dsi_set_output_client(dev);
 
@@ -69,18 +68,20 @@ static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
 
 static int kirin_drm_kms_init(struct drm_device *dev)
 {
-	struct kirin_drm_private *priv = dev->dev_private;
+	struct kirin_drm_private *priv = to_drm_private(dev);
 	static struct kirin_dc_ops const *dc_ops;
 	int ret;
 
-	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
+	priv = drmm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
 	dev->dev_private = priv;
 	dev_set_drvdata(dev->dev, dev);
 
-	drm_mode_config_init(dev);
+	ret = drmm_mode_config_init(dev);
+	if (ret)
+		return ret;
 
 	dev->mode_config.min_width = 0;
 	dev->mode_config.min_height = 0;
@@ -94,20 +95,20 @@ static int kirin_drm_kms_init(struct drm_device *dev)
 	dc_ops = of_device_get_match_data(dev->dev);
 	ret = dc_ops->init(dev);
 	if (ret)
-		goto err_mode_config_cleanup;
+		return ret;
 
 	/* bind and init sub drivers */
 	ret = component_bind_all(dev->dev, dev);
 	if (ret) {
 		DRM_ERROR("failed to bind all component.\n");
-		goto err_dc_cleanup;
+		return ret;
 	}
 
 	/* vblank init */
 	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
 	if (ret) {
 		DRM_ERROR("failed to initialize vblank.\n");
-		goto err_unbind_all;
+		return ret;
 	}
 	/* with irq_enabled = true, we can use the vblank feature. */
 	dev->irq_enabled = true;
@@ -119,28 +120,10 @@ static int kirin_drm_kms_init(struct drm_device *dev)
 	drm_kms_helper_poll_init(dev);
 
 	return 0;
-
-err_unbind_all:
-	component_unbind_all(dev->dev, dev);
-err_dc_cleanup:
-	dc_ops->cleanup(dev);
-err_mode_config_cleanup:
-	drm_mode_config_cleanup(dev);
-	devm_kfree(dev->dev, priv);
-	dev->dev_private = NULL;
-
-	return ret;
 }
 
 DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops);
 
-static int kirin_gem_cma_dumb_create(struct drm_file *file,
-				     struct drm_device *dev,
-				     struct drm_mode_create_dumb *args)
-{
-	return drm_gem_cma_dumb_create_internal(file, dev, args);
-}
-
 static int kirin_drm_connectors_register(struct drm_device *dev)
 {
 	struct drm_connector_list_iter conn_iter;
@@ -176,11 +159,11 @@ static int kirin_drm_connectors_register(struct drm_device *dev)
 static struct drm_driver kirin_drm_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
 				  DRIVER_ATOMIC | DRIVER_RENDER,
-	.fops				= &kirin_drm_fops,
+	.fops			= &kirin_drm_fops,
 
 	.gem_free_object	= drm_gem_cma_free_object,
 	.gem_vm_ops		= &drm_gem_cma_vm_ops,
-	.dumb_create		= kirin_gem_cma_dumb_create,
+	.dumb_create		= drm_gem_cma_dumb_create_internal,
 
 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
@@ -207,42 +190,48 @@ static int compare_of(struct device *dev, void *data)
 static int kirin_drm_bind(struct device *dev)
 {
 	struct drm_driver *driver = &kirin_drm_driver;
-	struct drm_device *drm_dev;
 	struct kirin_drm_private *priv;
+	struct drm_device *drm;
 	int ret;
 
-	drm_dev = drm_dev_alloc(driver, dev);
-	if (!drm_dev)
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
 		return -ENOMEM;
 
-	ret = kirin_drm_kms_init(drm_dev);
+	drm = &priv->drm;
+
+	ret = devm_drm_dev_init(dev, drm, driver);
+	if (ret) {
+		kfree(priv);
+		return ret;
+	}
+	drmm_add_final_kfree(drm, priv);
+
+	ret = kirin_drm_kms_init(drm);
 	if (ret)
-		goto err_drm_dev_unref;
+		return ret;
 
-	ret = drm_dev_register(drm_dev, 0);
+	ret = drm_dev_register(drm, 0);
 	if (ret)
-		goto err_kms_cleanup;
+		return ret;
 
-	drm_fbdev_generic_setup(drm_dev, 0);
-	priv = drm_dev->dev_private;
+	drm_fbdev_generic_setup(drm, 0);
 
 	/* connectors should be registered after drm device register */
-	ret = kirin_drm_connectors_register(drm_dev);
+	ret = kirin_drm_connectors_register(drm);
 	if (ret)
 		goto err_drm_dev_unregister;
 
 	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
 		 driver->name, driver->major, driver->minor, driver->patchlevel,
-		 driver->date, drm_dev->primary->index);
+		 driver->date, drm->primary->index);
 
 	return 0;
 
 err_drm_dev_unregister:
-	drm_dev_unregister(drm_dev);
-err_kms_cleanup:
-	kirin_drm_kms_cleanup(drm_dev);
-err_drm_dev_unref:
-	drm_dev_put(drm_dev);
+	drm_dev_unregister(drm);
+	kirin_drm_kms_cleanup(drm);
+	drm_dev_put(drm);
 
 	return ret;
 }
@@ -252,6 +241,7 @@ static void kirin_drm_unbind(struct device *dev)
 	struct drm_device *drm_dev = dev_get_drvdata(dev);
 
 	drm_dev_unregister(drm_dev);
+	drm_atomic_helper_shutdown(drm_dev);
 	kirin_drm_kms_cleanup(drm_dev);
 	drm_dev_put(drm_dev);
 }
diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
index 58f6fc7be347..09255d136c54 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
@@ -31,6 +31,7 @@ struct kirin_dc_ops {
 };
 
 struct kirin_drm_private {
+	struct drm_device drm;
 	struct drm_fb_helper *fbdev;
 	struct drm_crtc *crtc[MAX_CRTC];
 };
@@ -44,4 +45,6 @@ extern const struct kirin_dc_ops kirin960_dss_dc_ops;
 extern const struct kirin_dc_ops kirin970_dss_dc_ops;
 void dsi_set_output_client(struct drm_device *dev);
 
+#define to_drm_private(d) container_of(d, struct kirin_drm_private, drm)
+
 #endif /* __KIRIN_DRM_DRV_H__ */
Sam Ravnborg Aug. 20, 2020, 3:36 p.m. UTC | #20
Hi Mauro.

Quick feedback below.

	Sam

On Thu, Aug 20, 2020 at 05:13:22PM +0200, Mauro Carvalho Chehab wrote:
> Em Thu, 20 Aug 2020 16:48:08 +0200
> Sam Ravnborg <sam@ravnborg.org> escreveu:
> 
> > Hi Mauro.
> > 
> > On Thu, Aug 20, 2020 at 04:06:49PM +0200, Mauro Carvalho Chehab wrote:
> > > Em Wed, 19 Aug 2020 19:35:58 +0200
> > > Sam Ravnborg <sam@ravnborg.org> escreveu:
> > > 
> > > I'm already handling the other comments from your review (I'll send a
> > > more complete comment about them after finishing),  
> > If you get back only on things you do not understand or do not agree on
> > that would be fine. The rest should be visible in the changelog on the
> > updated patch - no need to do extra work here.
> > 
> > > but I have a doubt what you meant about this:
> > >   
> > > > +static int kirin_drm_bind(struct device *dev)  
> > > > > +{
> > > > > +	struct drm_driver *driver = &kirin_drm_driver;
> > > > > +	struct drm_device *drm_dev;
> > > > > +	struct kirin_drm_private *priv;
> > > > > +	int ret;
> > > > > +
> > > > > +	drm_dev = drm_dev_alloc(driver, dev);
> > > > > +	if (!drm_dev)
> > > > > +		return -ENOMEM;
> > > > > +
> > > > > +	ret = kirin_drm_kms_init(drm_dev);
> > > > > +	if (ret)
> > > > > +		goto err_drm_dev_unref;
> > > > > +
> > > > > +	ret = drm_dev_register(drm_dev, 0);    
> > > > There is better ways to do this. See drm_drv.c for the code example.  
> > > 
> > > Not sure if I understood your comment here. The drm_drv.c example also calls 
> > > drm_dev_register().  
> > 
> > This is indeed not obvious from my comments but what I wnated to say is
> > that the driver should embed drm_device in some struct,
> > maybe in "struct kirin_drm_private".
> > 
> > This should also be part of the referenced example.
> > 
> > I hope this clarifies it.
> 
> Yeah. I was already doing those changes ;-) 
> 
> Something like the enclosed patch, right?
> 
> Btw, I'm not sure if the error handling part is ok, as I didn't check
> what the devm stuff does at the subsystem. 
> 
> -
> 
> On a separate question, I was unable to use the helper macros,
> as it sounds that there's no macro with this:
> 
> 	.dumb_create		= drm_gem_cma_dumb_create_internal,
> 
> The existing DRM_GEM_CMA_VMAP_DRIVER_OPS uses, instead
> drm_gem_cma_dumb_create(). I'm not sure if this driver can use
> such function instead.

From the documentation of drm_gem_cma_dumb_create_internal:
* It should not be used directly
* as their &drm_driver.dumb_create callback.

I would expect drm_gem_cma_dumb_create() to be the right choice.
So you can go ahead with DRM_GEM_CMA_VMAP_DRIVER_OPS

(I hope I am right, not the are	i know much about)


> staging: hikey9xx/gpu: use drm_managed interface
>     
> Use a more modern design for the driver binding logic by
> using drm_managed and getting rid of drm->dev_private.
>     
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> 
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> index c7736f4d74b7..600c89605cc0 100644
> --- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> @@ -29,12 +29,13 @@
>  #include <drm/drm_of.h>
>  #include <drm/drm_probe_helper.h>
>  #include <drm/drm_vblank.h>
> +#include <drm/drm_managed.h>
>  
>  #include "kirin9xx_drm_drv.h"
>  
>  static int kirin_drm_kms_cleanup(struct drm_device *dev)
>  {
> -	struct kirin_drm_private *priv = dev->dev_private;
> +	struct kirin_drm_private *priv = to_drm_private(dev);
>  	static struct kirin_dc_ops const *dc_ops;
>  
>  	if (priv->fbdev)
> @@ -45,15 +46,13 @@ static int kirin_drm_kms_cleanup(struct drm_device *dev)
>  	drm_kms_helper_poll_fini(dev);
>  	dc_ops->cleanup(dev);

>  	drm_mode_config_cleanup(dev);
This should also be gone when you are using
drmm_mode_config_init()

> -	devm_kfree(dev->dev, priv);
> -	dev->dev_private = NULL;
>  
>  	return 0;
>  }
>  
>  static void kirin_fbdev_output_poll_changed(struct drm_device *dev)
>  {
> -	struct kirin_drm_private *priv = dev->dev_private;
> +	struct kirin_drm_private *priv = to_drm_private(dev);
>  
>  	dsi_set_output_client(dev);
>  
> @@ -69,18 +68,20 @@ static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
>  
>  static int kirin_drm_kms_init(struct drm_device *dev)
>  {
> -	struct kirin_drm_private *priv = dev->dev_private;
> +	struct kirin_drm_private *priv = to_drm_private(dev);
It is assigned a few lines later.

>  	static struct kirin_dc_ops const *dc_ops;
>  	int ret;
>  
> -	priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
> +	priv = drmm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>  	if (!priv)
>  		return -ENOMEM;

OK, I am confused here.
This code allocates a struct kirin_drm_private.
But the calling function does the same.
What am I missing here? Coffee?

>  
>  	dev->dev_private = priv;
>  	dev_set_drvdata(dev->dev, dev);
>  
> -	drm_mode_config_init(dev);
> +	ret = drmm_mode_config_init(dev);
> +	if (ret)
> +		return ret;
>  
>  	dev->mode_config.min_width = 0;
>  	dev->mode_config.min_height = 0;
> @@ -94,20 +95,20 @@ static int kirin_drm_kms_init(struct drm_device *dev)
>  	dc_ops = of_device_get_match_data(dev->dev);
>  	ret = dc_ops->init(dev);
>  	if (ret)
> -		goto err_mode_config_cleanup;
> +		return ret;
>  
>  	/* bind and init sub drivers */
>  	ret = component_bind_all(dev->dev, dev);
>  	if (ret) {
>  		DRM_ERROR("failed to bind all component.\n");
> -		goto err_dc_cleanup;
> +		return ret;
>  	}
>  
>  	/* vblank init */
>  	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
>  	if (ret) {
>  		DRM_ERROR("failed to initialize vblank.\n");
> -		goto err_unbind_all;
> +		return ret;
>  	}
>  	/* with irq_enabled = true, we can use the vblank feature. */
>  	dev->irq_enabled = true;
> @@ -119,28 +120,10 @@ static int kirin_drm_kms_init(struct drm_device *dev)
>  	drm_kms_helper_poll_init(dev);
>  
>  	return 0;
> -
> -err_unbind_all:
> -	component_unbind_all(dev->dev, dev);
> -err_dc_cleanup:
> -	dc_ops->cleanup(dev);
> -err_mode_config_cleanup:
> -	drm_mode_config_cleanup(dev);
> -	devm_kfree(dev->dev, priv);
> -	dev->dev_private = NULL;
> -
> -	return ret;
>  }
>  
>  DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops);
>  
> -static int kirin_gem_cma_dumb_create(struct drm_file *file,
> -				     struct drm_device *dev,
> -				     struct drm_mode_create_dumb *args)
> -{
> -	return drm_gem_cma_dumb_create_internal(file, dev, args);
> -}
> -
>  static int kirin_drm_connectors_register(struct drm_device *dev)
>  {
>  	struct drm_connector_list_iter conn_iter;
> @@ -176,11 +159,11 @@ static int kirin_drm_connectors_register(struct drm_device *dev)
>  static struct drm_driver kirin_drm_driver = {
>  	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
>  				  DRIVER_ATOMIC | DRIVER_RENDER,
> -	.fops				= &kirin_drm_fops,
> +	.fops			= &kirin_drm_fops,
>  
>  	.gem_free_object	= drm_gem_cma_free_object,
>  	.gem_vm_ops		= &drm_gem_cma_vm_ops,
> -	.dumb_create		= kirin_gem_cma_dumb_create,
> +	.dumb_create		= drm_gem_cma_dumb_create_internal,
>  
>  	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd,
>  	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle,
> @@ -207,42 +190,48 @@ static int compare_of(struct device *dev, void *data)
>  static int kirin_drm_bind(struct device *dev)
>  {
>  	struct drm_driver *driver = &kirin_drm_driver;
> -	struct drm_device *drm_dev;
>  	struct kirin_drm_private *priv;
> +	struct drm_device *drm;
>  	int ret;
>  
> -	drm_dev = drm_dev_alloc(driver, dev);
> -	if (!drm_dev)
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
>  		return -ENOMEM;
>  
> -	ret = kirin_drm_kms_init(drm_dev);
> +	drm = &priv->drm;
> +
> +	ret = devm_drm_dev_init(dev, drm, driver);
> +	if (ret) {
> +		kfree(priv);
> +		return ret;
> +	}
> +	drmm_add_final_kfree(drm, priv);
> +
> +	ret = kirin_drm_kms_init(drm);
>  	if (ret)
> -		goto err_drm_dev_unref;
> +		return ret;
>  
> -	ret = drm_dev_register(drm_dev, 0);
> +	ret = drm_dev_register(drm, 0);
>  	if (ret)
> -		goto err_kms_cleanup;
> +		return ret;
>  
> -	drm_fbdev_generic_setup(drm_dev, 0);
> -	priv = drm_dev->dev_private;
> +	drm_fbdev_generic_setup(drm, 0);
>  
>  	/* connectors should be registered after drm device register */
> -	ret = kirin_drm_connectors_register(drm_dev);
> +	ret = kirin_drm_connectors_register(drm);
>  	if (ret)
>  		goto err_drm_dev_unregister;
>  
>  	DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n",
>  		 driver->name, driver->major, driver->minor, driver->patchlevel,
> -		 driver->date, drm_dev->primary->index);
> +		 driver->date, drm->primary->index);
>  
>  	return 0;
>  
>  err_drm_dev_unregister:
> -	drm_dev_unregister(drm_dev);
> -err_kms_cleanup:
> -	kirin_drm_kms_cleanup(drm_dev);
> -err_drm_dev_unref:
> -	drm_dev_put(drm_dev);
> +	drm_dev_unregister(drm);
> +	kirin_drm_kms_cleanup(drm);
> +	drm_dev_put(drm);
>  
>  	return ret;
>  }
> @@ -252,6 +241,7 @@ static void kirin_drm_unbind(struct device *dev)
>  	struct drm_device *drm_dev = dev_get_drvdata(dev);
>  
>  	drm_dev_unregister(drm_dev);
I think this is not needed. But the component framework confuses me.

> +	drm_atomic_helper_shutdown(drm_dev);
>  	kirin_drm_kms_cleanup(drm_dev);
>  	drm_dev_put(drm_dev);
This should likewise go I think.

>  }
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> index 58f6fc7be347..09255d136c54 100644
> --- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> @@ -31,6 +31,7 @@ struct kirin_dc_ops {
>  };
>  
>  struct kirin_drm_private {
> +	struct drm_device drm;
>  	struct drm_fb_helper *fbdev;
>  	struct drm_crtc *crtc[MAX_CRTC];
>  };
> @@ -44,4 +45,6 @@ extern const struct kirin_dc_ops kirin960_dss_dc_ops;
>  extern const struct kirin_dc_ops kirin970_dss_dc_ops;
>  void dsi_set_output_client(struct drm_device *dev);
>  
> +#define to_drm_private(d) container_of(d, struct kirin_drm_private, drm)
> +
>  #endif /* __KIRIN_DRM_DRV_H__ */
Mauro Carvalho Chehab Aug. 21, 2020, 1:37 p.m. UTC | #21
Hi Sam,

Em Wed, 19 Aug 2020 19:35:58 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> > +	ret = drm_bridge_attach(encoder, bridge, NULL, 0);  
> The bridge should be attached with the falg that tell the bridge NOT to
> create a connector.
> 
> The display driver shall created the connector.
> 
> Please see how other drivers do this (but most driver uses the old
> pattern so so look for drm_bridge_attach() with the flag argument.

Not sure if I got what should be done here.

From what I've seen at the DRM code, one of the differences between the 
display engine for the first Hikey board (Kirin 620 based) and 960/970
is with regards to bridges. The first Hikey device doesn't use any
external bridges: both panel and HDMI support are provided by the SoC.

The Hikey 960 and 970 boards may either use an external bridge
or not. They also have two output connectors:

- The first one doesn't use an external bridge. It is used
  only together with an external daughter display panel board. 
  It sounds that one such panels is this one:

	https://www.96boards.org/blog/linksprite-hikey-aosp/

  I don't have any such board. The OOT driver came with one
  panel display, I didn't port such driver. 

- The second one uses an external bridge (adv7535) which is connected
  to the HDMI board's connector.

As there's just one bridge, the driver uses this to find its
OF data:

	struct device_node *bridge_node;

	bridge_node = of_graph_get_remote_port_parent(endpoint);
	dsi->bridge = of_drm_find_bridge(bridge_node);

Basically, it doesn't call drm_bridge_add(), and doesn't
declare any struct drm_bridge_funcs fops, as there's just one
bridge that it is always there.

-

That's said, when I ported the code from Kernel 4.9, I fixed
some broken things at the hotplug logic, trying to use other
drivers with external bridges as examples. Yet, as you noticed,
I ended using some older bridge model.  

The only other driver I found that doesn't use drm_bridge_add()
and doesn't pass 0 as flags is this one:

	drivers/gpu/drm/omapdrm/omap_drv.c

Is it a good example?

What I see different there there is that it calls drm_bridge_attach()
with:

	ret = drm_bridge_attach(pipe->encoder,
				pipe->output->bridge, NULL,
				DRM_BRIDGE_ATTACH_NO_CONNECTOR);

Is adding this enough? Or should I do something else?


Thanks,
Mauro
Mauro Carvalho Chehab Aug. 21, 2020, 1:58 p.m. UTC | #22
Em Wed, 19 Aug 2020 19:35:58 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> Also a few high level comments:

Hi Sam,

Finally finished addressing the things you pointed, except by a few
ones:
	- bridge bindings;
	- use drm_foo() instead of DRM_foo() when possible.

A few answers to some of your comments.

> There is too much unused code present - please delete.
> I dod not think I spotted it all.

I'll took a look and dropped most of it. I kept just some
code there related to an optional IOMMU support. The code
there is commented out because they depend on a driver that,
after some upstream discussions, I'm opting to not sending
it. Basically, there are better ways to support the integrated
MMU for the GPU core.

I'added some FIXME and commented out the code, as I intend
to take another look on it in the future. Not top priority,
as the driver works without it, but it could affect driver's
performance.

> Some style issues - ask checkpatch --strict for help identifying
> these.

Ok. There aren't much reported by checkpatch, as I did lots of
checkpatch fixes already in this series. I ended keeping
some CamelCase for macros:

	CHECK:CAMELCASE: Avoid CamelCase: <SMMU_SMRx_P>
	#143: FILE: drivers/staging/hikey9xx/gpu/kirin970_dpe_reg.h:143:
	+#define SMMU_SMRx_P	(0x10000)

and a few other things that, IMHO, making checkpatch happier
would make things worse for humans ;-)

> Needs to be adapted to new bridge handling - see comments.

Ok.

> Move panel stuff to drm_panel (or maybe I got confsed so this was just
> bride stuff).

There used to have a separate panel driver. The DRM driver has
support for it, probably using some older binding model (although
I converted some things that don't work on upstream Kernels anymore).

The old panel driver is there at the history (patch 01/49), but
a later patch drops it. The main thing is that Hikey 970 board
doesn't come with any panel. Maybe are out there some daughter
boards providing a panel display, but I was unable to find it.

So, I ended dropping the panel driver, but keeping the main
driver's code to handle it, as someone could find it useful.

> Lots track a few times - so may have confused myself a few times.
> 
> Many small comments - but general impression is good.

Good!

> Happy hacking!

Thanks!

> 	Sam
> 
> 
> > diff --git a/drivers/staging/hikey9xx/gpu/Kconfig b/drivers/staging/hikey9xx/gpu/Kconfig
> > new file mode 100644
> > index 000000000000..957da13bcf81
> > --- /dev/null
> > +++ b/drivers/staging/hikey9xx/gpu/Kconfig
> > @@ -0,0 +1,22 @@
> > +config DRM_HISI_KIRIN9XX
> > +	tristate "DRM Support for Hisilicon Kirin9xx series SoCs Platform"
> > +	depends on DRM && OF && ARM64
> > +	select DRM_KMS_HELPER
> > +	select DRM_GEM_CMA_HELPER
> > +	select DRM_KMS_CMA_HELPER
> > +	select DRM_MIPI_DSI
> > +	help
> > +	  Choose this option if you have a HiSilicon Kirin960 or Kirin970.
> > +	  If M is selected the module will be called kirin9xx-drm.
> > +
> > +config DRM_HISI_KIRIN970
> > +	bool "Enable support for Hisilicon Kirin970"
> > +	depends on DRM_MIPI_DSI  
> Implied by DRM_HISI_KIRIN9XX, so not needed.
> > +	depends on DRM_HISI_KIRIN9XX
> > +	help
> > +	  Choose this option if you have a hisilicon Kirin chipsets(kirin970).
> > +
> > +config DRM_HISI_KIRIN9XX_DSI
> > +	tristate
> > +	depends on DRM_HISI_KIRIN9XX
> > +	default y  
> This is essential a copy of DRM_HISI_KIRIN9XX - so no need for this
> extra Kconfig variable.

The above are left-overs. I'm dropping everything, keeping just
DRM_HISI_KIRIN9XX. The driver now has support for both Kirin 960 and 970
at the same time, without needing an extra Kconfig.

> > +	// D0  
> Some people dislike the more readable c99 comments.
> I do not recall if coding style allows them
> Ask checkpatch --strict

It used to be forbidden. Linus changed his mind about c99 comments
some time ago. It is now allowed. More than that, using c99 comments is 
now mandatory for SPDX on c files.

In any case, changing to /* */ is as easy as running this script:

	for i in drivers/staging/hikey9xx/gpu/*.[ch]; do perl -ne 'if (! m,// SPDX,) { s,//\s*(.*),/* \1 */,; }; print $_' -i $i; done

As I also prefer not using c99 comments, I applied a cleanup patch.

> Some of the enums I checked are not used.

Yeah, based on the size of the header files, when compared with
the size of the code, I suspect that lots of things there won't be
used. Cleaning this could take some time and may end removing
some bits that we could need in the future in order to address
existing problems at the driver. 

So, I opted preserving them, at least on this initial
patchset. I intend to do a further cleanup when trying to
merge this driver with the one for Kirin 620. On a side node,
currently, I don't have a Kirin 620 board. So, I'm postponing
such task until I get one such boards, as I'm not willing
to take the risk of changing it without being able to test.

> If their members are used then consider to use the enum
> and not just an int.

Yeah, it makes sense to use enum for type consistency.

I would prefer to do such kind of cleanup later on, together
with the attempt of having a single driver for all Kirin
supported DRM, as this is something that I'll need to revisit
when trying to merge the code.

> > +	ctx->dss_pri_clk = devm_clk_get(dev, "clk_edc0");
> > +	if (!ctx->dss_pri_clk) {
> > +		DRM_ERROR("failed to parse dss_pri_clk\n");
> > +	return -ENODEV;
> > +	}
...

> I had expected some of these could fail with a PROBE_DEFER.
> Consider to use the newly introduced dev_probe_err()

Yeah, getting clock lines can fail. I was unable to find dev_probe_err(),
at least on Kernel 5.9-rc1. I saw this comment:

	https://lkml.org/lkml/2020/3/6/356

It sounds it didn't reach upstream. Anyway, I add error handling for the
the clk_get calls:

	ctx->dss_pri_clk = devm_clk_get(dev, "clk_edc0");
	ret = PTR_ERR_OR_ZERO(ctx->dss_pri_clk);
	if (ret == -EPROBE_DEFER) {
		return ret;
	} else if (ret) {
		DRM_ERROR("failed to parse dss_pri_clk: %d\n", ret);
		return ret;
	}

This should be able to detect deferred probe, plus to warn
about other errors.

PS.: I'll be changing DRM_foo() to drm_foo() on a separate
patchset, after finishing the remaining code changes.


> > +static int  dss_drm_suspend(struct platform_device *pdev, pm_message_t state)
> > +{
> > +	struct dss_data *dss = platform_get_drvdata(pdev);
> > +	struct drm_crtc *crtc = &dss->acrtc.base;
> > +
> > +	dss_crtc_disable(crtc, NULL);
> > +
> > +	return 0;
> > +}  
> There is a suspend (and resume) helper - can it be used?

That's a very good question. Hard to answer right, as last time
I checked, DPMS suspend/resume are currently broken. 

I need to re-test it, but it has been hard to work on that part,
because right now there's no USB driver (so, no keyboard/mouse).

I was using x2x to send evdev events to the remote machine, but
I did some changes at the test distro, and this stopped working.

I need to find some other solution to send evdev events to Hikey970
while I don't fix my USB driver port.

So, for now, I prefer keeping those. I'll revisit this before
moving the driver out of staging, as this is one of the current
bugs.

> > +
> > +#define ROUND(x, y)		((x) / (y) + \
> > +				((x) % (y) * 10 / (y) >= 5 ? 1 : 0))
> > +#define ROUND1(x, y)	((x) / (y) + ((x) % (y)  ? 1 : 0))  
> Use generic macros for this?

> > +#define encoder_to_dsi(encoder) \
> > +	container_of(encoder, struct dw_dsi, encoder)
> > +#define host_to_dsi(host) \
> > +	container_of(host, struct dw_dsi, host)
> > +#define connector_to_dsi(connector) \
> > +	container_of(connector, struct dw_dsi, connector)
> > +#define DSS_REDUCE(x)	((x) > 0 ? ((x) - 1) : (x))  
> Use generic macros for this?

> > +struct dw_dsi_client {
> > +	u32 lanes;
> > +	u32 phy_clock; /* in kHz */
> > +	enum mipi_dsi_pixel_format format;
> > +	unsigned long mode_flags;
> > +};
> > +  
> 
> Can the panel stuff be moved out and utilise drm_panel?

I saw the code at drm_panel. The real issue here is that I can't
test anything related to panel support, as I lack any hardware
for testing. So, there's a high chance I may end breaking
something while trying to do that.

> > +struct dw_dsi {
> > +	struct drm_encoder encoder;
> > +	struct drm_bridge *bridge;
> > +	struct drm_panel *panel;
> > +	struct mipi_dsi_host host;
> > +	struct drm_connector connector; /* connector for panel */
> > +	struct drm_display_mode cur_mode;
> > +	struct dsi_hw_ctx *ctx;
> > +	struct mipi_phy_params phy;
> > +	struct mipi_panel_info mipi;
> > +	struct ldi_panel_info ldi;
> > +	u32 lanes;
> > +	enum mipi_dsi_pixel_format format;
> > +	unsigned long mode_flags;
> > +	struct gpio_desc *gpio_mux;
> > +	struct dw_dsi_client client[OUT_MAX];
> > +	enum dsi_output_client cur_client, attached_client;
> > +	bool enable;
> > +};  
> This smells like a bridge driver.
> Bridge drivers shall use the bridge panel.
> And new bridge drivers shall not create conectors, thats the role of the
> display driver.

Actually, this device is "hybrid" with regards to bridges.
HDMI uses an external bridge (adv7535), while panel doesn't.

In any case, I would prefer not touching anything related to
the panel. As I mentioned on my other comment, currently
I lack any panels that might work on Hikey 970. So, this is
something I can't test.

> > +
> > +	/* Link drm_bridge to encoder */
> > +	if (!bridge) {
> > +		DRM_INFO("no dsi bridge to attach the encoder\n");
> > +		return 0;
> > +	}
> > +
> > +	crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
> > +	if (!crtc_mask) {
> > +		DRM_ERROR("failed to find crtc mask\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	dev_info(dev, "Initializing CRTC encoder: %d\n",
> > +		 crtc_mask);
> > +
> > +	encoder->possible_crtcs = crtc_mask;
> > +	encoder->possible_clones = 0;
> > +	ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs,
> > +			       DRM_MODE_ENCODER_DSI, NULL);
> > +	if (ret) {
> > +		DRM_ERROR("failed to init dsi encoder\n");
> > +		return ret;
> > +	}
> > +
> > +	drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs);
> > +
> > +	/* associate the bridge to dsi encoder */
> > +	ret = drm_bridge_attach(encoder, bridge, NULL, 0);  
> The bridge should be attached with the falg that tell the bridge NOT to
> create a connector.
> 
> The display driver shall created the connector.
> 
> Please see how other drivers do this (but most driver uses the old
> pattern so so look for drm_bridge_attach() with the flag argument.
> I cannot see any user of this pwm and backlight stuff.
> So the best would be to drop it all.
> > +
> > +int hisi_pwm_set_backlight(struct backlight_device *bl, uint32_t bl_level)
> > +{

Yeah, this was dropped in a patch on this series that it is after the DT
changes.

The PWM and backlight stuff is used by the display panel driver which
came together with the DRM drivers. I dropped it, together with the
parts of the driver which used to call them.


Thanks,
Mauro
Mauro Carvalho Chehab Aug. 21, 2020, 2:41 p.m. UTC | #23
Another quick question:

Em Wed, 19 Aug 2020 19:35:58 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> > +#define DSS_REDUCE(x)	((x) > 0 ? ((x) - 1) : (x))  
> Use generic macros for this?

Do you know a generic macro similar to this? Or do you mean adding
it to include/kernel.h?

There are the atomic sub ones, but doesn't make sense here.

The closest one I found was min_not_zero(), but this would
take two args.

Btw, I agree that the name here is a bit odd... I would
have called such macro as 'dec_not_zero()'.

Thanks,
Mauro
Sam Ravnborg Aug. 21, 2020, 3:55 p.m. UTC | #24
Hi Mauro.

Thanks for the detailed feedabck.
Two comments in the following.

	Sam

> 
> > > +	ctx->dss_pri_clk = devm_clk_get(dev, "clk_edc0");
> > > +	if (!ctx->dss_pri_clk) {
> > > +		DRM_ERROR("failed to parse dss_pri_clk\n");
> > > +	return -ENODEV;
> > > +	}
> ...
> 
> > I had expected some of these could fail with a PROBE_DEFER.
> > Consider to use the newly introduced dev_probe_err()
> 
> Yeah, getting clock lines can fail. I was unable to find dev_probe_err(),
> at least on Kernel 5.9-rc1. I saw this comment:
> 
> 	https://lkml.org/lkml/2020/3/6/356
> 
> It sounds it didn't reach upstream. Anyway, I add error handling for the
> the clk_get calls:
> 
> 	ctx->dss_pri_clk = devm_clk_get(dev, "clk_edc0");
> 	ret = PTR_ERR_OR_ZERO(ctx->dss_pri_clk);
> 	if (ret == -EPROBE_DEFER) {
> 		return ret;
> 	} else if (ret) {
> 		DRM_ERROR("failed to parse dss_pri_clk: %d\n", ret);
> 		return ret;
> 	}
> 
> This should be able to detect deferred probe, plus to warn
> about other errors.

I got the name wrong. It is named dev_err_probe(), and was introduced in -rc1.
 
> > Can the panel stuff be moved out and utilise drm_panel?
> 
> I saw the code at drm_panel. The real issue here is that I can't
> test anything related to panel support, as I lack any hardware
> for testing. So, there's a high chance I may end breaking
> something while trying to do that.

I will try to take a look again when you post next revision.
Maybe we should update it and risk that is not works, so whenever
someone try to fix it they do so on top of an up-to-date implmentation.
Lets se and decide later.


	Sam
Sam Ravnborg Aug. 21, 2020, 3:56 p.m. UTC | #25
Hi Mauro.

On Fri, Aug 21, 2020 at 04:41:58PM +0200, Mauro Carvalho Chehab wrote:
> Another quick question:
> 
> Em Wed, 19 Aug 2020 19:35:58 +0200
> Sam Ravnborg <sam@ravnborg.org> escreveu:
> 
> > > +#define DSS_REDUCE(x)	((x) > 0 ? ((x) - 1) : (x))  
> > Use generic macros for this?
> 
> Do you know a generic macro similar to this? Or do you mean adding
> it to include/kernel.h?

It looked like something there should be a macro for.
But I do not know one.

And no, do not try to go the kernel.h route on this.
At least not until you see more than one user.

	Sam
Joe Perches Aug. 21, 2020, 4:09 p.m. UTC | #26
On Wed, 2020-08-19 at 22:48 +0200, Sam Ravnborg wrote:
> And sometimes checkpatch is just wrong.

I'm interested in examples for when checkpatch is "just wrong".
Mauro Carvalho Chehab Aug. 24, 2020, 6:49 a.m. UTC | #27
Hi John,

Em Wed, 19 Aug 2020 20:28:44 -0700
John Stultz <john.stultz@linaro.org> escreveu:


> That said even with the patches I've got on top of your series, I
> still see a few issues:
> 1) I'm seeing red-blue swap with your driver.  I need to dig a bit to
> see what the difference is, I know gralloc has a config option for
> this, and maybe the version of the driver I'm carrying has it wrong?

Maybe it is due to this:

	drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c:      hal_fmt = HISI_FB_PIXEL_FORMAT_BGRA_8888;/* dss_get_format(fb->pixel_format); */

It sounds to me that someone added a hack hardcoding BGRA_8888 over
there.

Btw, I removed the hack, with:


diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
index a68db1a27bbf..ba64aae371e4 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
@@ -857,7 +857,7 @@ void hisi_fb_pan_display(struct drm_plane *plane)
        rect.right = src_w - 1;
        rect.top = 0;
        rect.bottom = src_h - 1;
-       hal_fmt = HISI_FB_PIXEL_FORMAT_BGRA_8888;/* dss_get_format(fb->pixel_format); */
+       hal_fmt = dss_get_format(fb->format->format);
 
        DRM_DEBUG_DRIVER("channel%d: src:(%d,%d, %dx%d) crtc:(%d,%d, %dx%d), rect(%d,%d,%d,%d),fb:%dx%d, pixel_format=%d, stride=%d, paddr=0x%x, bpp=%d.\n",
                         chn_idx, src_x, src_y, src_w, src_h,


And now red and blue are swapped on my HDMI screen too.

I'll compare this part with your version, but I guess the bug is
on this logic.


Thanks,
Mauro
Mauro Carvalho Chehab Aug. 24, 2020, 1:18 p.m. UTC | #28
Em Mon, 24 Aug 2020 08:49:30 +0200
Mauro Carvalho Chehab <mchehab+huawei@kernel.org> escreveu:

> Hi John,
> 
> Em Wed, 19 Aug 2020 20:28:44 -0700
> John Stultz <john.stultz@linaro.org> escreveu:
> 
> 
> > That said even with the patches I've got on top of your series, I
> > still see a few issues:
> > 1) I'm seeing red-blue swap with your driver.  I need to dig a bit to
> > see what the difference is, I know gralloc has a config option for
> > this, and maybe the version of the driver I'm carrying has it wrong?  
> 
> Maybe it is due to this:
> 
> 	drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c:      hal_fmt = HISI_FB_PIXEL_FORMAT_BGRA_8888;/* dss_get_format(fb->pixel_format); */
> 
> It sounds to me that someone added a hack hardcoding BGRA_8888 over
> there.
> 
> Btw, I removed the hack, with:
> 
> 
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> index a68db1a27bbf..ba64aae371e4 100644
> --- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> @@ -857,7 +857,7 @@ void hisi_fb_pan_display(struct drm_plane *plane)
>         rect.right = src_w - 1;
>         rect.top = 0;
>         rect.bottom = src_h - 1;
> -       hal_fmt = HISI_FB_PIXEL_FORMAT_BGRA_8888;/* dss_get_format(fb->pixel_format); */
> +       hal_fmt = dss_get_format(fb->format->format);
>  
>         DRM_DEBUG_DRIVER("channel%d: src:(%d,%d, %dx%d) crtc:(%d,%d, %dx%d), rect(%d,%d,%d,%d),fb:%dx%d, pixel_format=%d, stride=%d, paddr=0x%x, bpp=%d.\n",
>                          chn_idx, src_x, src_y, src_w, src_h,
> 
> 
> And now red and blue are swapped on my HDMI screen too.
> 
> I'll compare this part with your version, but I guess the bug is
> on this logic.

It sounds to me that the Hikey 960 version on your tree has some color 
inversion hack, just for ARGB 32 bpp. See:

	static const struct kirin_format dpe_formats[] = {
		{ DRM_FORMAT_RGB565, DPE_RGB_565 },
		{ DRM_FORMAT_BGR565, DPE_BGR_565 },
		{ DRM_FORMAT_XRGB8888, DPE_RGBX_8888 },
		{ DRM_FORMAT_XBGR8888, DPE_BGRX_8888 },
		{ DRM_FORMAT_RGBA8888, DPE_RGBA_8888 },
		{ DRM_FORMAT_BGRA8888, DPE_BGRA_8888 },
		{ DRM_FORMAT_ARGB8888, DPE_BGRA_8888 },
		{ DRM_FORMAT_ABGR8888, DPE_RGBA_8888 },
	};

The last two lines are weird, as they're reverting the byte order,
If there's some endiannes issue (which the change from ARGB->ABGR 
seems to imply), I would expect to have something similar for the 
other formats as well.

I did some tests here: both FB and X11 sets bpp to 24 bits.

Trying to use "startx -- -depth 32" don't work:

	"Default Screen Section" for depth/fbbpp 32/32
	[   129.250] (EE) modeset(0): Given depth (32) is not supported by the driver

Which sounds weird, as the driver announces 32 bit formats. 
I suspect that this could be related to the valid modes hack at 
the driver.

Btw, there are some color shift also with --depth 16, but replacing
BGR <=> RGB didn't work.

Did you test the different bpp resolutions with the driver on your
tree? The enclosed patch makes 24 bits bpp work here.

Thanks,
Mauro

-

[PATCH] staging: kirin9xx/gpu: rework the colorspace mode setting logic

There are some problems when setting the fourcc KMS:
The original code hardcodes BGRA_8888. The real issue here seems
to be that the byte order is different than the one for Kirin 620.

Instead of addressing it, the origincla code just used a fixed
mode. A port of the Hikey 960 DPE driver based on Kernel 5.0,
found at:

	https://git.linaro.org/people/john.stultz/android-dev.git/tree/drivers/gpu/drm/hisilicon/kirin/kirin_drm_dpe.c?h=dev/hikey960-mainline-WIP

contains a hack that swaps the byte order for 32-bits
ARGB/BRGR (see the last two lines):

		{ DRM_FORMAT_RGB565, DPE_RGB_565 },
		{ DRM_FORMAT_BGR565, DPE_BGR_565 },
		{ DRM_FORMAT_XRGB8888, DPE_RGBX_8888 },
		{ DRM_FORMAT_XBGR8888, DPE_BGRX_8888 },
		{ DRM_FORMAT_RGBA8888, DPE_RGBA_8888 },
		{ DRM_FORMAT_BGRA8888, DPE_BGRA_8888 },
		{ DRM_FORMAT_ARGB8888, DPE_BGRA_8888 },
		{ DRM_FORMAT_ABGR8888, DPE_RGBA_8888 },

But the same change was not applied to other modesets.

The Hikey 960 port was tested with AOSP, which seems to
be using ABGR format. Here, the chosen fourcc was
XBGR 32 bpp instead. I suspect that the original developer
also found a similar issue and decided to hardcode the
fourcc format.

That's said, currently the drivers uses some code instead of
tables in order to seek for the register settings. The version
from John Stultz tre for Kirin 960 that does a better approach
of using tables instead of code.

I opted to use the same code as the basis for the new logic,
as it makes easier to identify what the driver is actually doing.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h b/drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
index e05522f85df8..26add227c389 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_dpe.h
@@ -144,6 +144,39 @@ enum dss_dma_format {
 	DMA_PIXEL_FORMAT_AYUV_4444,
 };
 
+enum dpe_fb_format {
+	DPE_RGB_565 = 0,
+	DPE_RGBX_4444,
+	DPE_RGBA_4444,
+	DPE_RGBX_5551,
+	DPE_RGBA_5551,
+	DPE_RGBX_8888,
+	DPE_RGBA_8888,
+	DPE_BGR_565,
+	DPE_BGRX_4444,
+	DPE_BGRA_4444,
+	DPE_BGRX_5551,
+	DPE_BGRA_5551,
+	DPE_BGRX_8888,
+	DPE_BGRA_8888,
+	DPE_YUV_422_I,
+	/* YUV Semi-planar */
+	DPE_YCbCr_422_SP,
+	DPE_YCrCb_422_SP,
+	DPE_YCbCr_420_SP,
+	DPE_YCrCb_420_SP,
+	/* YUV Planar */
+	DPE_YCbCr_422_P,
+	DPE_YCrCb_422_P,
+	DPE_YCbCr_420_P,
+	DPE_YCrCb_420_P,
+	/* YUV Package */
+	DPE_YUYV_422_Pkg,
+	DPE_UYVY_422_Pkg,
+	DPE_YVYU_422_Pkg,
+	DPE_VYUY_422_Pkg,
+};
+
 enum dss_buf_format {
 	DSS_BUF_LINEAR = 0,
 	DSS_BUF_TILE,
diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
index 8034c5134b25..a3071388a86c 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dpe_utils.h
@@ -22,46 +22,7 @@ enum dss_channel {
 
 #define PRIMARY_CH	DSS_CH1 /* primary plane */
 
-enum hisi_fb_pixel_format {
-	HISI_FB_PIXEL_FORMAT_RGB_565 = 0,
-	HISI_FB_PIXEL_FORMAT_RGBX_4444,
-	HISI_FB_PIXEL_FORMAT_RGBA_4444,
-	HISI_FB_PIXEL_FORMAT_RGBX_5551,
-	HISI_FB_PIXEL_FORMAT_RGBA_5551,
-	HISI_FB_PIXEL_FORMAT_RGBX_8888,
-	HISI_FB_PIXEL_FORMAT_RGBA_8888,
-
-	HISI_FB_PIXEL_FORMAT_BGR_565,
-	HISI_FB_PIXEL_FORMAT_BGRX_4444,
-	HISI_FB_PIXEL_FORMAT_BGRA_4444,
-	HISI_FB_PIXEL_FORMAT_BGRX_5551,
-	HISI_FB_PIXEL_FORMAT_BGRA_5551,
-	HISI_FB_PIXEL_FORMAT_BGRX_8888,
-	HISI_FB_PIXEL_FORMAT_BGRA_8888,
-
-	HISI_FB_PIXEL_FORMAT_YUV_422_I,
-
-	/* YUV Semi-planar */
-	HISI_FB_PIXEL_FORMAT_YCbCr_422_SP,	/* NV16 */
-	HISI_FB_PIXEL_FORMAT_YCrCb_422_SP,
-	HISI_FB_PIXEL_FORMAT_YCbCr_420_SP,
-	HISI_FB_PIXEL_FORMAT_YCrCb_420_SP,	/* NV21 */
-
-	/* YUV Planar */
-	HISI_FB_PIXEL_FORMAT_YCbCr_422_P,
-	HISI_FB_PIXEL_FORMAT_YCrCb_422_P,
-	HISI_FB_PIXEL_FORMAT_YCbCr_420_P,
-	HISI_FB_PIXEL_FORMAT_YCrCb_420_P,	/* HISI_FB_PIXEL_FORMAT_YV12 */
-
-	/* YUV Package */
-	HISI_FB_PIXEL_FORMAT_YUYV_422,
-	HISI_FB_PIXEL_FORMAT_UYVY_422,
-	HISI_FB_PIXEL_FORMAT_YVYU_422,
-	HISI_FB_PIXEL_FORMAT_VYUY_422,
-	HISI_FB_PIXEL_FORMAT_MAX,
-
-	HISI_FB_PIXEL_FORMAT_UNSUPPORT = 800
-};
+#define HISI_FB_PIXEL_FORMAT_UNSUPPORT 800
 
 struct dss_hw_ctx {
 	struct drm_device *dev;
@@ -155,12 +116,6 @@ struct dss_data {
 	struct dss_hw_ctx ctx;
 };
 
-/* ade-format info: */
-struct dss_format {
-	u32 pixel_format;
-	enum hisi_fb_pixel_format dss_format;
-};
-
 struct dss_img {
 	u32 format;
 	u32 width;
@@ -266,8 +221,10 @@ int hisi_dss_mctl_mutex_lock(struct dss_hw_ctx *ctx);
 int hisi_dss_mctl_mutex_unlock(struct dss_hw_ctx *ctx);
 int hisi_dss_ovl_base_config(struct dss_hw_ctx *ctx, u32 xres, u32 yres);
 
+u32 hisi_dss_get_channel_formats(struct drm_device *dev, u8 ch, const u32 **formats);
+
 void hisi_fb_pan_display(struct drm_plane *plane);
 
-u32 dss_get_format(struct drm_device *dev, u32 pixel_format);
+u32 dpe_get_format(struct dss_hw_ctx *ctx, u32 pixel_format);
 
 #endif
diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
index 9bc114a33885..93eb9bf8b305 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
@@ -38,58 +38,6 @@
 #include "kirin9xx_drm_dpe_utils.h"
 #include "kirin9xx_dpe.h"
 
-static const struct dss_format dss_formats[] = {
-	/* 16bpp RGB: */
-	{ DRM_FORMAT_RGB565, HISI_FB_PIXEL_FORMAT_RGB_565 },
-	{ DRM_FORMAT_BGR565, HISI_FB_PIXEL_FORMAT_BGR_565 },
-	/* 32bpp [A]RGB: */
-	{ DRM_FORMAT_XRGB8888, HISI_FB_PIXEL_FORMAT_RGBX_8888 },
-	{ DRM_FORMAT_XBGR8888, HISI_FB_PIXEL_FORMAT_BGRX_8888 },
-	{ DRM_FORMAT_RGBA8888, HISI_FB_PIXEL_FORMAT_RGBA_8888 },
-	{ DRM_FORMAT_BGRA8888, HISI_FB_PIXEL_FORMAT_BGRA_8888 },
-	{ DRM_FORMAT_ARGB8888, HISI_FB_PIXEL_FORMAT_RGBA_8888 },
-	{ DRM_FORMAT_ABGR8888, HISI_FB_PIXEL_FORMAT_BGRA_8888 },
-};
-
-static const u32 channel_formats1[] = {
-	DRM_FORMAT_RGB565,
-	DRM_FORMAT_BGR565,
-	DRM_FORMAT_XRGB8888,
-	DRM_FORMAT_XBGR8888,
-	DRM_FORMAT_RGBA8888,
-	DRM_FORMAT_BGRA8888,
-	DRM_FORMAT_ARGB8888,
-	DRM_FORMAT_ABGR8888,
-};
-
-u32 dss_get_channel_formats(struct drm_device *dev, u8 ch, const u32 **formats)
-{
-	switch (ch) {
-	case DSS_CH1:
-		*formats = channel_formats1;
-		return ARRAY_SIZE(channel_formats1);
-	default:
-		drm_err(dev, "no formats for channel %d\n", ch);
-		*formats = NULL;
-		return 0;
-	}
-}
-
-/* convert from fourcc format to dss format */
-u32 dss_get_format(struct drm_device *dev, u32 pixel_format)
-{
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(dss_formats); i++)
-		if (dss_formats[i].pixel_format == pixel_format)
-			return dss_formats[i].dss_format;
-
-	/* not found */
-	drm_err(dev, "Not found pixel format!!fourcc_format= %d\n",
-		pixel_format);
-	return HISI_FB_PIXEL_FORMAT_UNSUPPORT;
-}
-
 /*****************************************************************************/
 
 int hdmi_pxl_ppll7_init(struct dss_hw_ctx *ctx, u64 pixel_clock)
@@ -616,6 +564,8 @@ static int dss_plane_atomic_check(struct drm_plane *plane,
 {
 	struct drm_framebuffer *fb = state->fb;
 	struct drm_crtc *crtc = state->crtc;
+	struct dss_crtc *acrtc = to_dss_crtc(crtc);
+	struct dss_hw_ctx *ctx = acrtc->ctx;
 	struct drm_crtc_state *crtc_state;
 	u32 src_x = state->src_x >> 16;
 	u32 src_y = state->src_y >> 16;
@@ -630,7 +580,7 @@ static int dss_plane_atomic_check(struct drm_plane *plane,
 	if (!crtc || !fb)
 		return 0;
 
-	fmt = dss_get_format(plane->dev, fb->format->format);
+	fmt = dpe_get_format(ctx, fb->format->format);
 	if (fmt == HISI_FB_PIXEL_FORMAT_UNSUPPORT)
 		return -EINVAL;
 
@@ -706,7 +656,7 @@ static int dss_plane_init(struct drm_device *dev, struct dss_plane *aplane,
 	int ret = 0;
 
 	/* get properties */
-	fmts_cnt = dss_get_channel_formats(dev, aplane->ch, &fmts);
+	fmts_cnt = hisi_dss_get_channel_formats(dev, aplane->ch, &fmts);
 	if (ret)
 		return ret;
 
diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
index a68db1a27bbf..fb034337d689 100644
--- a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
+++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
@@ -22,168 +22,118 @@ static const int mid_array[DSS_CHN_MAX_DEFINE] = {
 	0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x2, 0x1, 0x3, 0x0
 };
 
-static int hisi_pixel_format_hal2dma(int format)
+struct dpe_format {
+	u32 pixel_format;
+	enum dpe_fb_format dpe_format;
+};
+
+static const struct dpe_format dpe_formats[] = {
+	{ DRM_FORMAT_RGB565, DPE_RGB_565 },
+	{ DRM_FORMAT_BGR565, DPE_BGR_565 },
+	{ DRM_FORMAT_XRGB8888, DPE_BGRX_8888 },
+	{ DRM_FORMAT_XBGR8888, DPE_RGBX_8888 },
+	{ DRM_FORMAT_RGBA8888, DPE_RGBA_8888 },
+	{ DRM_FORMAT_BGRA8888, DPE_BGRA_8888 },
+	{ DRM_FORMAT_ARGB8888, DPE_RGBA_8888 },
+	{ DRM_FORMAT_ABGR8888, DPE_BGRA_8888 },
+};
+
+static const u32 dpe_channel_formats[] = {
+	DRM_FORMAT_RGB565,
+	DRM_FORMAT_BGR565,
+	DRM_FORMAT_XRGB8888,
+	DRM_FORMAT_XBGR8888,
+	DRM_FORMAT_RGBA8888,
+	DRM_FORMAT_BGRA8888,
+	DRM_FORMAT_ARGB8888,
+	DRM_FORMAT_ABGR8888,
+};
+
+static u32 dpe_pixel_dma_format_map[] = {
+	DMA_PIXEL_FORMAT_RGB_565,
+	DMA_PIXEL_FORMAT_XRGB_4444,
+	DMA_PIXEL_FORMAT_ARGB_4444,
+	DMA_PIXEL_FORMAT_XRGB_5551,
+	DMA_PIXEL_FORMAT_ARGB_5551,
+	DMA_PIXEL_FORMAT_XRGB_8888,
+	DMA_PIXEL_FORMAT_ARGB_8888,
+	DMA_PIXEL_FORMAT_RGB_565,
+	DMA_PIXEL_FORMAT_XRGB_4444,
+	DMA_PIXEL_FORMAT_ARGB_4444,
+	DMA_PIXEL_FORMAT_XRGB_5551,
+	DMA_PIXEL_FORMAT_ARGB_5551,
+	DMA_PIXEL_FORMAT_XRGB_8888,
+	DMA_PIXEL_FORMAT_ARGB_8888,
+	DMA_PIXEL_FORMAT_YUYV_422,
+	DMA_PIXEL_FORMAT_YUV_422_SP_HP,
+	DMA_PIXEL_FORMAT_YUV_422_SP_HP,
+	DMA_PIXEL_FORMAT_YUV_420_SP_HP,
+	DMA_PIXEL_FORMAT_YUV_420_SP_HP,
+	DMA_PIXEL_FORMAT_YUV_422_P_HP,
+	DMA_PIXEL_FORMAT_YUV_422_P_HP,
+	DMA_PIXEL_FORMAT_YUV_420_P_HP,
+	DMA_PIXEL_FORMAT_YUV_420_P_HP,
+	DMA_PIXEL_FORMAT_YUYV_422,
+	DMA_PIXEL_FORMAT_YUYV_422,
+	DMA_PIXEL_FORMAT_YUYV_422,
+	DMA_PIXEL_FORMAT_YUYV_422,
+};
+
+static u32 dpe_pixel_dfc_format_map[] = {
+	DFC_PIXEL_FORMAT_RGB_565,
+	DFC_PIXEL_FORMAT_XBGR_4444,
+	DFC_PIXEL_FORMAT_ABGR_4444,
+	DFC_PIXEL_FORMAT_XBGR_5551,
+	DFC_PIXEL_FORMAT_ABGR_5551,
+	DFC_PIXEL_FORMAT_XBGR_8888,
+	DFC_PIXEL_FORMAT_ABGR_8888,
+	DFC_PIXEL_FORMAT_BGR_565,
+	DFC_PIXEL_FORMAT_XRGB_4444,
+	DFC_PIXEL_FORMAT_ARGB_4444,
+	DFC_PIXEL_FORMAT_XRGB_5551,
+	DFC_PIXEL_FORMAT_ARGB_5551,
+	DFC_PIXEL_FORMAT_XRGB_8888,
+	DFC_PIXEL_FORMAT_ARGB_8888,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_YVYU422,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_YVYU422,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_YVYU422,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_YVYU422,
+	DFC_PIXEL_FORMAT_YUYV422,
+	DFC_PIXEL_FORMAT_UYVY422,
+	DFC_PIXEL_FORMAT_YVYU422,
+	DFC_PIXEL_FORMAT_VYUY422,
+};
+
+u32 dpe_get_format(struct dss_hw_ctx *ctx, u32 pixel_format)
 {
-	int ret = 0;
-
-	switch (format) {
-	case HISI_FB_PIXEL_FORMAT_RGB_565:
-	case HISI_FB_PIXEL_FORMAT_BGR_565:
-		ret = DMA_PIXEL_FORMAT_RGB_565;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBX_4444:
-	case HISI_FB_PIXEL_FORMAT_BGRX_4444:
-		ret = DMA_PIXEL_FORMAT_XRGB_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_4444:
-	case HISI_FB_PIXEL_FORMAT_BGRA_4444:
-		ret = DMA_PIXEL_FORMAT_ARGB_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBX_5551:
-	case HISI_FB_PIXEL_FORMAT_BGRX_5551:
-		ret = DMA_PIXEL_FORMAT_XRGB_5551;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_5551:
-	case HISI_FB_PIXEL_FORMAT_BGRA_5551:
-		ret = DMA_PIXEL_FORMAT_ARGB_5551;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_RGBX_8888:
-	case HISI_FB_PIXEL_FORMAT_BGRX_8888:
-		ret = DMA_PIXEL_FORMAT_XRGB_8888;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_8888:
-	case HISI_FB_PIXEL_FORMAT_BGRA_8888:
-		ret = DMA_PIXEL_FORMAT_ARGB_8888;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YUV_422_I:
-	case HISI_FB_PIXEL_FORMAT_YUYV_422:
-	case HISI_FB_PIXEL_FORMAT_YVYU_422:
-	case HISI_FB_PIXEL_FORMAT_UYVY_422:
-	case HISI_FB_PIXEL_FORMAT_VYUY_422:
-		ret = DMA_PIXEL_FORMAT_YUYV_422;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YCbCr_422_P:
-	case HISI_FB_PIXEL_FORMAT_YCrCb_422_P:
-		ret = DMA_PIXEL_FORMAT_YUV_422_P_HP;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCbCr_420_P:
-	case HISI_FB_PIXEL_FORMAT_YCrCb_420_P:
-		ret = DMA_PIXEL_FORMAT_YUV_420_P_HP;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YCbCr_422_SP:
-	case HISI_FB_PIXEL_FORMAT_YCrCb_422_SP:
-		ret = DMA_PIXEL_FORMAT_YUV_422_SP_HP;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCbCr_420_SP:
-	case HISI_FB_PIXEL_FORMAT_YCrCb_420_SP:
-		ret = DMA_PIXEL_FORMAT_YUV_420_SP_HP;
-		break;
-
-	default:
-		DRM_ERROR("not support format(%d)!\n", format);
-		ret = -1;
-		break;
+	int i;
+	const struct dpe_format *fmt = dpe_formats;
+	u32 size = ARRAY_SIZE(dpe_formats);
+
+
+	for (i = 0; i < size; i++) {
+		if (fmt[i].pixel_format == pixel_format) {
+			drm_info(ctx->dev, "requested fourcc %x, dpe format %d",
+				 pixel_format, fmt[i].dpe_format);
+			return fmt[i].dpe_format;
+		}
 	}
 
-	return ret;
+	drm_err(ctx->dev, "Not found pixel format! fourcc = %x\n",
+		pixel_format);
+
+	return HISI_FB_PIXEL_FORMAT_UNSUPPORT;
 }
 
-static int hisi_pixel_format_hal2dfc(int format)
+u32 hisi_dss_get_channel_formats(struct drm_device *dev, u8 ch, const u32 **formats)
 {
-	int ret = 0;
-
-	switch (format) {
-	case HISI_FB_PIXEL_FORMAT_RGB_565:
-		ret = DFC_PIXEL_FORMAT_RGB_565;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBX_4444:
-		ret = DFC_PIXEL_FORMAT_XBGR_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_4444:
-		ret = DFC_PIXEL_FORMAT_ABGR_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBX_5551:
-		ret = DFC_PIXEL_FORMAT_XBGR_5551;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_5551:
-		ret = DFC_PIXEL_FORMAT_ABGR_5551;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBX_8888:
-		ret = DFC_PIXEL_FORMAT_XBGR_8888;
-		break;
-	case HISI_FB_PIXEL_FORMAT_RGBA_8888:
-		ret = DFC_PIXEL_FORMAT_ABGR_8888;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_BGR_565:
-		ret = DFC_PIXEL_FORMAT_BGR_565;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRX_4444:
-		ret = DFC_PIXEL_FORMAT_XRGB_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRA_4444:
-		ret = DFC_PIXEL_FORMAT_ARGB_4444;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRX_5551:
-		ret = DFC_PIXEL_FORMAT_XRGB_5551;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRA_5551:
-		ret = DFC_PIXEL_FORMAT_ARGB_5551;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRX_8888:
-		ret = DFC_PIXEL_FORMAT_XRGB_8888;
-		break;
-	case HISI_FB_PIXEL_FORMAT_BGRA_8888:
-		ret = DFC_PIXEL_FORMAT_ARGB_8888;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YUV_422_I:
-	case HISI_FB_PIXEL_FORMAT_YUYV_422:
-		ret = DFC_PIXEL_FORMAT_YUYV422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YVYU_422:
-		ret = DFC_PIXEL_FORMAT_YVYU422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_UYVY_422:
-		ret = DFC_PIXEL_FORMAT_UYVY422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_VYUY_422:
-		ret = DFC_PIXEL_FORMAT_VYUY422;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YCbCr_422_SP:
-		ret = DFC_PIXEL_FORMAT_YUYV422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCrCb_422_SP:
-		ret = DFC_PIXEL_FORMAT_YVYU422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCbCr_420_SP:
-		ret = DFC_PIXEL_FORMAT_YUYV422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCrCb_420_SP:
-		ret = DFC_PIXEL_FORMAT_YVYU422;
-		break;
-
-	case HISI_FB_PIXEL_FORMAT_YCbCr_422_P:
-	case HISI_FB_PIXEL_FORMAT_YCbCr_420_P:
-		ret = DFC_PIXEL_FORMAT_YUYV422;
-		break;
-	case HISI_FB_PIXEL_FORMAT_YCrCb_422_P:
-	case HISI_FB_PIXEL_FORMAT_YCrCb_420_P:
-		ret = DFC_PIXEL_FORMAT_YVYU422;
-		break;
-
-	default:
-		DRM_ERROR("not support format(%d)!\n", format);
-		ret = -1;
-		break;
-	}
-
-	return ret;
+	*formats = dpe_channel_formats;
+	return ARRAY_SIZE(dpe_channel_formats);
 }
 
 static int hisi_dss_aif_ch_config(struct dss_hw_ctx *ctx, int chn_idx)
@@ -347,8 +297,10 @@ static int hisi_dss_mctl_sys_config(struct dss_hw_ctx *ctx, int chn_idx)
 }
 
 static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
-				const struct dss_rect_ltrb *rect, u32 display_addr, u32 hal_format,
-	u32 bpp, int chn_idx, bool afbcd, bool mmu_enable)
+				const struct dss_rect_ltrb *rect,
+				u32 display_addr, u32 hal_format,
+				u32 bpp, int chn_idx, bool afbcd,
+				bool mmu_enable)
 {
 	void __iomem *rdma_base;
 
@@ -358,7 +310,6 @@ static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
 	u32 rdma_oft_x1 = 0;
 	u32 rdma_oft_y1 = 0;
 	u32 rdma_stride = 0;
-	u32 rdma_bpp = 0;
 	u32 rdma_format = 0;
 	u32 stretch_size_vrt = 0;
 
@@ -371,18 +322,6 @@ static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
 	u32 afbcd_payload_addr = 0;
 	u32 afbcd_payload_stride = 0;
 
-	if (!ctx) {
-		DRM_ERROR("ctx is NULL!\n");
-		return -1;
-	}
-
-	if (bpp == 4)
-		rdma_bpp = 0x5;
-	else if (bpp == 2)
-		rdma_bpp = 0x0;
-	else
-		rdma_bpp = 0x0;
-
 	rdma_base = ctx->base +
 		ctx->g_dss_module_base[chn_idx][MODULE_DMA];
 
@@ -392,11 +331,7 @@ static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
 	rdma_oft_x1 = rect->right / aligned_pixel;
 	rdma_oft_y1 = rect->bottom;
 
-	rdma_format = hisi_pixel_format_hal2dma(hal_format);
-	if (rdma_format < 0) {
-		DRM_ERROR("layer format(%d) not support !\n", hal_format);
-		return -EINVAL;
-	}
+	rdma_format = dpe_pixel_dma_format_map[hal_format];
 
 	if (afbcd) {
 		mm_base_0 = 0;
@@ -470,10 +405,9 @@ static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
 		set_reg(rdma_base + DMA_OFT_Y0, rdma_oft_y0, 16, 0);
 		set_reg(rdma_base + DMA_OFT_X1, rdma_oft_x1, 12, 0);
 		set_reg(rdma_base + DMA_OFT_Y1, rdma_oft_y1, 16, 0);
-		/* set_reg(rdma_base + DMA_CTRL, rdma_format, 5, 3); */
-		/* set_reg(rdma_base + DMA_CTRL, (mmu_enable ? 0x1 : 0x0), 1, 8); */
+		set_reg(rdma_base + DMA_CTRL, rdma_format, 5, 3);
+		set_reg(rdma_base + DMA_CTRL, (mmu_enable ? 0x1 : 0x0), 1, 8);
 		set_reg(rdma_base + DMA_CTRL, 0x130, 32, 0);
-		/* set_reg(rdma_base + DMA_CTRL, (mmu_enable ? 0x1 : 0x0), 1, 8); */
 		set_reg(rdma_base + DMA_STRETCH_SIZE_VRT, stretch_size_vrt, 32, 0);
 		set_reg(rdma_base + DMA_DATA_ADDR0, display_addr, 32, 0);
 		set_reg(rdma_base + DMA_STRIDE0, rdma_stride, 13, 0);
@@ -495,11 +429,6 @@ static int hisi_dss_rdfc_config(struct dss_hw_ctx *ctx,
 	u32 size_vrt = 0;
 	u32 dfc_fmt = 0;
 
-	if (!ctx) {
-		DRM_ERROR("ctx is NULL!\n");
-		return -1;
-	}
-
 	rdfc_base = ctx->base +
 		ctx->g_dss_module_base[chn_idx][MODULE_DFC];
 
@@ -507,15 +436,11 @@ static int hisi_dss_rdfc_config(struct dss_hw_ctx *ctx,
 	size_hrz = rect->right - rect->left;
 	size_vrt = rect->bottom - rect->top;
 
-	dfc_fmt = hisi_pixel_format_hal2dfc(hal_format);
-	if (dfc_fmt < 0) {
-		DRM_ERROR("layer format (%d) not support !\n", hal_format);
-		return -EINVAL;
-	}
+	dfc_fmt = dpe_pixel_dfc_format_map[hal_format];
 
-	set_reg(rdfc_base + DFC_DISP_SIZE, (size_vrt | (size_hrz << 16)), 29, 0);
+	set_reg(rdfc_base + DFC_DISP_SIZE, (size_vrt | (size_hrz << 16)),
+		29, 0);
 	set_reg(rdfc_base + DFC_PIX_IN_NUM, dfc_pix_in_num, 1, 0);
-	/* set_reg(rdfc_base + DFC_DISP_FMT, (bpp <= 2) ? 0x0 : 0x6, 5, 1); */
 	set_reg(rdfc_base + DFC_DISP_FMT, dfc_fmt, 5, 1);
 	set_reg(rdfc_base + DFC_CTL_CLIP_EN, 0x1, 1, 0);
 	set_reg(rdfc_base + DFC_ICG_MODULE, 0x1, 1, 0);
@@ -857,7 +782,7 @@ void hisi_fb_pan_display(struct drm_plane *plane)
 	rect.right = src_w - 1;
 	rect.top = 0;
 	rect.bottom = src_h - 1;
-	hal_fmt = HISI_FB_PIXEL_FORMAT_BGRA_8888;/* dss_get_format(fb->pixel_format); */
+	hal_fmt = dpe_get_format(ctx, fb->format->format);
 
 	DRM_DEBUG_DRIVER("channel%d: src:(%d,%d, %dx%d) crtc:(%d,%d, %dx%d), rect(%d,%d,%d,%d),fb:%dx%d, pixel_format=%d, stride=%d, paddr=0x%x, bpp=%d.\n",
 			 chn_idx, src_x, src_y, src_w, src_h,
Mauro Carvalho Chehab Aug. 24, 2020, 4:06 p.m. UTC | #29
Em Fri, 21 Aug 2020 17:56:50 +0200
Sam Ravnborg <sam@ravnborg.org> escreveu:

> Hi Mauro.
> 
> On Fri, Aug 21, 2020 at 04:41:58PM +0200, Mauro Carvalho Chehab wrote:
> > Another quick question:
> > 
> > Em Wed, 19 Aug 2020 19:35:58 +0200
> > Sam Ravnborg <sam@ravnborg.org> escreveu:
> >   
> > > > +#define DSS_REDUCE(x)	((x) > 0 ? ((x) - 1) : (x))    
> > > Use generic macros for this?  
> > 
> > Do you know a generic macro similar to this? Or do you mean adding
> > it to include/kernel.h?  
> 
> It looked like something there should be a macro for.
> But I do not know one.
> 
> And no, do not try to go the kernel.h route on this.
> At least not until you see more than one user.

Yeah, adding this to kernel.h just for a single usage is overkill. I would
be expecting that a non-underflow decrement logic is something that 
would be used on other places at the Kernel, but identifying this
pattern would require some time. Maybe Kernel janitors could write some
coccinelle script to replace similar patterns like that into some
macro in the future.

Thanks,
Mauro
Dave Airlie Aug. 24, 2020, 7:29 p.m. UTC | #30
On Thu, 20 Aug 2020 at 20:02, Laurent Pinchart
<laurent.pinchart@ideasonboard.com> wrote:
>
> Hi Mauro,
>
> On Thu, Aug 20, 2020 at 09:03:26AM +0200, Mauro Carvalho Chehab wrote:
> > Em Wed, 19 Aug 2020 12:52:06 -0700 John Stultz escreveu:
> > > On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart wrote:
> > > > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:
> > > > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:
> > > > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > > > should also support Hikey 960) from the official 96boards tree:
> > > > > >
> > > > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > > > >
> > > > > > Based on his history, this driver seems to be originally written
> > > > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > > > implementation for FB dev API.
> > > > > >
> > > > > > As I need to preserve the original history (with has patches from
> > > > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > > > patch applied there. The remaining patches are incremental,
> > > > > > and port this driver to work with upstream Kernel.
> > > > > >
> > > ...
> > > > > > - Due to legal reasons, I need to preserve the authorship of
> > > > > >   each one responsbile for each patch. So, I need to start from
> > > > > >   the original patch from Kernel 4.4;
> > > ...
> > > > > I do acknowledge you need to preserve history and all -
> > > > > but this patchset is not easy to review.
> > > >
> > > > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > > > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > > > that contains the history is useful if anyone is interested, but I don't
> > > > think it's required in mainline.
> > >
> > > Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> > > have on this but preserving the "absolute" history here is actively
> > > detrimental for review and understanding of the patch set.
> > >
> > > Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> > > lines should be sufficient to provide both atribution credit and DCO
> > > history.
> >
> > I'm not convinced that, from legal standpoint, folding things would
> > be enough. See, there are at least 3 legal systems involved here
> > among the different patch authors:
> >
> >       - civil law;
> >       - common law;
> >       - customary law + common law.
> >
> > Merging stuff altogether from different law systems can be problematic,
> > and trying to discuss this with experienced IP property lawyers will
> > for sure take a lot of time and efforts. I also bet that different
> > lawyers will have different opinions, because laws are subject to
> > interpretation. With that matter I'm not aware of any court rules
> > with regards to folded patches. So, it sounds to me that folding
> > patches is something that has yet to be proofed in courts around
> > the globe.
> >
> > At least for US legal system, it sounds that the Country of
> > origin of a patch is relevant, as they have a concept of
> > "national technology" that can be subject to export regulations.
> >
> > From my side, I really prefer to play safe and stay out of any such
> > legal discussions.
>
> Let's be serious for a moment. If you think there are legal issues in
> taking GPL-v2.0-only patches and squashing them while retaining
> authorship information through tags, the Linux kernel if *full* of that.
> You also routinely modify patches that you commit to the media subsystem
> to fix "small issues".
>
> The country of origin argument makes no sense either, the kernel code
> base if full of code coming from pretty much all country on the planet.
>
> Keeping the patches separate make this hard to review. Please squash
> them.

I'm inclined to agree with Laurent here.

Patches submitted as GPL-v2 with DCO lines and author names/companies
should be fine to be squashed and rearranged,
as long as the DCO and Authorship is kept somewhere in the new patch
that is applied.

Review is more important here.

Dave.
Sam Ravnborg Aug. 24, 2020, 9:10 p.m. UTC | #31
Hi Mauro



> kirin9xx_fb_panel.h b/drivers/staging/hikey9xx/gpu/kirin9xx_fb_panel.h
> new file mode 100644
> index 000000000000..a69c20470f1d
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_fb_panel.h

This file is not referenced and should be deleted.

	Sam
Sam Ravnborg Aug. 24, 2020, 9:24 p.m. UTC | #32
Hi Mauro

> Before posting the big patch series again, let me send the new
> version folded into a single patch.

Review 2/N

The way output_poll_changed is used to set gpio_mux to select between
the panel and the HDMI looks strange. But I do not know if there is a
more correct way to do it. Other DRM people would need to help
here if there is a better way to do it.

I looked briefly af suspend/resume.
I think this would benefit from using drm_mode_config_helper_suspend()
and drm_mode_config_helper_resume() but I did not finalize the anlyzis.

Other than this only some small details in the following.

	Sam

>  kirin9xx_drm_drv.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> new file mode 100644
> index 000000000000..61b65f8b1ace
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.c
> @@ -0,0 +1,277 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hisilicon Kirin SoCs drm master driver
> + *
> + * Copyright (c) 2016 Linaro Limited.
> + * Copyright (c) 2014-2016 Hisilicon Limited.
> + * Copyright (c) 2014-2020, Huawei Technologies Co., Ltd
> + * Author:
> + *	<cailiwei@hisilicon.com>
> + *	<zhengwanchun@hisilicon.com>
> + */
> +
> +#include <linux/of_platform.h>
> +#include <linux/component.h>
> +#include <linux/of_graph.h>
Sort includes

> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/drm_managed.h>
Sort includes

> +
> +#include "kirin9xx_dpe.h"
> +#include "kirin9xx_drm_drv.h"
> +
> +static int kirin_drm_kms_cleanup(struct drm_device *dev)
> +{
> +	struct kirin_drm_private *priv = to_drm_private(dev);
> +
> +	if (priv->fbdev)
> +		priv->fbdev = NULL;
> +
> +	drm_kms_helper_poll_fini(dev);
> +	kirin9xx_dss_drm_cleanup(dev);
> +
> +	return 0;
> +}
> +
> +static void kirin_fbdev_output_poll_changed(struct drm_device *dev)
> +{
> +	struct kirin_drm_private *priv = to_drm_private(dev);
> +
> +	dsi_set_output_client(dev);
> +
> +	drm_fb_helper_hotplug_event(priv->fbdev);
> +}
> +
> +static const struct drm_mode_config_funcs kirin_drm_mode_config_funcs = {
> +	.fb_create = drm_gem_fb_create,
> +	.output_poll_changed = kirin_fbdev_output_poll_changed,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static int kirin_drm_kms_init(struct drm_device *dev)
> +{
> +	long kirin_type;
> +	int ret;
> +
> +	dev_set_drvdata(dev->dev, dev);
> +
> +	ret = drmm_mode_config_init(dev);
> +	if (ret)
> +		return ret;
> +
> +	dev->mode_config.min_width = 0;
> +	dev->mode_config.min_height = 0;
> +	dev->mode_config.max_width = 2048;
> +	dev->mode_config.max_height = 2048;
> +	dev->mode_config.preferred_depth = 32;
> +
> +	dev->mode_config.funcs = &kirin_drm_mode_config_funcs;
> +
> +	/* display controller init */
> +	kirin_type = (long)of_device_get_match_data(dev->dev);
> +	if (WARN_ON(!kirin_type))
> +		return -ENODEV;
> +
> +	ret = dss_drm_init(dev, kirin_type);
> +	if (ret)
> +		return ret;
> +
> +	/* bind and init sub drivers */
> +	ret = component_bind_all(dev->dev, dev);
> +	if (ret) {
> +		drm_err(dev, "failed to bind all component.\n");
> +		return ret;
> +	}
> +
> +	/* vblank init */
> +	ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
> +	if (ret) {
> +		drm_err(dev, "failed to initialize vblank.\n");
> +		return ret;
> +	}
> +	/* with irq_enabled = true, we can use the vblank feature. */
> +	dev->irq_enabled = true;
> +
> +	/* reset all the states of crtc/plane/encoder/connector */
> +	drm_mode_config_reset(dev);
> +
> +	/* init kms poll for handling hpd */
> +	drm_kms_helper_poll_init(dev);
> +
> +	return 0;
> +}
> +
> +DEFINE_DRM_GEM_CMA_FOPS(kirin_drm_fops);
Move it to right above kirin_drm_driver where it is used

> +
> +static int kirin_drm_connectors_register(struct drm_device *dev)
> +{
> +	struct drm_connector_list_iter conn_iter;
> +	struct drm_connector *failed_connector;
> +	struct drm_connector *connector;
> +	int ret;
> +
> +	mutex_lock(&dev->mode_config.mutex);
> +	drm_connector_list_iter_begin(dev, &conn_iter);
> +	drm_for_each_connector_iter(connector, &conn_iter) {
> +		ret = drm_connector_register(connector);
> +		if (ret) {
> +			failed_connector = connector;
> +			goto err;
> +		}
> +	}
> +	mutex_unlock(&dev->mode_config.mutex);
> +
> +	return 0;
> +
> +err:
> +	drm_connector_list_iter_begin(dev, &conn_iter);
> +	drm_for_each_connector_iter(connector, &conn_iter) {
> +		if (failed_connector == connector)
> +			break;
> +		drm_connector_unregister(connector);
> +	}
> +	mutex_unlock(&dev->mode_config.mutex);
> +
> +	return ret;
> +}
> +
> +static struct drm_driver kirin_drm_driver = {
> +	.driver_features	= DRIVER_GEM | DRIVER_MODESET |
> +				  DRIVER_ATOMIC | DRIVER_RENDER,
> +
> +	.fops			= &kirin_drm_fops,
> +	.name			= "kirin9xx",
> +	.desc			= "Hisilicon Kirin9xx SoCs' DRM Driver",
> +	.date			= "20170309",
> +	.major			= 1,
> +	.minor			= 0,
> +
> +	DRM_GEM_CMA_VMAP_DRIVER_OPS
> +};
Looks good now.

> +
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> +	return dev->of_node == data;
> +}
> +
> +static int kirin_drm_bind(struct device *dev)
> +{
> +	struct kirin_drm_private *priv;
> +	struct drm_device *drm;
> +	int ret;
> +
> +	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	drm = &priv->drm;
> +
> +	ret = devm_drm_dev_init(dev, drm, &kirin_drm_driver);
> +	if (ret) {
> +		kfree(priv);
> +		return ret;
> +	}
> +	drmm_add_final_kfree(drm, priv);
> +
> +	ret = kirin_drm_kms_init(drm);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_dev_register(drm, 0);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(drm, 0);
Should be last - after connector register.

> +
> +	/* connectors should be registered after drm device register */
> +	ret = kirin_drm_connectors_register(drm);
> +	if (ret)
> +		goto err_drm_dev_unregister;
I am rather sure registering connectors are already taken care of by
drm_dev_register(). 
The driver set DRIVER_MODESET so drm_modeset_register_all() is called
which again registers all connectors.

> +
> +	return 0;
> +
> +err_drm_dev_unregister:
> +	drm_dev_unregister(drm);
> +	kirin_drm_kms_cleanup(drm);

> +	drm_dev_put(drm);
Not needed when using drmm_* and embedded drm_device
> +
> +	return ret;
> +}
> +
> +static void kirin_drm_unbind(struct device *dev)
> +{
> +	struct drm_device *drm_dev = dev_get_drvdata(dev);
> +
> +	drm_dev_unregister(drm_dev);
> +	drm_atomic_helper_shutdown(drm_dev);
> +	kirin_drm_kms_cleanup(drm_dev);

> +	drm_dev_put(drm_dev);
Not needed when using drmm_* and embedded drm_device

> +}
> +
> +static const struct component_master_ops kirin_drm_ops = {
> +	.bind = kirin_drm_bind,
> +	.unbind = kirin_drm_unbind,
> +};
> +
> +static int kirin_drm_platform_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct component_match *match = NULL;
> +	struct device_node *remote;
> +
> +	remote = of_graph_get_remote_node(np, 0, 0);
> +	if (!remote)
> +		return -ENODEV;
> +
> +	drm_of_component_match_add(dev, &match, compare_of, remote);
> +	of_node_put(remote);
> +
> +	return component_master_add_with_match(dev, &kirin_drm_ops, match);
> +}
> +
> +static int kirin_drm_platform_remove(struct platform_device *pdev)
> +{
> +	component_master_del(&pdev->dev, &kirin_drm_ops);
> +	return 0;
> +}
> +
> +static const struct of_device_id kirin_drm_dt_ids[] = {
> +	{ .compatible = "hisilicon,hi3660-dpe",
> +	  .data = (void *)FB_ACCEL_HI366x,
> +	},
> +	{ .compatible = "hisilicon,kirin970-dpe",
> +	  .data = (void *)FB_ACCEL_KIRIN970,
> +	},
> +	{ /* end node */ },
> +};
> +MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids);
> +
> +static struct platform_driver kirin_drm_platform_driver = {
> +	.probe = kirin_drm_platform_probe,
> +	.remove = kirin_drm_platform_remove,
> +	.suspend = kirin9xx_dss_drm_suspend,
> +	.resume = kirin9xx_dss_drm_resume,
> +	.driver = {
> +		.name = "kirin9xx-drm",
> +		.of_match_table = kirin_drm_dt_ids,
> +	},
> +};
> +
> +module_platform_driver(kirin_drm_platform_driver);
> +
> +MODULE_AUTHOR("cailiwei <cailiwei@hisilicon.com>");
> +MODULE_AUTHOR("zhengwanchun <zhengwanchun@hisilicon.com>");
> +MODULE_DESCRIPTION("hisilicon Kirin SoCs' DRM master driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> new file mode 100644
> index 000000000000..9bfcb35d6fa5
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_drv.h
> @@ -0,0 +1,42 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2016 Linaro Limited.
> + * Copyright (c) 2014-2016 Hisilicon Limited.
> + * Copyright (c) 2014-2020, Huawei Technologies Co., Ltd
> + */
> +
> +#ifndef __KIRIN_DRM_DRV_H__
> +#define __KIRIN_DRM_DRV_H__
> +
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_fb_helper.h>
> +#include <drm/drm_print.h>
> +
> +#include <linux/iommu.h>
> +
> +#define MAX_CRTC	2
> +
> +struct kirin_drm_private {
> +	struct drm_device drm;
Hmm, hare we have drm_device embedded - so some comments from previous
review can be ignored - ups.

> +	struct drm_fb_helper *fbdev;
Never assigned - can be deleted.
kirin_fbdev_output_poll_changed() needs to be re-visited as it assumes
fbdev is assigned.

> +	struct drm_crtc *crtc[MAX_CRTC];
> +};
> +
> +struct kirin_fbdev {
> +	struct drm_fb_helper fb_helper;
> +	struct drm_framebuffer *fb;
> +};
struct kirin_fbdev is unused - delete.

> +
> +/* provided by kirin9xx_drm_dss.c */
> +void kirin9xx_dss_drm_cleanup(struct drm_device *dev);
> +int kirin9xx_dss_drm_suspend(struct platform_device *pdev, pm_message_t state);
> +int kirin9xx_dss_drm_resume(struct platform_device *pdev);
> +int dss_drm_init(struct drm_device *dev, u32 g_dss_version_tag);
> +
> +void dsi_set_output_client(struct drm_device *dev);
> +
> +#define to_drm_private(d) container_of(d, struct kirin_drm_private, drm)
> +
> +#endif /* __KIRIN_DRM_DRV_H__ */
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
> new file mode 100644
> index 000000000000..ff93df229868
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_dss.c
> @@ -0,0 +1,979 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Hisilicon Hi6220 SoC ADE(Advanced Display Engine)'s crtc&plane driver
> + *
> + * Copyright (c) 2016 Linaro Limited.
> + * Copyright (c) 2014-2016 Hisilicon Limited.
> + * Copyright (c) 2014-2020, Huawei Technologies Co., Ltd
> + *
> + * Author:
> + *	Xinliang Liu <z.liuxinliang@hisilicon.com>
> + *	Xinliang Liu <xinliang.liu@linaro.org>
> + *	Xinwei Kong <kong.kongxinwei@hisilicon.com>
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <video/display_timing.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/of_address.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +#include <drm/drm_vblank.h>
> +#include <drm/drm_modeset_helper_vtables.h>
> +
> +#include "kirin9xx_drm_drv.h"
> +#include "kirin9xx_drm_dpe_utils.h"
> +#include "kirin9xx_dpe.h"
> +
> +int hdmi_pxl_ppll7_init(struct dss_hw_ctx *ctx, u64 pixel_clock)
> +{
> +	u64 vco_min_freq_output = KIRIN970_VCO_MIN_FREQ_OUTPUT;
> +	u64 refdiv, fbdiv, frac, postdiv1 = 0, postdiv2 = 0;
> +	u64 dss_pxl0_clk = 7 * 144000000UL;
> +	u64 sys_clock_fref = KIRIN970_SYS_19M2;
> +	u64 ppll7_freq_divider;
> +	u64 vco_freq_output;
> +	u64 frac_range = 0x1000000; /* 2^24 */
> +	u64 pixel_clock_ori;
> +	u64 pixel_clock_cur;
> +	u32 ppll7ctrl0;
> +	u32 ppll7ctrl1;
> +	u32 ppll7ctrl0_val;
> +	u32 ppll7ctrl1_val;
> +	int ceil_temp;
> +	int i, ret;
> +	const int freq_divider_list[22] = {
> +		1,  2,  3,  4,  5,  6,  7,  8,
> +		9, 10, 12, 14, 15, 16, 20, 21,
> +		24, 25, 30, 36, 42, 49
> +	};
> +	const int postdiv1_list[22] = {
> +		1, 2, 3, 4, 5, 6, 7, 4, 3, 5,
> +		4, 7, 5, 4, 5, 7, 6, 5, 6, 6,
> +		7, 7
> +	};
> +	const int postdiv2_list[22] = {
> +		1, 1, 1, 1, 1, 1, 1, 2, 3, 2,
> +		3, 2, 3, 4, 4, 3, 4, 5, 5, 6,
> +		6, 7
> +	};
> +
> +	if (!pixel_clock) {
> +		drm_err(ctx->dev, "Pixel clock can't be zero!\n");
> +		return -EINVAL;
> +	}
> +
> +	pixel_clock_ori = pixel_clock;
> +	pixel_clock_cur = pixel_clock_ori;
> +
> +	if (pixel_clock_ori <= 255000000) {
> +		pixel_clock_cur *= 7;
> +		dss_pxl0_clk /= 7;
> +	} else if (pixel_clock_ori <= 415000000) {
> +		pixel_clock_cur *= 5;
> +		dss_pxl0_clk /= 5;
> +	} else if (pixel_clock_ori <= 594000000) {
> +		pixel_clock_cur *= 3;
> +		dss_pxl0_clk /= 3;
> +	} else {
> +		drm_err(ctx->dev, "Clock not supported!\n");
> +		return -EINVAL;
> +	}
> +
> +	pixel_clock_cur = pixel_clock_cur / 1000;
> +	if (!pixel_clock_cur) {
> +		drm_err(ctx->dev, "pixel_clock_cur can't be zero!\n");
> +		return -EINVAL;
> +	}
> +
> +	ceil_temp = DIV_ROUND_UP(vco_min_freq_output, pixel_clock_cur);
> +
> +	ppll7_freq_divider = (u64)ceil_temp;
> +
> +	for (i = 0; i < ARRAY_SIZE(freq_divider_list); i++) {
> +		if (freq_divider_list[i] >= ppll7_freq_divider) {
> +			ppll7_freq_divider = freq_divider_list[i];
> +			postdiv1 = postdiv1_list[i];
> +			postdiv2 = postdiv2_list[i];
> +			break;
> +		}
> +	}
> +
> +	if (i == ARRAY_SIZE(freq_divider_list)) {
> +		drm_err(ctx->dev, "Can't find a valid setting for PLL7!\n");
> +		return -EINVAL;
> +	}
> +
> +	vco_freq_output = ppll7_freq_divider * pixel_clock_cur;
> +	if (!vco_freq_output) {
> +		drm_err(ctx->dev, "Can't find a valid setting for VCO_FREQ!\n");
> +		return -EINVAL;
> +	}
> +
> +	ceil_temp = DIV_ROUND_UP(400000, vco_freq_output);
> +
> +	refdiv = ((vco_freq_output * ceil_temp) >= 494000) ? 1 : 2;
> +	fbdiv = (vco_freq_output * ceil_temp) * refdiv / sys_clock_fref;
> +
> +	frac = (u64)(ceil_temp * vco_freq_output - sys_clock_fref / refdiv * fbdiv) * refdiv * frac_range;
> +	frac = (u64)frac / sys_clock_fref;
> +
> +	ppll7ctrl0 = readl(ctx->pmctrl_base + MIDIA_PPLL7_CTRL0);
> +	ppll7ctrl0 &= ~MIDIA_PPLL7_FREQ_DEVIDER_MASK;
> +
> +	ppll7ctrl0_val = 0x0;
> +	ppll7ctrl0_val |= (u32)(postdiv2 << 23 | postdiv1 << 20 | fbdiv << 8 | refdiv << 2);
> +	ppll7ctrl0_val &= MIDIA_PPLL7_FREQ_DEVIDER_MASK;
> +	ppll7ctrl0 |= ppll7ctrl0_val;
> +
> +	writel(ppll7ctrl0, ctx->pmctrl_base + MIDIA_PPLL7_CTRL0);
> +
> +	ppll7ctrl1 = readl(ctx->pmctrl_base + MIDIA_PPLL7_CTRL1);
> +	ppll7ctrl1 &= ~MIDIA_PPLL7_FRAC_MODE_MASK;
> +
> +	ppll7ctrl1_val = 0x0;
> +	ppll7ctrl1_val |= (u32)(1 << 25 | 0 << 24 | frac);
> +	ppll7ctrl1_val &= MIDIA_PPLL7_FRAC_MODE_MASK;
> +	ppll7ctrl1 |= ppll7ctrl1_val;
> +
> +	writel(ppll7ctrl1, ctx->pmctrl_base + MIDIA_PPLL7_CTRL1);
> +
> +	drm_dbg(ctx->dev, "PLL7 set to (0x%0x, 0x%0x)\n",
> +		ppll7ctrl0, ppll7ctrl1);
> +
> +	ret = clk_set_rate(ctx->dss_pxl0_clk, dss_pxl0_clk);
> +	if (ret < 0)
> +		drm_err(ctx->dev, "%s: clk_set_rate(dss_pxl0_clk, %llu) failed: %d!\n",
> +			  __func__, dss_pxl0_clk, ret);
> +	else
> +		drm_dbg(ctx->dev, "dss_pxl0_clk:[%llu]->[%lu].\n",
> +			dss_pxl0_clk, clk_get_rate(ctx->dss_pxl0_clk));
> +
> +	return ret;
> +}
> +
> +static u64 dss_calculate_clock(struct dss_crtc *acrtc,
> +			       const struct drm_display_mode *mode)
> +{
> +	u64 clk_Hz;
> +
> +	if (acrtc->ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		if (mode->clock == 148500)
> +			clk_Hz = 144000 * 1000UL;
> +		else if (mode->clock == 83496)
> +			clk_Hz = 84000 * 1000UL;
> +		else if (mode->clock == 74440)
> +			clk_Hz = 72000 * 1000UL;
> +		else if (mode->clock == 74250)
> +			clk_Hz = 72000 * 1000UL;
> +		else
> +			clk_Hz = mode->clock * 1000UL;
> +
> +		/* Adjust pixel clock for compatibility with 10.1 inch special displays. */
> +		if (mode->clock == 148500 && mode->width_mm == 532 && mode->height_mm == 299)
> +			clk_Hz = 152000 * 1000UL;
> +	} else {
> +		if (mode->clock == 148500)
> +			clk_Hz = 144000 * 1000UL;
> +		else if (mode->clock == 83496)
> +			clk_Hz = 80000 * 1000UL;
> +		else if (mode->clock == 74440)
> +			clk_Hz = 72000 * 1000UL;
> +		else if (mode->clock == 74250)
> +			clk_Hz = 72000 * 1000UL;
> +		else
> +			clk_Hz = mode->clock * 1000UL;
> +	}
> +
> +	return clk_Hz;
> +}
> +
> +static void dss_ldi_set_mode(struct dss_crtc *acrtc)
> +{
> +	struct drm_display_mode *adj_mode = &acrtc->base.state->adjusted_mode;
> +	struct drm_display_mode *mode = &acrtc->base.state->mode;
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	u32 clock = mode->clock;
> +	u64 clk_Hz;
> +	int ret;
> +
> +	clk_Hz = dss_calculate_clock(acrtc, mode);
> +
> +	drm_dbg(ctx->dev, "Requested clock %u kHz, setting to %llu kHz\n",
> +		clock, clk_Hz / 1000);
> +
> +	if (acrtc->ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		ret = hdmi_pxl_ppll7_init(ctx, clk_Hz);
> +	} else {
> +		ret = clk_set_rate(ctx->dss_pxl0_clk, clk_Hz);
> +		if (!ret) {
> +			clk_Hz = clk_get_rate(ctx->dss_pxl0_clk);
> +			drm_dbg(ctx->dev, "dss_pxl0_clk:[%llu]->[%lu].\n",
> +				clk_Hz, clk_get_rate(ctx->dss_pxl0_clk));
> +		}
> +	}
> +
> +	if (ret)
> +		drm_err(ctx->dev, "failed to set pixel clock\n");
> +	else
> +		adj_mode->clock = clk_Hz / 1000;
> +
> +	dpe_init(acrtc);
> +}
> +
> +static int dss_power_up(struct dss_crtc *acrtc)
> +{
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	int ret = 0;
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		dpe_common_clk_enable(ctx);
> +		dpe_inner_clk_enable(ctx);
> +		dpe_set_clk_rate(ctx);
> +	} else {
> +		ret = clk_prepare_enable(ctx->dss_pxl0_clk);
> +		if (ret) {
> +			drm_err(ctx->dev,
> +				"failed to enable dss_pxl0_clk (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		ret = clk_prepare_enable(ctx->dss_pri_clk);
> +		if (ret) {
> +			drm_err(ctx->dev,
> +				"failed to enable dss_pri_clk (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		ret = clk_prepare_enable(ctx->dss_pclk_dss_clk);
> +		if (ret) {
> +			drm_err(ctx->dev,
> +				"failed to enable dss_pclk_dss_clk (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		ret = clk_prepare_enable(ctx->dss_axi_clk);
> +		if (ret) {
> +			drm_err(ctx->dev,
> +				"failed to enable dss_axi_clk (%d)\n", ret);
> +			return ret;
> +		}
> +
> +		ret = clk_prepare_enable(ctx->dss_mmbuf_clk);
> +		if (ret) {
> +			drm_err(ctx->dev,
> +				"failed to enable dss_mmbuf_clk (%d)\n", ret);
> +			return ret;
> +		}
> +	}
> +
> +	dss_inner_clk_common_enable(ctx);
> +	dss_inner_clk_pdp_enable(ctx);
> +
> +	dpe_interrupt_mask(acrtc);
> +	dpe_interrupt_clear(acrtc);
> +	dpe_irq_enable(acrtc);
> +	dpe_interrupt_unmask(acrtc);
> +
> +	ctx->power_on = true;
> +
> +	return ret;
> +}
> +
> +static void dss_power_down(struct dss_crtc *acrtc)
> +{
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	dpe_interrupt_mask(acrtc);
> +	dpe_irq_disable(acrtc);
> +	dpe_deinit(acrtc);
> +
> +	dpe_check_itf_status(acrtc);
> +	dss_inner_clk_pdp_disable(ctx);
> +
> +	dpe_inner_clk_disable(ctx);
> +	dpe_common_clk_disable(ctx);
> +
> +	ctx->power_on = false;
> +}
> +
> +static int dss_enable_vblank(struct drm_crtc *crtc)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	drm_dbg(ctx->dev, "%s\n", __func__);
> +	if (!ctx->power_on) {
> +		drm_dbg(ctx->dev, "Enabling vblank\n");
> +		(void)dss_power_up(acrtc);
> +	}
> +
> +	return 0;
> +}
enable_vblank is supposed to enable interrupts, not a general power up.
Power up should be done by another helper.

> +
> +static void dss_disable_vblank(struct drm_crtc *crtc)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	drm_dbg(ctx->dev, "%s\n", __func__);
> +	if (!ctx->power_on) {
> +		drm_err(ctx->dev, "power is down! vblank disable fail\n");
> +		return;
> +	}
> +}
Same here, just disable vblank

> +
> +static irqreturn_t dss_irq_handler(int irq, void *data)
> +{
> +	struct dss_crtc *acrtc = data;
> +	struct drm_crtc *crtc = &acrtc->base;
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	void __iomem *dss_base = ctx->base;
> +
> +	u32 isr_s1 = 0;
> +	u32 isr_s2 = 0;
> +	u32 mask = 0;
> +
> +	isr_s1 = readl(dss_base + GLB_CPU_PDP_INTS);
> +	isr_s2 = readl(dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INTS);
> +
> +	writel(isr_s2, dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INTS);
> +	writel(isr_s1, dss_base + GLB_CPU_PDP_INTS);
> +
> +	isr_s1 &= ~(readl(dss_base + GLB_CPU_PDP_INT_MSK));
> +	isr_s2 &= ~(readl(dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INT_MSK));
> +
> +	if (isr_s2 & BIT_VSYNC) {
> +		ctx->vsync_timestamp = ktime_get();
> +		drm_crtc_handle_vblank(crtc);
> +	}
> +
> +	if (isr_s2 & BIT_LDI_UNFLOW) {
> +		mask = readl(dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INT_MSK);
> +		mask |= BIT_LDI_UNFLOW;
> +		writel(mask, dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INT_MSK);
> +
> +		drm_err(ctx->dev, "ldi underflow!\n");
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static bool dss_crtc_mode_fixup(struct drm_crtc *crtc,
> +				const struct drm_display_mode *mode,
> +				struct drm_display_mode *adj_mode)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	u64 clk_Hz;
> +
> +	/* Check if clock is too high */
> +	if (mode->clock > 594000)
> +		return false;
> +	/*
> +	 * FIXME: the code should, instead, do some calculus instead of
> +	 * hardcoding the modes. Clearly, there's something missing at
> +	 * dss_calculate_clock()
> +	 */
> +
> +	/*
> +	 * HACK: reports at Hikey 970 mailing lists with the downstream
> +	 * Official Linaro 4.9 driver seem to indicate that some monitor
> +	 * modes aren't properly set. There, this hack was added.
> +	 *
> +	 * On my monitors, this wasn't needed, but better to keep this
> +	 * code here, together with this notice, just in case.
> +	 */
> +	if ((mode->hdisplay    == 1920 && mode->vdisplay == 1080 && mode->clock == 148500)
> +	    || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock == 148352)
> +	    || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock ==  80192)
> +	    || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock ==  74250)
> +	    || (mode->hdisplay == 1920 && mode->vdisplay == 1080 && mode->clock ==  61855)
> +	    || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 147116)
> +	    || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 146250)
> +	    || (mode->hdisplay == 1680 && mode->vdisplay == 1050 && mode->clock == 144589)
> +	    || (mode->hdisplay == 1600 && mode->vdisplay == 1200 && mode->clock == 160961)
> +	    || (mode->hdisplay == 1600 && mode->vdisplay == 900  && mode->clock == 118963)
> +	    || (mode->hdisplay == 1440 && mode->vdisplay == 900  && mode->clock == 126991)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock == 128946)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 1024 && mode->clock ==  98619)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 960  && mode->clock == 102081)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 800  && mode->clock ==  83496)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 720  && mode->clock ==  74440)
> +	    || (mode->hdisplay == 1280 && mode->vdisplay == 720  && mode->clock ==  74250)
> +	    || (mode->hdisplay == 1024 && mode->vdisplay == 768  && mode->clock ==  78800)
> +	    || (mode->hdisplay == 1024 && mode->vdisplay == 768  && mode->clock ==  75000)
> +	    || (mode->hdisplay == 1024 && mode->vdisplay == 768  && mode->clock ==  81833)
> +	    || (mode->hdisplay == 800  && mode->vdisplay == 600  && mode->clock ==  48907)
> +	    || (mode->hdisplay == 800  && mode->vdisplay == 600  && mode->clock ==  40000)
> +	    || (mode->hdisplay == 800  && mode->vdisplay == 480  && mode->clock ==  32000)
> +	   ) {
> +		clk_Hz = dss_calculate_clock(acrtc, mode);
> +
> +		/*
> +		 * On Kirin970, PXL0 clock is set to a const value,
> +		 * independently of the pixel clock.
> +		 */
> +		if (acrtc->ctx->g_dss_version_tag != FB_ACCEL_KIRIN970)
> +			clk_Hz = clk_round_rate(ctx->dss_pxl0_clk, mode->clock * 1000);
> +
> +		adj_mode->clock = clk_Hz / 1000;
> +
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static void dss_crtc_enable(struct drm_crtc *crtc,
> +			    struct drm_crtc_state *old_state)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	int ret;
> +
> +	if (acrtc->enable)
> +		return;
> +
> +	if (!ctx->power_on) {
> +		ret = dss_power_up(acrtc);
> +		if (ret)
> +			return;
> +	}
> +
> +	acrtc->enable = true;
> +	drm_crtc_vblank_on(crtc);
> +}
> +
> +static void dss_crtc_disable(struct drm_crtc *crtc,
> +			     struct drm_crtc_state *old_state)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +
> +	if (!acrtc->enable)
> +		return;
> +
> +	dss_power_down(acrtc);
> +	acrtc->enable = false;
> +	drm_crtc_vblank_off(crtc);
> +}
> +
> +static void dss_crtc_mode_set_nofb(struct drm_crtc *crtc)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	if (!ctx->power_on)
> +		(void)dss_power_up(acrtc);
> +	dss_ldi_set_mode(acrtc);
> +}
> +
> +static void dss_crtc_atomic_begin(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +{
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	if (!ctx->power_on)
> +		(void)dss_power_up(acrtc);
> +}
> +
> +static void dss_crtc_atomic_flush(struct drm_crtc *crtc,
> +				  struct drm_crtc_state *old_state)
> +
> +{
> +	struct drm_pending_vblank_event *event = crtc->state->event;
> +
> +	if (event) {
> +		crtc->state->event = NULL;
> +
> +		spin_lock_irq(&crtc->dev->event_lock);
> +		if (drm_crtc_vblank_get(crtc) == 0)
> +			drm_crtc_arm_vblank_event(crtc, event);
> +		else
> +			drm_crtc_send_vblank_event(crtc, event);
> +		spin_unlock_irq(&crtc->dev->event_lock);
> +	}
> +}
> +
> +static const struct drm_crtc_helper_funcs dss_crtc_helper_funcs = {
> +	.mode_fixup	= dss_crtc_mode_fixup,
> +	.atomic_enable	= dss_crtc_enable,
> +	.atomic_disable	= dss_crtc_disable,
> +	.mode_set_nofb	= dss_crtc_mode_set_nofb,
> +	.atomic_begin	= dss_crtc_atomic_begin,
> +	.atomic_flush	= dss_crtc_atomic_flush,
> +};
> +
> +static const struct drm_crtc_funcs dss_crtc_funcs = {
> +	.destroy	= drm_crtc_cleanup,
> +	.set_config	= drm_atomic_helper_set_config,
> +	.page_flip	= drm_atomic_helper_page_flip,
> +	.reset		= drm_atomic_helper_crtc_reset,
> +	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
> +	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
> +	.enable_vblank = dss_enable_vblank,
> +	.disable_vblank = dss_disable_vblank,
> +};
> +
> +static int dss_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
> +			 struct drm_plane *plane)
> +{
> +	struct kirin_drm_private *priv = to_drm_private(dev);
> +	struct device_node *port;
> +	int ret;
> +
> +	/* set crtc port so that
> +	 * drm_of_find_possible_crtcs call works
> +	 */
> +	port = of_get_child_by_name(dev->dev->of_node, "port");
> +	if (!port) {
> +		drm_err(dev, "no port node found in %s\n",
> +			dev->dev->of_node->full_name);
> +		return -EINVAL;
> +	}
> +	of_node_put(port);
> +	crtc->port = port;
> +
> +	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
> +					&dss_crtc_funcs, NULL);
> +	if (ret) {
> +		drm_err(dev, "failed to init crtc.\n");
> +		return ret;
> +	}
> +
> +	drm_crtc_helper_add(crtc, &dss_crtc_helper_funcs);
> +	priv->crtc[drm_crtc_index(crtc)] = crtc;
> +
> +	return 0;
> +}
> +
> +static int dss_plane_atomic_check(struct drm_plane *plane,
> +				  struct drm_plane_state *state)
> +{
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_crtc *crtc = state->crtc;
> +	struct dss_crtc *acrtc = to_dss_crtc(crtc);
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	struct drm_crtc_state *crtc_state;
> +	u32 src_x = state->src_x >> 16;
> +	u32 src_y = state->src_y >> 16;
> +	u32 src_w = state->src_w >> 16;
> +	u32 src_h = state->src_h >> 16;
> +	int crtc_x = state->crtc_x;
> +	int crtc_y = state->crtc_y;
> +	u32 crtc_w = state->crtc_w;
> +	u32 crtc_h = state->crtc_h;
> +	u32 fmt;
> +
> +	if (!crtc || !fb)
> +		return 0;
> +
> +	fmt = dpe_get_format(ctx, fb->format->format);
> +	if (fmt == HISI_FB_PIXEL_FORMAT_UNSUPPORT)
> +		return -EINVAL;
> +
> +	crtc_state = drm_atomic_get_crtc_state(state->state, crtc);
> +	if (IS_ERR(crtc_state))
> +		return PTR_ERR(crtc_state);
> +
> +	if (src_w != crtc_w || src_h != crtc_h) {
> +		drm_err(ctx->dev, "Scale not support!!!\n");
> +		return -EINVAL;
> +	}
> +
> +	if (src_x + src_w > fb->width ||
> +	    src_y + src_h > fb->height)
> +		return -EINVAL;
> +
> +	if (crtc_x < 0 || crtc_y < 0)
> +		return -EINVAL;
> +
> +	if (crtc_x + crtc_w > crtc_state->adjusted_mode.hdisplay ||
> +	    crtc_y + crtc_h > crtc_state->adjusted_mode.vdisplay)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void dss_plane_atomic_update(struct drm_plane *plane,
> +				    struct drm_plane_state *old_state)
> +{
> +	struct drm_plane_state *state = plane->state;
> +
> +	if (!state->fb) {
> +		state->visible = false;
> +		return;
> +	}
> +
> +	hisi_fb_pan_display(plane);
> +}
> +
> +static void dss_plane_atomic_disable(struct drm_plane *plane,
> +				     struct drm_plane_state *old_state)
> +{
> +	/* FIXME: Maybe this? */
> +#if 0
> +	struct dss_plane *aplane = to_dss_plane(plane);
> +	struct dss_crtc *acrtc = aplane->acrtc;
> +
> +	disable_ldi(acrtc);
> +	hisifb_mctl_sw_clr(acrtc);
> +#endif
> +}
> +
> +static const struct drm_plane_helper_funcs dss_plane_helper_funcs = {
> +	.atomic_check = dss_plane_atomic_check,
> +	.atomic_update = dss_plane_atomic_update,
> +	.atomic_disable = dss_plane_atomic_disable,
> +};
> +
> +static struct drm_plane_funcs dss_plane_funcs = {
> +	.update_plane	= drm_atomic_helper_update_plane,
> +	.disable_plane	= drm_atomic_helper_disable_plane,
> +	.destroy = drm_plane_cleanup,
> +	.reset = drm_atomic_helper_plane_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
> +};
> +
> +static int dss_plane_init(struct drm_device *dev, struct dss_plane *aplane,
> +			  enum drm_plane_type type)
> +{
> +	const u32 *fmts;
> +	u32 fmts_cnt;
> +	int ret = 0;
> +
> +	/* get properties */
> +	fmts_cnt = hisi_dss_get_channel_formats(dev, aplane->ch, &fmts);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_universal_plane_init(dev, &aplane->base, 1, &dss_plane_funcs,
> +				       fmts, fmts_cnt, NULL,
> +				       type, NULL);
> +	if (ret) {
> +		drm_err(dev, "fail to init plane, ch=%d\n", aplane->ch);
> +		return ret;
> +	}
> +
> +	drm_plane_helper_add(&aplane->base, &dss_plane_helper_funcs);
> +
> +	return 0;
> +}
> +
> +static int dss_enable_iommu(struct platform_device *pdev, struct dss_hw_ctx *ctx)
> +{
> +#if 0
> +/*
> + * FIXME:
> + *
> + * Right now, the IOMMU support is actually disabled. See the caller of
> + * hisi_dss_smmu_config(). Yet, if we end enabling it, this should be
> + * ported to use io-pgtable directly.
> + */
> +	struct device *dev = NULL;
> +
> +	dev = &pdev->dev;
> +
> +	/* create iommu domain */
> +	ctx->mmu_domain = iommu_domain_alloc(dev->bus);
> +	if (!ctx->mmu_domain) {
> +		drm_err(ctx->dev, "iommu_domain_alloc failed!\n");
> +		return -EINVAL;
> +	}
> +
> +	iommu_attach_device(ctx->mmu_domain, dev);
> +#endif
> +	return 0;
> +}
> +
> +static int dss_dts_parse(struct platform_device *pdev, struct dss_hw_ctx *ctx)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = NULL;
> +	const char *compatible;
> +	int ret = 0;
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)
> +		compatible = "hisilicon,kirin970-dpe";
> +	else
> +		compatible = "hisilicon,hi3660-dpe";
> +
> +	np = of_find_compatible_node(NULL, NULL, compatible);
> +	if (!np) {
> +		drm_err(ctx->dev, "NOT FOUND device node %s!\n", compatible);
> +		return -ENXIO;
> +	}
> +
> +	/* Initialize version-specific data */
> +	if (ctx->g_dss_version_tag == FB_ACCEL_HI366x)
> +		kirin960_dpe_defs(ctx);
> +	else
> +		kirin970_dpe_defs(ctx);
> +
> +	ctx->base = of_iomap(np, 0);
> +	if (!(ctx->base)) {
> +		drm_err(ctx->dev, "failed to get dss base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	ctx->peri_crg_base  = of_iomap(np, 1);
> +	if (!(ctx->peri_crg_base)) {
> +		drm_err(ctx->dev, "failed to get dss peri_crg_base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	ctx->sctrl_base  = of_iomap(np, 2);
> +	if (!(ctx->sctrl_base)) {
> +		drm_err(ctx->dev, "failed to get dss sctrl_base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		ctx->pctrl_base = of_iomap(np, 3);
> +		if (!(ctx->pctrl_base)) {
> +			drm_err(ctx->dev,
> +				"failed to get dss pctrl_base resource.\n");
> +			return -ENXIO;
> +		}
> +	} else {
> +		ctx->pmc_base = of_iomap(np, 3);
> +		if (!(ctx->pmc_base)) {
> +			drm_err(ctx->dev,
> +				"failed to get dss pmc_base resource.\n");
> +			return -ENXIO;
> +		}
> +	}
> +
> +	ctx->noc_dss_base = of_iomap(np, 4);
> +	if (!(ctx->noc_dss_base)) {
> +		drm_err(ctx->dev, "failed to get noc_dss_base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		ctx->pmctrl_base = of_iomap(np, 5);
> +		if (!(ctx->pmctrl_base)) {
> +			drm_err(ctx->dev,
> +				"failed to get dss pmctrl_base resource.\n");
> +			return -ENXIO;
> +		}
> +
> +		ctx->media_crg_base = of_iomap(np, 6);
> +		if (!(ctx->media_crg_base)) {
> +			drm_err(ctx->dev,
> +				"failed to get dss media_crg_base resource.\n");
> +			return -ENXIO;
> +		}
> +	}
> +
> +	/* get irq no */
> +	ctx->irq = irq_of_parse_and_map(np, 0);
> +	if (ctx->irq <= 0) {
> +		drm_err(ctx->dev, "failed to get irq_pdp resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	drm_dbg(ctx->dev, "dss irq = %d.\n", ctx->irq);
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		ctx->dpe_regulator = devm_regulator_get(dev, REGULATOR_PDP_NAME);
> +		if (!ctx->dpe_regulator) {
> +			drm_err(ctx->dev,
> +				"failed to get dpe_regulator resource! ret=%d.\n",
> +				ret);
> +			return -ENXIO;
> +		}
> +	}
> +
> +	ctx->dss_mmbuf_clk = devm_clk_get(dev, "clk_dss_axi_mm");
> +	ret = PTR_ERR_OR_ZERO(ctx->dss_mmbuf_clk);
> +	if (ret == -EPROBE_DEFER) {
> +		return ret;
> +	} else if (ret) {
> +		drm_err(ctx->dev, "failed to parse dss_mmbuf_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctx->dss_axi_clk = devm_clk_get(dev, "aclk_dss");
> +	ret = PTR_ERR_OR_ZERO(ctx->dss_axi_clk);
> +	if (ret == -EPROBE_DEFER) {
> +		return ret;
> +	} else if (ret) {
> +		drm_err(ctx->dev, "failed to parse dss_axi_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctx->dss_pclk_dss_clk = devm_clk_get(dev, "pclk_dss");
> +	ret = PTR_ERR_OR_ZERO(ctx->dss_pclk_dss_clk);
> +	if (ret == -EPROBE_DEFER) {
> +		return ret;
> +	} else if (ret) {
> +		drm_err(ctx->dev,
> +			"failed to parse dss_pclk_dss_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	ctx->dss_pri_clk = devm_clk_get(dev, "clk_edc0");
> +	ret = PTR_ERR_OR_ZERO(ctx->dss_pri_clk);
> +	if (ret == -EPROBE_DEFER) {
> +		return ret;
> +	} else if (ret) {
> +		drm_err(ctx->dev, "failed to parse dss_pri_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (ctx->g_dss_version_tag != FB_ACCEL_KIRIN970) {
> +		ret = clk_set_rate(ctx->dss_pri_clk, DEFAULT_DSS_CORE_CLK_07V_RATE);
> +		if (ret < 0) {
> +			drm_err(ctx->dev, "dss_pri_clk clk_set_rate(%lu) failed, error=%d!\n",
> +				DEFAULT_DSS_CORE_CLK_07V_RATE, ret);
> +			return -EINVAL;
> +		}
> +
> +		drm_dbg(ctx->dev, "dss_pri_clk:[%lu]->[%llu].\n",
> +			DEFAULT_DSS_CORE_CLK_07V_RATE, (uint64_t)clk_get_rate(ctx->dss_pri_clk));
> +	}
> +
> +	ctx->dss_pxl0_clk = devm_clk_get(dev, "clk_ldi0");
> +	ret = PTR_ERR_OR_ZERO(ctx->dss_pri_clk);
> +	if (ret == -EPROBE_DEFER) {
> +		return ret;
> +	} else if (ret) {
> +		drm_err(ctx->dev, "failed to parse dss_pxl0_clk: %d\n", ret);
> +		return ret;
> +	}
> +
> +	if (ctx->g_dss_version_tag != FB_ACCEL_KIRIN970) {
> +		ret = clk_set_rate(ctx->dss_pxl0_clk, DSS_MAX_PXL0_CLK_144M);
> +		if (ret < 0) {
> +			drm_err(ctx->dev,
> +				"dss_pxl0_clk clk_set_rate(%lu) failed, error=%d!\n",
> +				DSS_MAX_PXL0_CLK_144M, ret);
> +			return -EINVAL;
> +		}
> +
> +		drm_dbg(ctx->dev, "dss_pxl0_clk:[%lu]->[%llu].\n",
> +			DSS_MAX_PXL0_CLK_144M,
> +			(uint64_t)clk_get_rate(ctx->dss_pxl0_clk));
> +	}
> +
> +	/* enable IOMMU */
> +	dss_enable_iommu(pdev, ctx);
> +
> +	return 0;
> +}
> +
> +int dss_drm_init(struct drm_device *dev, u32 g_dss_version_tag)
> +{
> +	struct platform_device *pdev = to_platform_device(dev->dev);
> +	struct dss_data *dss;
> +	struct dss_hw_ctx *ctx;
> +	struct dss_crtc *acrtc;
> +	struct dss_plane *aplane;
> +	enum drm_plane_type type;
> +	int ret;
> +	int i;
> +
> +	dss = devm_kzalloc(dev->dev, sizeof(*dss), GFP_KERNEL);
> +	if (!dss) {
> +		drm_err(dev, "failed to alloc dss_data\n");
> +		return -ENOMEM;
> +	}
> +	platform_set_drvdata(pdev, dss);
> +
> +	ctx = &dss->ctx;
> +	ctx->dev = dev;
> +	ctx->g_dss_version_tag = g_dss_version_tag;
> +
> +	acrtc = &dss->acrtc;
> +	acrtc->ctx = ctx;
> +	acrtc->out_format = LCD_RGB888;
> +	acrtc->bgr_fmt = LCD_RGB;
> +
> +	ret = dss_dts_parse(pdev, ctx);
> +	if (ret)
> +		return ret;
> +
> +	ctx->vactive0_end_flag = 0;
> +	init_waitqueue_head(&ctx->vactive0_end_wq);
> +
> +	/*
> +	 * plane init
> +	 * TODO: Now only support primary plane, overlay planes
> +	 * need to do.
TODO rather unclear..

> +	 */
> +	for (i = 0; i < DSS_CH_NUM; i++) {
> +		aplane = &dss->aplane[i];
> +		aplane->ch = i;
> +		/* aplane->ctx = ctx; */
> +		aplane->acrtc = acrtc;
> +		type = i == PRIMARY_CH ? DRM_PLANE_TYPE_PRIMARY :
> +			DRM_PLANE_TYPE_OVERLAY;
> +
> +		ret = dss_plane_init(dev, aplane, type);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* crtc init */
> +	ret = dss_crtc_init(dev, &acrtc->base, &dss->aplane[PRIMARY_CH].base);
> +	if (ret)
> +		return ret;
> +
> +	/* vblank irq init */
> +	ret = devm_request_irq(dev->dev, ctx->irq, dss_irq_handler,
> +			       IRQF_SHARED, dev->driver->name, acrtc);
> +	if (ret) {
> +		drm_err(dev, "fail to  devm_request_irq, ret=%d!", ret);
> +		return ret;
> +	}
> +
> +	disable_irq(ctx->irq);
> +
> +	return 0;
> +}
> +
> +void kirin9xx_dss_drm_cleanup(struct drm_device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev->dev);
> +	struct dss_data *dss = platform_get_drvdata(pdev);
> +	struct drm_crtc *crtc = &dss->acrtc.base;
> +
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +int kirin9xx_dss_drm_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +	struct dss_data *dss = platform_get_drvdata(pdev);
> +	struct drm_crtc *crtc = &dss->acrtc.base;
> +
> +	dss_crtc_disable(crtc, NULL);
> +
> +	return 0;
> +}
> +
> +int kirin9xx_dss_drm_resume(struct platform_device *pdev)
> +{
> +	struct dss_data *dss = platform_get_drvdata(pdev);
> +	struct drm_crtc *crtc = &dss->acrtc.base;
> +
> +	dss_crtc_mode_set_nofb(crtc);
> +	dss_crtc_enable(crtc, NULL);
> +
> +	return 0;
> +}
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> new file mode 100644
> index 000000000000..c3e9fc95ad39
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_drm_overlay_utils.c
> @@ -0,0 +1,761 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2008-2011, Hisilicon Tech. Co., Ltd. All rights reserved.
> + * Copyright (c) 2008-2020, Huawei Technologies Co., Ltd
> + */
> +
> +#include <drm/drm_atomic.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_drv.h>
> +#include <drm/drm_fb_cma_helper.h>
> +#include <drm/drm_fourcc.h>
> +#include <drm/drm_gem_cma_helper.h>
> +#include <drm/drm_plane_helper.h>
> +
> +#include "kirin9xx_drm_dpe_utils.h"
> +#include "kirin9xx_drm_drv.h"
> +#include "kirin9xx_dpe.h"
Excessive includes?

> +
> +static const int mid_array[DSS_CHN_MAX_DEFINE] = {
> +	0xb, 0xa, 0x9, 0x8, 0x7, 0x6, 0x5, 0x4, 0x2, 0x1, 0x3, 0x0
> +};
> +
> +struct dpe_format {
> +	u32 pixel_format;
> +	enum dpe_fb_format dpe_format;
> +};
> +
> +/*
> + * FIXME: this driver was supposed to support 16 bpp with:
> + *
> + *       { DRM_FORMAT_RGB565, DPE_RGB_565 },
> + *       { DRM_FORMAT_BGR565, DPE_BGR_565 }
> + *
> + * However, those seem to be setting an YUYV mode.
> + * Using DRM_FORMAT_YUYV/DRM_FORMAT_UYVY doesn't currently work with X11,
> + * perhaps due to fb emulation. So, for now, let's just drop 16 bpp.
> + */
> +
> +static const struct dpe_format dpe_formats[] = {
> +	/* 24 bpp */
> +	{ DRM_FORMAT_XRGB8888, DPE_BGRX_8888 },
> +	{ DRM_FORMAT_XBGR8888, DPE_RGBX_8888 },
> +	/* 32 bpp */
> +	{ DRM_FORMAT_RGBA8888, DPE_RGBA_8888 },
> +	{ DRM_FORMAT_BGRA8888, DPE_BGRA_8888 },
> +	{ DRM_FORMAT_ARGB8888, DPE_RGBA_8888 },
> +	{ DRM_FORMAT_ABGR8888, DPE_BGRA_8888 },
> +};
> +
> +static const u32 dpe_channel_formats[] = {
> +	DRM_FORMAT_YUYV,
> +	DRM_FORMAT_UYVY,
> +	DRM_FORMAT_XRGB8888,
> +	DRM_FORMAT_XBGR8888,
> +	DRM_FORMAT_RGBA8888,
> +	DRM_FORMAT_BGRA8888,
> +	DRM_FORMAT_ARGB8888,
> +	DRM_FORMAT_ABGR8888,
> +};
> +
> +static u32 dpe_pixel_dma_format_map[] = {
> +	DMA_PIXEL_FORMAT_RGB_565,
> +	DMA_PIXEL_FORMAT_XRGB_4444,
> +	DMA_PIXEL_FORMAT_ARGB_4444,
> +	DMA_PIXEL_FORMAT_XRGB_5551,
> +	DMA_PIXEL_FORMAT_ARGB_5551,
> +	DMA_PIXEL_FORMAT_XRGB_8888,
> +	DMA_PIXEL_FORMAT_ARGB_8888,
> +	DMA_PIXEL_FORMAT_RGB_565,
> +	DMA_PIXEL_FORMAT_XRGB_4444,
> +	DMA_PIXEL_FORMAT_ARGB_4444,
> +	DMA_PIXEL_FORMAT_XRGB_5551,
> +	DMA_PIXEL_FORMAT_ARGB_5551,
> +	DMA_PIXEL_FORMAT_XRGB_8888,
> +	DMA_PIXEL_FORMAT_ARGB_8888,
> +	DMA_PIXEL_FORMAT_YUYV_422,
> +	DMA_PIXEL_FORMAT_YUV_422_SP_HP,
> +	DMA_PIXEL_FORMAT_YUV_422_SP_HP,
> +	DMA_PIXEL_FORMAT_YUV_420_SP_HP,
> +	DMA_PIXEL_FORMAT_YUV_420_SP_HP,
> +	DMA_PIXEL_FORMAT_YUV_422_P_HP,
> +	DMA_PIXEL_FORMAT_YUV_422_P_HP,
> +	DMA_PIXEL_FORMAT_YUV_420_P_HP,
> +	DMA_PIXEL_FORMAT_YUV_420_P_HP,
> +	DMA_PIXEL_FORMAT_YUYV_422,
> +	DMA_PIXEL_FORMAT_YUYV_422,
> +	DMA_PIXEL_FORMAT_YUYV_422,
> +	DMA_PIXEL_FORMAT_YUYV_422,
> +};
> +
> +static u32 dpe_pixel_dfc_format_map[] = {
> +	DFC_PIXEL_FORMAT_RGB_565,
> +	DFC_PIXEL_FORMAT_XBGR_4444,
> +	DFC_PIXEL_FORMAT_ABGR_4444,
> +	DFC_PIXEL_FORMAT_XBGR_5551,
> +	DFC_PIXEL_FORMAT_ABGR_5551,
> +	DFC_PIXEL_FORMAT_XBGR_8888,
> +	DFC_PIXEL_FORMAT_ABGR_8888,
> +	DFC_PIXEL_FORMAT_BGR_565,
> +	DFC_PIXEL_FORMAT_XRGB_4444,
> +	DFC_PIXEL_FORMAT_ARGB_4444,
> +	DFC_PIXEL_FORMAT_XRGB_5551,
> +	DFC_PIXEL_FORMAT_ARGB_5551,
> +	DFC_PIXEL_FORMAT_XRGB_8888,
> +	DFC_PIXEL_FORMAT_ARGB_8888,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_YVYU422,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_YVYU422,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_YVYU422,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_YVYU422,
> +	DFC_PIXEL_FORMAT_YUYV422,
> +	DFC_PIXEL_FORMAT_UYVY422,
> +	DFC_PIXEL_FORMAT_YVYU422,
> +	DFC_PIXEL_FORMAT_VYUY422,
> +};
> +
> +u32 dpe_get_format(struct dss_hw_ctx *ctx, u32 pixel_format)
> +{
> +	int i;
> +	const struct dpe_format *fmt = dpe_formats;
> +	u32 size = ARRAY_SIZE(dpe_formats);
> +
> +
> +	for (i = 0; i < size; i++) {
> +		if (fmt[i].pixel_format == pixel_format) {
> +			drm_info(ctx->dev, "requested fourcc %x, dpe format %d",
> +				 pixel_format, fmt[i].dpe_format);
> +			return fmt[i].dpe_format;
> +		}
> +	}
> +
> +	drm_err(ctx->dev, "Not found pixel format! fourcc = %x\n",
> +		pixel_format);
> +
> +	return HISI_FB_PIXEL_FORMAT_UNSUPPORT;
> +}
> +
> +u32 hisi_dss_get_channel_formats(struct drm_device *dev, u8 ch, const u32 **formats)
> +{
> +	*formats = dpe_channel_formats;
> +	return ARRAY_SIZE(dpe_channel_formats);
> +}
> +
> +static int hisi_dss_aif_ch_config(struct dss_hw_ctx *ctx, int chn_idx)
> +{
> +	void __iomem *aif0_ch_base;
> +	int mid = 0;
> +
> +	mid = mid_array[chn_idx];
> +	aif0_ch_base = ctx->base + ctx->g_dss_module_base[chn_idx][MODULE_AIF0_CHN];
> +
> +	set_reg(aif0_ch_base, 0x0, 1, 0);
> +	set_reg(aif0_ch_base, (uint32_t)mid, 4, 4);
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_smmu_config(struct dss_hw_ctx *ctx, int chn_idx, bool mmu_enable)
> +{
> +	void __iomem *smmu_base;
> +	u32 idx = 0, i = 0;
> +
> +	smmu_base = ctx->base + ctx->smmu_offset;
> +
> +	for (i = 0; i < ctx->g_dss_chn_sid_num[chn_idx]; i++) {
> +		idx = ctx->g_dss_smmu_smrx_idx[chn_idx] + i;
> +		if (!mmu_enable) {
> +			set_reg(smmu_base + SMMU_SMRx_NS + idx * 0x4, 1, 32, 0);
> +		} else {
> +			/* set_reg(smmu_base + SMMU_SMRx_NS + idx * 0x4, 0x70, 32, 0); */
> +			set_reg(smmu_base + SMMU_SMRx_NS + idx * 0x4, 0x1C, 32, 0);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_mif_config(struct dss_hw_ctx *ctx, int chn_idx, bool mmu_enable)
> +{
> +	void __iomem *mif_base;
> +	void __iomem *mif_ch_base;
> +
> +	mif_base = ctx->base + DSS_MIF_OFFSET;
> +	mif_ch_base = ctx->base +
> +		ctx->g_dss_module_base[chn_idx][MODULE_MIF_CHN];
> +
> +	if (!mmu_enable)
> +		set_reg(mif_ch_base + MIF_CTRL1, 0x1, 1, 5);
> +	else
> +		set_reg(mif_ch_base + MIF_CTRL1, 0x00080000, 32, 0);
> +
> +	return 0;
> +}
> +
> +int hisi_dss_mctl_mutex_lock(struct dss_hw_ctx *ctx)
static?

> +{
> +	void __iomem *mctl_base;
> +
> +	mctl_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_MCTL_BASE];
> +
> +	set_reg(mctl_base + MCTL_CTL_MUTEX, 0x1, 1, 0);
> +
> +	return 0;
> +}
> +
> +int hisi_dss_mctl_mutex_unlock(struct dss_hw_ctx *ctx)
static?

> +{
> +	void __iomem *mctl_base;
> +
> +
> +	mctl_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_MCTL_BASE];
> +
> +	set_reg(mctl_base + MCTL_CTL_MUTEX, 0x0, 1, 0);
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_mctl_ov_config(struct dss_hw_ctx *ctx, int chn_idx)
> +{
> +	void __iomem *mctl_base;
> +	u32 mctl_rch_offset = 0;
> +
> +	mctl_rch_offset = (uint32_t)(MCTL_CTL_MUTEX_RCH0 + chn_idx * 0x4);
> +
> +	mctl_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_MCTL_BASE];
> +
> +	set_reg(mctl_base + MCTL_CTL_EN, 0x1, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_TOP, 0x2, 32, 0); /* auto mode */
> +	set_reg(mctl_base + MCTL_CTL_DBG, 0xB13A00, 32, 0);
> +
> +	set_reg(mctl_base + mctl_rch_offset, 0x1, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_ITF, 0x1, 2, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_DBUF, 0x1, 2, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_OV, 1 << DSS_OVL0, 4, 0);
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_mctl_sys_config(struct dss_hw_ctx *ctx, int chn_idx)
> +{
> +	void __iomem *mctl_sys_base;
> +
> +	u32 layer_idx = 0;
> +	u32 mctl_rch_ov_oen_offset = 0;
> +	u32 mctl_rch_flush_en_offset = 0;
> +
> +
> +	mctl_sys_base = ctx->base + DSS_MCTRL_SYS_OFFSET;
> +	mctl_rch_ov_oen_offset = MCTL_RCH0_OV_OEN + chn_idx * 0x4;
> +	mctl_rch_flush_en_offset = MCTL_RCH0_FLUSH_EN + chn_idx * 0x4;
> +
> +	set_reg(mctl_sys_base + mctl_rch_ov_oen_offset,
> +		((1 << (layer_idx + 1)) | (0x100 << DSS_OVL0)), 32, 0);
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)
> +		set_reg(mctl_sys_base + MCTL_RCH_OV0_SEL, 0xe, 4, 0);
> +	else
> +		set_reg(mctl_sys_base + MCTL_RCH_OV0_SEL, 0x8, 4, 0);
> +
> +	set_reg(mctl_sys_base + MCTL_RCH_OV0_SEL, chn_idx, 4, (layer_idx + 1) * 4);
> +
> +	set_reg(mctl_sys_base + MCTL_OV0_FLUSH_EN, 0xd, 4, 0);
> +	set_reg(mctl_sys_base + mctl_rch_flush_en_offset, 0x1, 32, 0);
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_rdma_config(struct dss_hw_ctx *ctx,
> +				const struct dss_rect_ltrb *rect,
> +				u32 display_addr, u32 hal_format,
> +				u32 bpp, int chn_idx, bool afbcd,
> +				bool mmu_enable)
> +{
> +	void __iomem *rdma_base;
> +
> +	u32 aligned_pixel = 0;
> +	u32 rdma_oft_x0 = 0;
> +	u32 rdma_oft_y0 = 0;
> +	u32 rdma_oft_x1 = 0;
> +	u32 rdma_oft_y1 = 0;
> +	u32 rdma_stride = 0;
> +	u32 rdma_format = 0;
> +	u32 stretch_size_vrt = 0;
> +
> +	u32 stride_align = 0;
> +	u32 mm_base_0 = 0;
> +	u32 mm_base_1 = 0;
> +
> +	u32 afbcd_header_addr = 0;
> +	u32 afbcd_header_stride = 0;
> +	u32 afbcd_payload_addr = 0;
> +	u32 afbcd_payload_stride = 0;
> +
> +	rdma_base = ctx->base +
> +		ctx->g_dss_module_base[chn_idx][MODULE_DMA];
> +
> +	aligned_pixel = DMA_ALIGN_BYTES / bpp;
> +	rdma_oft_x0 = rect->left / aligned_pixel;
> +	rdma_oft_y0 = rect->top;
> +	rdma_oft_x1 = rect->right / aligned_pixel;
> +	rdma_oft_y1 = rect->bottom;
> +
> +	rdma_format = dpe_pixel_dma_format_map[hal_format];
> +
> +	if (afbcd) {
> +		mm_base_0 = 0;
> +		mm_base_1 = mm_base_0 + rect->right * bpp * MMBUF_LINE_NUM;
> +		mm_base_0 = ALIGN(mm_base_0, MMBUF_ADDR_ALIGN);
> +		mm_base_1 = ALIGN(mm_base_1, MMBUF_ADDR_ALIGN);
> +
> +		if ((((rect->right - rect->left) + 1) & (ctx->afbc_header_addr_align - 1)) ||
> +		    (((rect->bottom - rect->top) + 1) & (AFBC_BLOCK_ALIGN - 1))) {
> +			drm_err(ctx->dev,
> +				"img width(%d) is not %d bytes aligned, or img height (%d) is not %d bytes aligned!\n",
> +				((rect->right - rect->left) + 1),
> +				ctx->afbc_header_addr_align,
> +				((rect->bottom - rect->top) + 1),
> +				AFBC_BLOCK_ALIGN);
> +		}
> +
> +		if ((mm_base_0 & (MMBUF_ADDR_ALIGN - 1)) || (mm_base_1 & (MMBUF_ADDR_ALIGN - 1))) {
> +			drm_err(ctx->dev,
> +				"mm_base_0(0x%x) is not %d bytes aligned, or mm_base_1(0x%x) is not %d bytes aligned!\n",
> +				mm_base_0, MMBUF_ADDR_ALIGN,
> +				mm_base_1, MMBUF_ADDR_ALIGN);
> +		}
> +		/* header */
> +		afbcd_header_stride = (((rect->right - rect->left) + 1) / AFBC_BLOCK_ALIGN) * AFBC_HEADER_STRIDE_BLOCK;
> +		afbcd_header_addr = (uint32_t)(unsigned long)display_addr;
> +
> +		/* payload */
> +		if (bpp == 4)
> +			stride_align = AFBC_PAYLOAD_STRIDE_ALIGN_32;
> +		else if (bpp == 2)
> +			stride_align = AFBC_PAYLOAD_STRIDE_ALIGN_16;
> +		else
> +			drm_err(ctx->dev,"bpp(%d) not supported!\n", bpp);
> +
> +		afbcd_payload_stride = (((rect->right - rect->left) + 1) / AFBC_BLOCK_ALIGN) * stride_align;
> +
> +		afbcd_payload_addr = afbcd_header_addr + ALIGN(16 * (((rect->right - rect->left) + 1) / 16) *
> +				(((rect->bottom - rect->top) + 1) / 16), 1024);
> +		afbcd_payload_addr = afbcd_payload_addr +
> +			(rect->top / AFBC_BLOCK_ALIGN) * afbcd_payload_stride +
> +			(rect->left / AFBC_BLOCK_ALIGN) * stride_align;
> +
> +		set_reg(rdma_base + CH_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(rdma_base + CH_REG_DEFAULT, 0x0, 32, 0);
> +		set_reg(rdma_base + DMA_OFT_X0, rdma_oft_x0, 12, 0);
> +		set_reg(rdma_base + DMA_OFT_Y0, rdma_oft_y0, 16, 0);
> +		set_reg(rdma_base + DMA_OFT_X1, rdma_oft_x1, 12, 0);
> +		set_reg(rdma_base + DMA_OFT_Y1, rdma_oft_y1, 16, 0);
> +		set_reg(rdma_base + DMA_STRETCH_SIZE_VRT, (rect->bottom - rect->top), 13, 0);
> +		set_reg(rdma_base + DMA_CTRL, rdma_format, 5, 3);
> +		set_reg(rdma_base + DMA_CTRL, (mmu_enable ? 0x1 : 0x0), 1, 8);
> +
> +		set_reg(rdma_base + AFBCD_HREG_PIC_WIDTH, (rect->right - rect->left), 16, 0);
> +		set_reg(rdma_base + AFBCD_HREG_PIC_HEIGHT, (rect->bottom - rect->top), 16, 0);
> +		set_reg(rdma_base + AFBCD_CTL, AFBC_HALF_BLOCK_UPPER_LOWER_ALL, 2, 6);
> +		set_reg(rdma_base + AFBCD_HREG_HDR_PTR_LO, afbcd_header_addr, 32, 0);
> +		set_reg(rdma_base + AFBCD_INPUT_HEADER_STRIDE, afbcd_header_stride, 14, 0);
> +		set_reg(rdma_base + AFBCD_PAYLOAD_STRIDE, afbcd_payload_stride, 20, 0);
> +		set_reg(rdma_base + AFBCD_MM_BASE_0, mm_base_0, 32, 0);
> +		set_reg(rdma_base + AFBCD_HREG_FORMAT, 0x1, 1, 21);
> +		set_reg(rdma_base + AFBCD_SCRAMBLE_MODE, 0x0, 32, 0);
> +		set_reg(rdma_base + AFBCD_AFBCD_PAYLOAD_POINTER, afbcd_payload_addr, 32, 0);
> +		set_reg(rdma_base + AFBCD_HEIGHT_BF_STR, (rect->bottom - rect->top), 16, 0);
> +
> +		set_reg(rdma_base + CH_CTL, 0xf005, 32, 0);
> +	} else {
> +		stretch_size_vrt = rdma_oft_y1 - rdma_oft_y0;
> +		rdma_stride = ((rect->right - rect->left) + 1) * bpp / DMA_ALIGN_BYTES;
> +
> +		set_reg(rdma_base + CH_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(rdma_base + CH_REG_DEFAULT, 0x0, 32, 0);
> +
> +		set_reg(rdma_base + DMA_OFT_X0, rdma_oft_x0, 12, 0);
> +		set_reg(rdma_base + DMA_OFT_Y0, rdma_oft_y0, 16, 0);
> +		set_reg(rdma_base + DMA_OFT_X1, rdma_oft_x1, 12, 0);
> +		set_reg(rdma_base + DMA_OFT_Y1, rdma_oft_y1, 16, 0);
> +		set_reg(rdma_base + DMA_CTRL, rdma_format, 5, 3);
> +		set_reg(rdma_base + DMA_CTRL, (mmu_enable ? 0x1 : 0x0), 1, 8);
> +		set_reg(rdma_base + DMA_CTRL, 0x130, 32, 0);
> +		set_reg(rdma_base + DMA_STRETCH_SIZE_VRT, stretch_size_vrt, 32, 0);
> +		set_reg(rdma_base + DMA_DATA_ADDR0, display_addr, 32, 0);
> +		set_reg(rdma_base + DMA_STRIDE0, rdma_stride, 13, 0);
> +
> +		set_reg(rdma_base + CH_CTL, 0x1, 1, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_rdfc_config(struct dss_hw_ctx *ctx,
> +				const struct dss_rect_ltrb *rect,
> +				u32 hal_format, u32 bpp, int chn_idx)
> +{
> +	void __iomem *rdfc_base;
> +
> +	u32 dfc_pix_in_num = 0;
> +	u32 size_hrz = 0;
> +	u32 size_vrt = 0;
> +	u32 dfc_fmt = 0;
> +
> +	rdfc_base = ctx->base +
> +		ctx->g_dss_module_base[chn_idx][MODULE_DFC];
> +
> +	dfc_pix_in_num = (bpp <= 2) ? 0x1 : 0x0;
> +	size_hrz = rect->right - rect->left;
> +	size_vrt = rect->bottom - rect->top;
> +
> +	dfc_fmt = dpe_pixel_dfc_format_map[hal_format];
> +
> +	set_reg(rdfc_base + DFC_DISP_SIZE, (size_vrt | (size_hrz << 16)),
> +		29, 0);
> +	set_reg(rdfc_base + DFC_PIX_IN_NUM, dfc_pix_in_num, 1, 0);
> +	set_reg(rdfc_base + DFC_DISP_FMT, dfc_fmt, 5, 1);
> +	set_reg(rdfc_base + DFC_CTL_CLIP_EN, 0x1, 1, 0);
> +	set_reg(rdfc_base + DFC_ICG_MODULE, 0x1, 1, 0);
> +
> +	return 0;
> +}
> +
> +int hisi_dss_ovl_base_config(struct dss_hw_ctx *ctx, u32 xres, u32 yres)
> +{
> +	void __iomem *mctl_sys_base;
> +	void __iomem *mctl_base;
> +	void __iomem *ovl0_base;
> +
> +	mctl_sys_base = ctx->base + DSS_MCTRL_SYS_OFFSET;
> +	mctl_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_MCTL_BASE];
> +	ovl0_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_OVL_BASE];
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		set_reg(ovl0_base + OV8_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(ovl0_base + OV8_REG_DEFAULT, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_SIZE, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +
> +		set_reg(ovl0_base + OV_BG_COLOR_RGB, 0x00000000, 32, 0);
> +		set_reg(ovl0_base + OV_BG_COLOR_A, 0x00000000, 32, 0);
> +		set_reg(ovl0_base + OV_DST_STARTPOS, 0x0, 32, 0);
> +		set_reg(ovl0_base + OV_DST_ENDPOS, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OV_GCFG, 0x10001, 32, 0);
> +		set_reg(mctl_sys_base + MCTL_RCH_OV0_SEL, 0xE, 4, 0);
> +	} else {
> +		set_reg(ovl0_base + OVL6_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(ovl0_base + OVL6_REG_DEFAULT, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_SIZE, (xres - 1) | ((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_BG_COLOR, 0xFF000000, 32, 0);
> +		set_reg(ovl0_base + OVL_DST_STARTPOS, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_DST_ENDPOS, (xres - 1) | ((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_GCFG, 0x10001, 32, 0);
> +		set_reg(mctl_sys_base + MCTL_RCH_OV0_SEL, 0x8, 4, 0);
> +	}
> +
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_ITF, 0x1, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_DBUF, 0x1, 2, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_OV, 1 << DSS_OVL0, 4, 0);
> +	set_reg(mctl_sys_base + MCTL_OV0_FLUSH_EN, 0xd, 4, 0);
> +
> +	return 0;
> +}
> +
> +static int hisi_dss_ovl_config(struct dss_hw_ctx *ctx,
> +			       const struct dss_rect_ltrb *rect, u32 xres, u32 yres)
> +{
> +	void __iomem *ovl0_base;
> +
> +	ovl0_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_OVL0][MODULE_OVL_BASE];
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		set_reg(ovl0_base + OV8_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(ovl0_base + OV8_REG_DEFAULT, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_SIZE, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OV_BG_COLOR_RGB, 0x3FF00000, 32, 0);
> +		set_reg(ovl0_base + OV_BG_COLOR_A, 0x3ff, 32, 0);
> +
> +		set_reg(ovl0_base + OV_DST_STARTPOS, 0x0, 32, 0);
> +		set_reg(ovl0_base + OV_DST_ENDPOS, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OV_GCFG, 0x10001, 32, 0);
> +		set_reg(ovl0_base + OV_LAYER0_POS, (rect->left) |
> +			((rect->top) << 16), 32, 0);
> +		set_reg(ovl0_base + OV_LAYER0_SIZE, (rect->right) |
> +			((rect->bottom) << 16), 32, 0);
> +		set_reg(ovl0_base + OV_LAYER0_ALPHA_MODE, 0x1004000, 32, 0); /* NEED CHECK? */
> +		set_reg(ovl0_base + OV_LAYER0_ALPHA_A, 0x3ff03ff, 32, 0);
> +		set_reg(ovl0_base + OV_LAYER0_CFG, 0x1, 1, 0);
> +	} else {
> +		set_reg(ovl0_base + OVL6_REG_DEFAULT, 0x1, 32, 0);
> +		set_reg(ovl0_base + OVL6_REG_DEFAULT, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_SIZE, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_BG_COLOR, 0xFFFF0000, 32, 0);
> +		set_reg(ovl0_base + OVL_DST_STARTPOS, 0x0, 32, 0);
> +		set_reg(ovl0_base + OVL_DST_ENDPOS, (xres - 1) |
> +			((yres - 1) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_GCFG, 0x10001, 32, 0);
> +		set_reg(ovl0_base + OVL_LAYER0_POS, (rect->left) |
> +			((rect->top) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_LAYER0_SIZE, (rect->right) |
> +			((rect->bottom) << 16), 32, 0);
> +		set_reg(ovl0_base + OVL_LAYER0_ALPHA, 0x00ff40ff, 32, 0);
> +		set_reg(ovl0_base + OVL_LAYER0_CFG, 0x1, 1, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static void hisi_dss_qos_on(struct dss_hw_ctx *ctx)
> +{
> +	char __iomem *noc_dss_base;
> +
> +	noc_dss_base = ctx->noc_dss_base;
> +
> +	writel(0x2, noc_dss_base + 0xc);
> +	writel(0x2, noc_dss_base + 0x8c);
> +	writel(0x2, noc_dss_base + 0x10c);
> +	writel(0x2, noc_dss_base + 0x18c);
> +}
> +
> +static void hisi_dss_mif_on(struct dss_hw_ctx *ctx)
> +{
> +	char __iomem *dss_base;
> +	char __iomem *mif_base;
> +
> +	dss_base = ctx->base;
> +	mif_base = ctx->base + DSS_MIF_OFFSET;
> +
> +	set_reg(mif_base + MIF_ENABLE, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH0_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH1_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH2_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH3_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH4_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH5_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH6_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH7_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH8_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH9_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +
> +	set_reg(dss_base + MIF_CH10_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +	set_reg(dss_base + MIF_CH11_OFFSET + MIF_CTRL0, 0x1, 1, 0);
> +}
> +
> +void hisi_dss_smmu_on(struct dss_hw_ctx *ctx)
static?

> +{
> +#if 0
> +/*
> + * FIXME:
> + *
> + * Right now, the IOMMU support is actually disabled. See the caller of
> + * hisi_dss_smmu_config(). Yet, if we end enabling it, this should be
> + * ported to use io-pgtable directly.
> + */
> +	void __iomem *smmu_base;
> +	struct iommu_domain_data *domain_data = NULL;
> +	u32 phy_pgd_base = 0;
> +	u64 fama_phy_pgd_base;
> +
> +	smmu_base = ctx->base + ctx->smmu_offset;
> +
> +	set_reg(smmu_base + SMMU_SCR, 0x0, 1, 0);  /* global bypass cancel */
> +	set_reg(smmu_base + SMMU_SCR, 0x1, 8, 20); /* ptw_mid */
> +	set_reg(smmu_base + SMMU_SCR, 0xf, 4, 16); /* pwt_pf */
> +	set_reg(smmu_base + SMMU_SCR, 0x7, 3, 3);  /* interrupt cachel1 cach3l2 en */
> +	set_reg(smmu_base + SMMU_LP_CTRL, 0x1, 1, 0);  /* auto_clk_gt_en */
> +
> +	/* Long Descriptor */
> +	set_reg(smmu_base + SMMU_CB_TTBCR, 0x1, 1, 0);
> +
> +	set_reg(smmu_base + SMMU_ERR_RDADDR, 0x7FF00000, 32, 0);
> +	set_reg(smmu_base + SMMU_ERR_WRADDR, 0x7FFF0000, 32, 0);
> +
> +	/* disable cmdlist, dbg, reload */
> +	set_reg(smmu_base + SMMU_RLD_EN0_NS, DSS_SMMU_RLD_EN0_DEFAULT_VAL, 32, 0);
> +	set_reg(smmu_base + SMMU_RLD_EN1_NS, DSS_SMMU_RLD_EN1_DEFAULT_VAL, 32, 0);
> +
> +	/* cmdlist stream bypass */
> +	set_reg(smmu_base + SMMU_SMRx_NS + 36 * 0x4, 0x1, 32, 0); /* debug stream id */
> +	set_reg(smmu_base + SMMU_SMRx_NS + 37 * 0x4, 0x1, 32, 0); /* cmd unsec stream id */
> +	set_reg(smmu_base + SMMU_SMRx_NS + 38 * 0x4, 0x1, 32, 0); /* cmd sec stream id */
> +
> +	/* TTBR0 */
> +	domain_data = (struct iommu_domain_data *)(ctx->mmu_domain->priv);
> +	fama_phy_pgd_base = domain_data->phy_pgd_base;
> +	phy_pgd_base = (uint32_t)(domain_data->phy_pgd_base);
> +	drm_dbg(ctx->dev,
> +		"fama_phy_pgd_base = %llu, phy_pgd_base =0x%x\n",
> +		fama_phy_pgd_base, phy_pgd_base);
> +	set_reg(smmu_base + SMMU_CB_TTBR0, phy_pgd_base, 32, 0);
> +#endif
> +}
> +
> +void hisifb_dss_on(struct dss_hw_ctx *ctx)
> +{
> +	/* dss qos on */
> +	hisi_dss_qos_on(ctx);
> +	/* mif on */
> +	hisi_dss_mif_on(ctx);
> +	/* smmu on */
> +	hisi_dss_smmu_on(ctx);
> +}
> +
> +void hisi_dss_mctl_on(struct dss_hw_ctx *ctx)
> +{
> +	char __iomem *mctl_base = NULL;
> +	char __iomem *mctl_sys_base = NULL;
> +
> +	mctl_base = ctx->base +
> +		ctx->g_dss_module_ovl_base[DSS_MCTL0][MODULE_MCTL_BASE];
> +	mctl_sys_base = ctx->base + DSS_MCTRL_SYS_OFFSET;
> +
> +	set_reg(mctl_base + MCTL_CTL_EN, 0x1, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_MUTEX_ITF, 0x1, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_DBG, 0xB13A00, 32, 0);
> +	set_reg(mctl_base + MCTL_CTL_TOP, 0x2, 32, 0);
> +}
> +
> +void hisi_dss_unflow_handler(struct dss_hw_ctx *ctx, bool unmask)
static?
> +{
> +	void __iomem *dss_base;
> +	u32 tmp = 0;
> +
> +	dss_base = ctx->base;
> +
> +	tmp = readl(dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INT_MSK);
> +	if (unmask)
> +		tmp &= ~BIT_LDI_UNFLOW;
> +	else
> +		tmp |= BIT_LDI_UNFLOW;
> +
> +	writel(tmp, dss_base + DSS_LDI0_OFFSET + LDI_CPU_ITF_INT_MSK);
> +}
> +
> +void hisifb_mctl_sw_clr(struct dss_crtc *acrtc)
> +{
> +	char __iomem *mctl_base = NULL;
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +	int mctl_status;
> +	int delay_count = 0;
> +	bool is_timeout;
> +
> +	mctl_base = ctx->base +
> +		    ctx->g_dss_module_ovl_base[DSS_MCTL0][MODULE_MCTL_BASE];
> +
> +	if (mctl_base)
> +		set_reg(mctl_base + MCTL_CTL_CLEAR, 0x1, 1, 0);
> +
> +	while (1) {
> +		mctl_status = readl(mctl_base + MCTL_CTL_STATUS);
> +		if (((mctl_status & 0x10) == 0) || (delay_count > 500)) {
> +			is_timeout = (delay_count > 100) ? true : false;
> +			delay_count = 0;
> +			break;
> +		}
> +
> +		udelay(1);
> +		++delay_count;
> +	}
> +
> +	if (is_timeout)
> +		drm_err(ctx->dev, "mctl_status =0x%x !\n", mctl_status);
> +
> +	enable_ldi(acrtc);
> +	DRM_INFO("-.\n");
> +}
> +
> +void hisi_fb_pan_display(struct drm_plane *plane)
> +{
> +	struct drm_plane_state *state = plane->state;
> +	struct drm_framebuffer *fb = state->fb;
> +	struct drm_display_mode *mode;
> +	struct drm_display_mode *adj_mode;
> +
> +	struct dss_plane *aplane = to_dss_plane(plane);
> +	struct dss_crtc *acrtc = aplane->acrtc;
> +	struct dss_hw_ctx *ctx = acrtc->ctx;
> +
> +	struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(state->fb, 0);
> +
> +	bool afbcd = false;
> +	bool mmu_enable = false;
> +	struct dss_rect_ltrb rect;
> +	u32 bpp;
> +	u32 stride;
> +	u32 display_addr = 0;
> +	u32 hal_fmt;
> +	int chn_idx = DSS_RCHN_D2;
> +
> +	int crtc_x = state->crtc_x;
> +	int crtc_y = state->crtc_y;
> +	unsigned int crtc_w = state->crtc_w;
> +	unsigned int crtc_h = state->crtc_h;
> +	u32 src_x = state->src_x >> 16;
> +	u32 src_y = state->src_y >> 16;
> +	u32 src_w = state->src_w >> 16;
> +	u32 src_h = state->src_h >> 16;
> +
> +	u32 hfp, hbp, hsw, vfp, vbp, vsw;
> +
> +	mode = &acrtc->base.state->mode;
> +	adj_mode = &acrtc->base.state->adjusted_mode;
> +
> +	bpp = fb->format->cpp[0];
> +	stride = fb->pitches[0];
> +
> +	display_addr = (u32)obj->paddr + src_y * stride;
> +
> +	rect.left = 0;
> +	rect.right = src_w - 1;
> +	rect.top = 0;
> +	rect.bottom = src_h - 1;
> +	hal_fmt = dpe_get_format(ctx, fb->format->format);
> +
> +	drm_dbg(ctx->dev,
> +		"channel%d: src:(%d,%d, %dx%d) crtc:(%d,%d, %dx%d), rect(%d,%d,%d,%d),fb:%dx%d, pixel_format=%d, stride=%d, paddr=0x%x, bpp=%d.\n",
> +			 chn_idx, src_x, src_y, src_w, src_h,
> +			 crtc_x, crtc_y, crtc_w, crtc_h,
> +			 rect.left, rect.top, rect.right, rect.bottom,
> +			 fb->width, fb->height, hal_fmt,
> +			 stride, display_addr, bpp);
> +
> +	hfp = mode->hsync_start - mode->hdisplay;
> +	hbp = mode->htotal - mode->hsync_end;
> +	hsw = mode->hsync_end - mode->hsync_start;
> +	vfp = mode->vsync_start - mode->vdisplay;
> +	vbp = mode->vtotal - mode->vsync_end;
> +	vsw = mode->vsync_end - mode->vsync_start;
The above varaibles are assinged but not used - delete them?

> +
> +	hisi_dss_mctl_mutex_lock(ctx);
> +	hisi_dss_aif_ch_config(ctx, chn_idx);
> +	hisi_dss_mif_config(ctx, chn_idx, mmu_enable);
> +	hisi_dss_smmu_config(ctx, chn_idx, mmu_enable);
> +
> +	hisi_dss_rdma_config(ctx, &rect, display_addr, hal_fmt, bpp, chn_idx, afbcd, mmu_enable);
> +	hisi_dss_rdfc_config(ctx, &rect, hal_fmt, bpp, chn_idx);
> +	hisi_dss_ovl_config(ctx, &rect, mode->hdisplay, mode->vdisplay);
> +
> +	hisi_dss_mctl_ov_config(ctx, chn_idx);
> +	hisi_dss_mctl_sys_config(ctx, chn_idx);
> +	hisi_dss_mctl_mutex_unlock(ctx);
> +	hisi_dss_unflow_handler(ctx, true);
> +
> +	enable_ldi(acrtc);
> +}
Mauro Carvalho Chehab Aug. 25, 2020, 11:30 a.m. UTC | #33
Em Tue, 25 Aug 2020 05:29:29 +1000
Dave Airlie <airlied@gmail.com> escreveu:

> On Thu, 20 Aug 2020 at 20:02, Laurent Pinchart
> <laurent.pinchart@ideasonboard.com> wrote:
> >
> > Hi Mauro,
> >
> > On Thu, Aug 20, 2020 at 09:03:26AM +0200, Mauro Carvalho Chehab wrote:  
> > > Em Wed, 19 Aug 2020 12:52:06 -0700 John Stultz escreveu:  
> > > > On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart wrote:  
> > > > > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:  
> > > > > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:  
> > > > > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > > > > should also support Hikey 960) from the official 96boards tree:
> > > > > > >
> > > > > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > > > > >
> > > > > > > Based on his history, this driver seems to be originally written
> > > > > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > > > > implementation for FB dev API.
> > > > > > >
> > > > > > > As I need to preserve the original history (with has patches from
> > > > > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > > > > patch applied there. The remaining patches are incremental,
> > > > > > > and port this driver to work with upstream Kernel.
> > > > > > >  
> > > > ...  
> > > > > > > - Due to legal reasons, I need to preserve the authorship of
> > > > > > >   each one responsbile for each patch. So, I need to start from
> > > > > > >   the original patch from Kernel 4.4;  
> > > > ...  
> > > > > > I do acknowledge you need to preserve history and all -
> > > > > > but this patchset is not easy to review.  
> > > > >
> > > > > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > > > > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > > > > that contains the history is useful if anyone is interested, but I don't
> > > > > think it's required in mainline.  
> > > >
> > > > Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> > > > have on this but preserving the "absolute" history here is actively
> > > > detrimental for review and understanding of the patch set.
> > > >
> > > > Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> > > > lines should be sufficient to provide both atribution credit and DCO
> > > > history.  
> > >
> > > I'm not convinced that, from legal standpoint, folding things would
> > > be enough. See, there are at least 3 legal systems involved here
> > > among the different patch authors:
> > >
> > >       - civil law;
> > >       - common law;
> > >       - customary law + common law.
> > >
> > > Merging stuff altogether from different law systems can be problematic,
> > > and trying to discuss this with experienced IP property lawyers will
> > > for sure take a lot of time and efforts. I also bet that different
> > > lawyers will have different opinions, because laws are subject to
> > > interpretation. With that matter I'm not aware of any court rules
> > > with regards to folded patches. So, it sounds to me that folding
> > > patches is something that has yet to be proofed in courts around
> > > the globe.
> > >
> > > At least for US legal system, it sounds that the Country of
> > > origin of a patch is relevant, as they have a concept of
> > > "national technology" that can be subject to export regulations.
> > >
> > > From my side, I really prefer to play safe and stay out of any such
> > > legal discussions.  
> >
> > Let's be serious for a moment. If you think there are legal issues in
> > taking GPL-v2.0-only patches and squashing them while retaining
> > authorship information through tags, the Linux kernel if *full* of that.
> > You also routinely modify patches that you commit to the media subsystem
> > to fix "small issues".
> >
> > The country of origin argument makes no sense either, the kernel code
> > base if full of code coming from pretty much all country on the planet.
> >
> > Keeping the patches separate make this hard to review. Please squash
> > them.  
> 
> I'm inclined to agree with Laurent here.
> 
> Patches submitted as GPL-v2 with DCO lines and author names/companies
> should be fine to be squashed and rearranged,
> as long as the DCO and Authorship is kept somewhere in the new patch
> that is applied.
> 
> Review is more important here.

Sorry, but I can't agree that review is more important than to be able
to properly indicate copyrights in a valid way at the legal systems that
it would apply ;-)

In any case, there's an easy way to make the code easy to review:
I can write the patches against staging (where it is OK to submit
preserving the history) and then add a final patch moving it out
of staging.

You can then just review the last patch, as it will contain the
entire code on it.

Another alternative, as I'm already doing with Sam, is for me to
submit the folded code as a reply to 00/xx. You can then just 
review the final code, without concerning about how the code reached
there.

From review point of the view, this will be the same as reviewing
a folded patch, but, from legal standpoint, the entire copyright
chain will be preserved.

Thanks,
Mauro
Laurent Pinchart Aug. 25, 2020, 11:38 a.m. UTC | #34
Hi Mauro,

On Tue, Aug 25, 2020 at 01:30:25PM +0200, Mauro Carvalho Chehab wrote:
> Em Tue, 25 Aug 2020 05:29:29 +1000
> Dave Airlie <airlied@gmail.com> escreveu:
> 
> > On Thu, 20 Aug 2020 at 20:02, Laurent Pinchart
> > <laurent.pinchart@ideasonboard.com> wrote:
> > >
> > > Hi Mauro,
> > >
> > > On Thu, Aug 20, 2020 at 09:03:26AM +0200, Mauro Carvalho Chehab wrote:  
> > > > Em Wed, 19 Aug 2020 12:52:06 -0700 John Stultz escreveu:  
> > > > > On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart wrote:  
> > > > > > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:  
> > > > > > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:  
> > > > > > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > > > > > should also support Hikey 960) from the official 96boards tree:
> > > > > > > >
> > > > > > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > > > > > >
> > > > > > > > Based on his history, this driver seems to be originally written
> > > > > > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > > > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > > > > > implementation for FB dev API.
> > > > > > > >
> > > > > > > > As I need to preserve the original history (with has patches from
> > > > > > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > > > > > patch applied there. The remaining patches are incremental,
> > > > > > > > and port this driver to work with upstream Kernel.
> > > > > > > >  
> > > > > ...  
> > > > > > > > - Due to legal reasons, I need to preserve the authorship of
> > > > > > > >   each one responsbile for each patch. So, I need to start from
> > > > > > > >   the original patch from Kernel 4.4;  
> > > > > ...  
> > > > > > > I do acknowledge you need to preserve history and all -
> > > > > > > but this patchset is not easy to review.  
> > > > > >
> > > > > > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > > > > > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > > > > > that contains the history is useful if anyone is interested, but I don't
> > > > > > think it's required in mainline.  
> > > > >
> > > > > Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> > > > > have on this but preserving the "absolute" history here is actively
> > > > > detrimental for review and understanding of the patch set.
> > > > >
> > > > > Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> > > > > lines should be sufficient to provide both atribution credit and DCO
> > > > > history.  
> > > >
> > > > I'm not convinced that, from legal standpoint, folding things would
> > > > be enough. See, there are at least 3 legal systems involved here
> > > > among the different patch authors:
> > > >
> > > >       - civil law;
> > > >       - common law;
> > > >       - customary law + common law.
> > > >
> > > > Merging stuff altogether from different law systems can be problematic,
> > > > and trying to discuss this with experienced IP property lawyers will
> > > > for sure take a lot of time and efforts. I also bet that different
> > > > lawyers will have different opinions, because laws are subject to
> > > > interpretation. With that matter I'm not aware of any court rules
> > > > with regards to folded patches. So, it sounds to me that folding
> > > > patches is something that has yet to be proofed in courts around
> > > > the globe.
> > > >
> > > > At least for US legal system, it sounds that the Country of
> > > > origin of a patch is relevant, as they have a concept of
> > > > "national technology" that can be subject to export regulations.
> > > >
> > > > From my side, I really prefer to play safe and stay out of any such
> > > > legal discussions.  
> > >
> > > Let's be serious for a moment. If you think there are legal issues in
> > > taking GPL-v2.0-only patches and squashing them while retaining
> > > authorship information through tags, the Linux kernel if *full* of that.
> > > You also routinely modify patches that you commit to the media subsystem
> > > to fix "small issues".
> > >
> > > The country of origin argument makes no sense either, the kernel code
> > > base if full of code coming from pretty much all country on the planet.
> > >
> > > Keeping the patches separate make this hard to review. Please squash
> > > them.  
> > 
> > I'm inclined to agree with Laurent here.
> > 
> > Patches submitted as GPL-v2 with DCO lines and author names/companies
> > should be fine to be squashed and rearranged,
> > as long as the DCO and Authorship is kept somewhere in the new patch
> > that is applied.
> > 
> > Review is more important here.
> 
> Sorry, but I can't agree that review is more important than to be able
> to properly indicate copyrights in a valid way at the legal systems that
> it would apply ;-)
> 
> In any case, there's an easy way to make the code easy to review:
> I can write the patches against staging (where it is OK to submit
> preserving the history) and then add a final patch moving it out
> of staging.
> 
> You can then just review the last patch, as it will contain the
> entire code on it.
> 
> Another alternative, as I'm already doing with Sam, is for me to
> submit the folded code as a reply to 00/xx. You can then just 
> review the final code, without concerning about how the code reached
> there.
> 
> From review point of the view, this will be the same as reviewing
> a folded patch, but, from legal standpoint, the entire copyright
> chain will be preserved.

Let's stop with the legal FUD please. Squashing patches is done
routinely in the kernel. If you have evidence this causes legal issues,
please bring it up with the TAB or the LF to make this practice stop.
Otherwise, please squash this series.
Daniel Stone Aug. 25, 2020, 12:31 p.m. UTC | #35
Hi Mauro,

On Tue, 25 Aug 2020 at 12:30, Mauro Carvalho Chehab
<mchehab+huawei@kernel.org> wrote:
> Sorry, but I can't agree that review is more important than to be able
> to properly indicate copyrights in a valid way at the legal systems that
> it would apply ;-)

The way to properly indicate copyright coverage is to insert a
copyright statement in the file. This has been the accepted way of
communicating copyright notices since approximately the dawn of time.
The value of the 'author' field within a chain of git commits does not
have privileged legal value.

If what you were saying is true, it would be impossible for any
project to copy code from any other project, unless they did git
filter-branch and made sure to follow renames too. As others have
noted, it would also be impossible for any patches to be developed
collaboratively by different copyright holders, or for maintainers to
apply changes.

This is accepted community practice and has passed signoffs from a
million different lawyers and copyright holders. If you wish to break
with this and do something different, the onus is on you to provide
the community with _specific_ legal advice; if this is accepted, the
development model would have to drastically change in the presence of
single pieces of code developed by multiple distinct copyright
holders.

Cheers,
Daniel
Sam Ravnborg Aug. 25, 2020, 6:11 p.m. UTC | #36
Hi Mauro

> Before posting the big patch series again, let me send the new
> version folded into a single patch.
> 
> If you'd like to see the entire thing, I'm placing it here:
> 
> 	https://gitlab.freedesktop.org/mchehab_kernel/hikey-970/-/commits/hikey970_v2/

Review 3/3

For next submission then to ease review it would be nice that the patch
is spilt up a little. Maybe something like:
- HW specific stuff in two patches (header fiels with register + setup
  code)
- Display driver files
- DSI driver files
- build stuff (Makefile, Kconfig fragments)
So all splits on file level - which should be easy to do.

This will make it easier for more people to give feedback. It is a bit
much to walk through 4k loc or what the full size is.
And then we can also provide a-b or r-b for fragments so the reviewed
parts can be ignored for later reviews.


For the DSI parts I may be hit by the old "when you have a hammer then
everything looks like a nail syndrome".
In my case the hammer is the bridge interface.

Suggestions:
- Move encoder to display driver
- Move connector creation to display driver (using
  drm_bridge_connector_init())
- Use drm_bridge interface for the DSI driver parts
- Maybe use the HDMI bridge driver as a chained driver - I did not look
  to close on this
- Use the standard bridge interface to find the bridge and drop use of
  the component framework

I hope that some other drm people chime in here.
Either to tell this is non-sense or confirm this is the way to go.

The above does not give any hint how to use the gpio_mux to shift
between the panel and HDMI. This logic needs to be in the display driver
as the bridge driver will only be used for HDMI - as I understand the
code. And I do not know how to do it.

Some details in the following that are not related to the above.

	Sam

> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
> new file mode 100644
> index 000000000000..a2eed961b7d5
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_drm_dsi.c
> @@ -0,0 +1,2126 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * DesignWare MIPI DSI Host Controller v1.02 driver
> + *
> + * Copyright (c) 2016 Linaro Limited.
> + * Copyright (c) 2014-2016 Hisilicon Limited.
> + * Copyright (c) 2014-2020, Huawei Technologies Co., Ltd
> + *
> + * Author:
> + *	<shizongxuan@huawei.com>
> + *	<zhangxiubin@huawei.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/iopoll.h>
> +#include <video/mipi_display.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/of_address.h>
Sort

> +
> +#include <drm/drm_of.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_encoder.h>
> +#include <drm/drm_device.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_probe_helper.h>
Sort

> +
> +#include "kirin9xx_dw_dsi_reg.h"
> +#include "kirin9xx_dpe.h"
> +#include "kirin9xx_drm_drv.h"
> +#include "kirin9xx_drm_dpe_utils.h"
Sort

> +
> +#define PHY_REF_CLK_RATE	19200000
> +#define PHY_REF_CLK_PERIOD_PS	(1000000000 / (PHY_REF_CLK_RATE / 1000))
> +
> +#define encoder_to_dsi(encoder) \
> +	container_of(encoder, struct dw_dsi, encoder)
> +#define host_to_dsi(host) \
> +	container_of(host, struct dw_dsi, host)
> +#define connector_to_dsi(connector) \
> +	container_of(connector, struct dw_dsi, connector)
Move the upcast next to the struct definition.


> +#define DSS_REDUCE(x)	((x) > 0 ? ((x) - 1) : (x))
> +
> +enum dsi_output_client {
> +	OUT_HDMI = 0,
> +	OUT_PANEL,
> +	OUT_MAX
> +};
> +
> +struct mipi_phy_params {
I did not check all fiels but I managed to find at least one unused
field. Cheack and delete what is unused.
> +	u64 lane_byte_clk;
> +	u32 clk_division;
> +
> +	u32 clk_lane_lp2hs_time;
> +	u32 clk_lane_hs2lp_time;
> +	u32 data_lane_lp2hs_time;
> +	u32 data_lane_hs2lp_time;
> +	u32 clk2data_delay;
> +	u32 data2clk_delay;
> +
> +	u32 clk_pre_delay;
> +	u32 clk_post_delay;
> +	u32 clk_t_lpx;
> +	u32 clk_t_hs_prepare;
> +	u32 clk_t_hs_zero;
> +	u32 clk_t_hs_trial;
> +	u32 clk_t_wakeup;
> +	u32 data_pre_delay;
> +	u32 data_post_delay;
> +	u32 data_t_lpx;
> +	u32 data_t_hs_prepare;
> +	u32 data_t_hs_zero;
> +	u32 data_t_hs_trial;
> +	u32 data_t_ta_go;
> +	u32 data_t_ta_get;
> +	u32 data_t_wakeup;
> +
> +	u32 phy_stop_wait_time;
> +
> +	u32 rg_vrefsel_vcm;
> +	u32 rg_hstx_ckg_sel;
> +	u32 rg_pll_fbd_div5f;
> +	u32 rg_pll_fbd_div1f;
> +	u32 rg_pll_fbd_2p;
> +	u32 rg_pll_enbwt;
> +	u32 rg_pll_fbd_p;
> +	u32 rg_pll_fbd_s;
> +	u32 rg_pll_pre_div1p;
> +	u32 rg_pll_pre_p;
> +	u32 rg_pll_vco_750m;
> +	u32 rg_pll_lpf_rs;
> +	u32 rg_pll_lpf_cs;
> +	u32 rg_pll_enswc;
> +	u32 rg_pll_chp;
> +
Some indent gone wrong here?
> +	u32 pll_register_override;		/* 0x1E[0] */
> +	u32 pll_power_down;			/* 0x1E[1] */
> +	u32 rg_band_sel;				/* 0x1E[2] */
> +	u32 rg_phase_gen_en;		/* 0x1E[3] */
> +	u32 reload_sel;				/* 0x1E[4] */
> +	u32 rg_pll_cp_p;				/* 0x1E[7:5] */
> +	u32 rg_pll_refsel;				/* 0x16[1:0] */
> +	u32 rg_pll_cp;				/* 0x16[7:5] */
> +	u32 load_command;
> +
> +	/* for CDPHY */
> +	u32 rg_cphy_div;	/* Q */
> +	u32 rg_div;		/* M 0x4A[7:0] */
> +	u32 rg_pre_div;	/* N 0x49[0] */
> +	u32 rg_320m;		/* 0x48[2] */
> +	u32 rg_2p5g;		/* 0x48[1] */
> +	u32 rg_0p8v;		/* 0x48[0] */
> +	u32 rg_lpf_r;		/* 0x46[5:4] */
> +	u32 rg_cp;			/* 0x46[3:0] */
> +	u32 t_prepare;
> +	u32 t_lpx;
> +	u32 t_prebegin;
> +	u32 t_post;
> +};
> +
> +struct dsi_hw_ctx {
> +	void __iomem *base;
> +	char __iomem *peri_crg_base;
> +	void __iomem *pctrl_base;
> +
> +	u32 g_dss_version_tag;
> +
> +	struct clk *dss_dphy0_ref_clk;
> +	struct clk *dss_dphy1_ref_clk;
Not used, delete.
> +	struct clk *dss_dphy0_cfg_clk;
> +	struct clk *dss_dphy1_cfg_clk;
Not used, delete.

> +	struct clk *dss_pclk_dsi0_clk;
> +	struct clk *dss_pclk_dsi1_clk;
Not sued, delete.
> +};
> +
> +struct dw_dsi_client {
> +	u32 lanes;
> +	u32 phy_clock; /* in kHz */
> +	enum mipi_dsi_pixel_format format;
> +	unsigned long mode_flags;
> +};
> +
> +struct mipi_panel_info {
Some filed in the following are either not used or in some cases only
read. Clean it up.

Example: clk_t_hs_exit_adjust is assigned 0 and only read.
Looks like stuff to drop.

> +	u8 dsi_version;
> +	u8 vc;
> +	u8 lane_nums;
> +	u8 lane_nums_select_support;
> +	u8 color_mode;
> +	u32 dsi_bit_clk; /* clock lane(p/n) */
> +	u32 burst_mode;
> +	u32 max_tx_esc_clk;
> +	u8 non_continue_en;
> +
> +	u32 dsi_bit_clk_val1;
> +	u32 dsi_bit_clk_val2;
> +	u32 dsi_bit_clk_val3;
> +	u32 dsi_bit_clk_val4;
> +	u32 dsi_bit_clk_val5;
> +	u32 dsi_bit_clk_upt;
> +	/* uint32_t dsi_pclk_rate; */
> +
> +	u32 hs_wr_to_time;
> +
> +	/* dphy config parameter adjust */
> +	u32 clk_post_adjust;
> +	u32 clk_pre_adjust;
> +	u32 clk_pre_delay_adjust;
> +	u32 clk_t_hs_exit_adjust;
> +	u32 clk_t_hs_trial_adjust;
> +	u32 clk_t_hs_prepare_adjust;
> +	int clk_t_lpx_adjust;
> +	u32 clk_t_hs_zero_adjust;
> +	u32 data_post_delay_adjust;
> +	int data_t_lpx_adjust;
> +	u32 data_t_hs_prepare_adjust;
> +	u32 data_t_hs_zero_adjust;
> +	u32 data_t_hs_trial_adjust;
> +	u32 rg_vrefsel_vcm_adjust;
> +
> +	/* only for Chicago<3660> use */
> +	u32 rg_vrefsel_vcm_clk_adjust;
> +	u32 rg_vrefsel_vcm_data_adjust;
> +
> +	u32 phy_mode;  /* 0: DPHY, 1:CPHY */
> +	u32 lp11_flag;
> +};
> +
> +struct ldi_panel_info {
Fileds are only partially used.

> +	u32 h_back_porch;
> +	u32 h_front_porch;
> +	u32 h_pulse_width;
> +
> +	/*
> +	 * note: vbp > 8 if used overlay compose,
> +	 * also lcd vbp > 8 in lcd power on sequence
> +	 */
> +	u32 v_back_porch;
> +	u32 v_front_porch;
> +	u32 v_pulse_width;
> +
> +	u8 hsync_plr;
> +	u8 vsync_plr;
> +	u8 pixelclk_plr;
> +	u8 data_en_plr;
> +
> +	/* for cabc */
> +	u8 dpi0_overlap_size;
> +	u8 dpi1_overlap_size;
> +};
> +
> +struct dw_dsi {
> +	struct drm_encoder encoder;
> +	struct drm_bridge *bridge;
> +	struct drm_panel *panel;
> +	struct mipi_dsi_host host;
> +	struct drm_connector connector; /* connector for panel */
> +	struct drm_display_mode cur_mode;
> +	struct dsi_hw_ctx *ctx;
Would it be possible to embed dsi_hw_ctx?
So everything is allocated in one go.

> +	struct mipi_phy_params phy;
> +	struct mipi_panel_info mipi;
> +	struct ldi_panel_info ldi;
> +	u32 lanes;
> +	enum mipi_dsi_pixel_format format;
> +	unsigned long mode_flags;
> +	struct gpio_desc *gpio_mux;
> +	struct dw_dsi_client client[OUT_MAX];
> +	enum dsi_output_client cur_client, attached_client;
> +	bool enable;
> +};
> +
> +struct dsi_data {
> +	struct dw_dsi dsi;
> +	struct dsi_hw_ctx ctx;
> +};
If dsi_hw_ctx is embedded in dw_dsi then this struct can be dropped.
> +
> +struct dsi_phy_range {
> +	u32 min_range_kHz;
> +	u32 max_range_kHz;
> +	u32 pll_vco_750M;
> +	u32 hstx_ckg_sel;
> +};
Not used, delete.
> +
> +static const struct dsi_phy_range dphy_range_info[] = {
> +	{   46875,    62500,   1,    7 },
> +	{   62500,    93750,   0,    7 },
> +	{   93750,   125000,   1,    6 },
> +	{  125000,   187500,   0,    6 },
> +	{  187500,   250000,   1,    5 },
> +	{  250000,   375000,   0,    5 },
> +	{  375000,   500000,   1,    4 },
> +	{  500000,   750000,   0,    4 },
> +	{  750000,  1000000,   1,    0 },
> +	{ 1000000,  1500000,   0,    0 }
> +};
Not used, delete.

> +
> +/*
> + * Except for debug, this is identical to the one defined at
> + * kirin9xx_drm_dpe_utils.h.
> + */
> +
> +void dsi_set_output_client(struct drm_device *dev)
> +{
> +	struct drm_connector_list_iter conn_iter;
> +	struct drm_connector *connector;
> +	enum dsi_output_client client;
> +	struct drm_encoder *encoder;
> +	struct dw_dsi *dsi;
> +
> +	mutex_lock(&dev->mode_config.mutex);
> +
> +	/* find dsi encoder */
> +	drm_for_each_encoder(encoder, dev)
> +		if (encoder->encoder_type == DRM_MODE_ENCODER_DSI)
> +			break;
> +	dsi = encoder_to_dsi(encoder);
> +
> +	/* find HDMI connector */
> +	drm_connector_list_iter_begin(dev, &conn_iter);
> +	drm_for_each_connector_iter(connector, &conn_iter)
> +		if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)
> +			break;
> +
> +	/*
> +	 * set the proper dsi output client
> +	 */
> +	client = connector->status == connector_status_connected ?
> +		OUT_HDMI : OUT_PANEL;
> +
> +	if (client != dsi->cur_client) {
> +		gpiod_set_value_cansleep(dsi->gpio_mux, client);
> +		dsi->cur_client = client;
> +
> +		msleep(20);
> +
> +		drm_info(dev, "client change to %s\n",
> +			 client == OUT_HDMI ? "HDMI" : "panel");
> +	}
> +
> +	mutex_unlock(&dev->mode_config.mutex);
> +}
> +EXPORT_SYMBOL(dsi_set_output_client);
> +
> +static void kirin970_get_dsi_dphy_ctrl(struct dw_dsi *dsi,
> +				       struct mipi_phy_params *phy_ctrl, u32 id)
> +{
> +	struct mipi_panel_info *mipi = NULL;
> +	struct drm_display_mode *mode = NULL;
> +	u32 dphy_req_kHz;
> +	int bpp;
> +	u32 ui = 0;
> +	u32 m_pll = 0;
> +	u32 n_pll = 0;
> +	u64 lane_clock = 0;
> +	u64 vco_div = 1;
> +	u32 m_n_fract = 0;
> +	u32 m_n_int = 0;
> +
> +	u32 accuracy = 0;
> +	u32 unit_tx_byte_clk_hs = 0;
> +	u32 clk_post = 0;
> +	u32 clk_pre = 0;
> +	u32 clk_t_hs_exit = 0;
> +	u32 clk_pre_delay = 0;
> +	u32 clk_t_hs_prepare = 0;
> +	u32 clk_t_lpx = 0;
> +	u32 clk_t_hs_zero = 0;
> +	u32 clk_t_hs_trial = 0;
> +	u32 data_post_delay = 0;
> +	u32 data_t_hs_prepare = 0;
> +	u32 data_t_hs_zero = 0;
> +	u32 data_t_hs_trial = 0;
> +	u32 data_t_lpx = 0;
> +
> +	WARN_ON(!phy_ctrl);
> +	WARN_ON(!dsi);
> +
> +	mode = &dsi->cur_mode;
> +	mipi = &dsi->mipi;
> +
> +	/*
> +	 * count phy params
> +	 */
> +	bpp = mipi_dsi_pixel_format_to_bpp(dsi->client[id].format);
> +	if (bpp < 0)
> +		return;
> +
> +	if (mode->clock > 80000)
> +		dsi->client[id].lanes = 4;
> +	else
> +		dsi->client[id].lanes = 3;
> +
> +	if (dsi->client[id].phy_clock)
> +		dphy_req_kHz = dsi->client[id].phy_clock;
> +	else
> +		dphy_req_kHz = mode->clock * bpp / dsi->client[id].lanes;
> +
> +	lane_clock = dphy_req_kHz / 1000;
> +	DRM_DEBUG("Expected : lane_clock = %llu M\n", lane_clock);
> +
> +	/************************  PLL parameters config  *********************/
> +	/* chip spec : */
> +	/* If the output data rate is below 320 Mbps, RG_BNAD_SEL should be set to 1. */
> +	/* At this mode a post divider of 1/4 will be applied to VCO. */
> +	if ((lane_clock >= 320) && (lane_clock <= 2500)) {
> +		phy_ctrl->rg_band_sel = 0;
> +		vco_div = 1;
> +	} else if ((lane_clock >= 80) && (lane_clock < 320)) {
> +		phy_ctrl->rg_band_sel = 1;
> +		vco_div = 4;
> +	} else {
> +		DRM_ERROR("80M <= lane_clock< = 2500M, not support lane_clock = %llu M.\n",
> +			  lane_clock);
> +	}
> +
> +	m_n_int = lane_clock * vco_div * 1000000UL / DEFAULT_MIPI_CLK_RATE;
> +	m_n_fract = ((lane_clock * vco_div * 1000000UL * 1000UL / DEFAULT_MIPI_CLK_RATE) % 1000) * 10 / 1000;
> +
> +	n_pll = 2;
> +
> +	m_pll = (u32)(lane_clock * vco_div * n_pll * 1000000UL / DEFAULT_MIPI_CLK_RATE);
> +
> +	lane_clock = m_pll * (DEFAULT_MIPI_CLK_RATE / n_pll) / vco_div;
> +	if (lane_clock > 750000000)
> +		phy_ctrl->rg_cp = 3;
> +	else if ((lane_clock >= 80000000) && (lane_clock <= 750000000))
> +		phy_ctrl->rg_cp = 1;
> +	else
> +		DRM_ERROR("80M <= lane_clock< = 2500M, not support lane_clock = %llu M.\n",
> +			  lane_clock);
> +
> +	/* chip spec : */
> +	phy_ctrl->rg_pre_div = n_pll - 1;
> +	phy_ctrl->rg_div = m_pll;
> +	phy_ctrl->rg_0p8v = 0;
> +	phy_ctrl->rg_2p5g = 1;
> +	phy_ctrl->rg_320m = 0;
> +	phy_ctrl->rg_lpf_r = 0;
> +
> +	/* TO DO HSTX select VCM VREF */
> +	phy_ctrl->rg_vrefsel_vcm = 0x5d;
> +
> +	/********************  clock/data lane parameters config  ******************/
> +	accuracy = 10;
> +	ui =  (u32)(10 * 1000000000UL * accuracy / lane_clock);
> +	/* unit of measurement */
> +	unit_tx_byte_clk_hs = 8 * ui;
> +
> +	/* D-PHY Specification : 60ns + 52*UI <= clk_post */
> +	clk_post = 600 * accuracy + 52 * ui + unit_tx_byte_clk_hs + mipi->clk_post_adjust * ui;
> +
> +	/* D-PHY Specification : clk_pre >= 8*UI */
> +	clk_pre = 8 * ui + unit_tx_byte_clk_hs + mipi->clk_pre_adjust * ui;
> +
> +	/* D-PHY Specification : clk_t_hs_exit >= 100ns */
> +	clk_t_hs_exit = 1000 * accuracy + 100 * accuracy + mipi->clk_t_hs_exit_adjust * ui;
> +
> +	/* clocked by TXBYTECLKHS */
> +	clk_pre_delay = 0 + mipi->clk_pre_delay_adjust * ui;
> +
> +	/* D-PHY Specification : clk_t_hs_trial >= 60ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_hs_trial = 600 * accuracy + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_trial_adjust * ui;
> +
> +	/* D-PHY Specification : 38ns <= clk_t_hs_prepare <= 95ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_hs_prepare = 660 * accuracy;
> +
> +	/* clocked by TXBYTECLKHS */
> +	data_post_delay = 0 + mipi->data_post_delay_adjust * ui;
> +
> +	/* D-PHY Specification : data_t_hs_trial >= max( n*8*UI, 60ns + n*4*UI ), n = 1 */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_hs_trial = ((600 * accuracy + 4 * ui) >= (8 * ui) ? (600 * accuracy + 4 * ui) : (8 * ui)) +
> +		2 * unit_tx_byte_clk_hs + mipi->data_t_hs_trial_adjust * ui;
> +
> +	/* D-PHY Specification : 40ns + 4*UI <= data_t_hs_prepare <= 85ns + 6*UI */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_hs_prepare = 400 * accuracy + 4 * ui;
> +	/* D-PHY chip spec : clk_t_lpx + clk_t_hs_prepare > 200ns */
> +	/* D-PHY Specification : clk_t_lpx >= 50ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_lpx = (uint32_t)(2000 * accuracy + 10 * accuracy + mipi->clk_t_lpx_adjust * ui - clk_t_hs_prepare);
> +
> +	/* D-PHY Specification : clk_t_hs_zero + clk_t_hs_prepare >= 300 ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_hs_zero = (uint32_t)(3000 * accuracy + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_zero_adjust * ui - clk_t_hs_prepare);
> +
> +	/* D-PHY chip spec : data_t_lpx + data_t_hs_prepare > 200ns */
> +	/* D-PHY Specification : data_t_lpx >= 50ns */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_lpx = (uint32_t)(2000 * accuracy + 10 * accuracy + mipi->data_t_lpx_adjust * ui - data_t_hs_prepare);
> +
> +	/* D-PHY Specification : data_t_hs_zero + data_t_hs_prepare >= 145ns + 10*UI */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_hs_zero = (uint32_t)(1450 * accuracy + 10 * ui +
> +		3 * unit_tx_byte_clk_hs + mipi->data_t_hs_zero_adjust * ui - data_t_hs_prepare);
> +
> +	phy_ctrl->clk_pre_delay = DIV_ROUND_UP(clk_pre_delay, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_prepare = DIV_ROUND_UP(clk_t_hs_prepare, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_lpx = DIV_ROUND_UP(clk_t_lpx, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_zero = DIV_ROUND_UP(clk_t_hs_zero, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_trial = DIV_ROUND_UP(clk_t_hs_trial, unit_tx_byte_clk_hs);
> +
> +	phy_ctrl->data_post_delay = DIV_ROUND_UP(data_post_delay, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_prepare = DIV_ROUND_UP(data_t_hs_prepare, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_lpx = DIV_ROUND_UP(data_t_lpx, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_zero = DIV_ROUND_UP(data_t_hs_zero, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_trial = DIV_ROUND_UP(data_t_hs_trial, unit_tx_byte_clk_hs);
> +
> +	phy_ctrl->clk_post_delay = phy_ctrl->data_t_hs_trial + DIV_ROUND_UP(clk_post, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_pre_delay = phy_ctrl->clk_pre_delay + 2 + phy_ctrl->clk_t_lpx +
> +		phy_ctrl->clk_t_hs_prepare + phy_ctrl->clk_t_hs_zero + 8 + DIV_ROUND_UP(clk_pre, unit_tx_byte_clk_hs);
> +
> +	phy_ctrl->clk_lane_lp2hs_time = phy_ctrl->clk_pre_delay + phy_ctrl->clk_t_lpx + phy_ctrl->clk_t_hs_prepare +
> +		phy_ctrl->clk_t_hs_zero + 5 + 7;
> +	phy_ctrl->clk_lane_hs2lp_time = phy_ctrl->clk_t_hs_trial + phy_ctrl->clk_post_delay + 8 + 4;
> +	phy_ctrl->data_lane_lp2hs_time = phy_ctrl->data_pre_delay + phy_ctrl->data_t_lpx + phy_ctrl->data_t_hs_prepare +
> +		phy_ctrl->data_t_hs_zero + 5 + 7;
> +	phy_ctrl->data_lane_hs2lp_time = phy_ctrl->data_t_hs_trial + 8 + 5;
> +
> +	phy_ctrl->phy_stop_wait_time = phy_ctrl->clk_post_delay + 4 + phy_ctrl->clk_t_hs_trial +
> +		DIV_ROUND_UP(clk_t_hs_exit, unit_tx_byte_clk_hs) - (phy_ctrl->data_post_delay + 4 + phy_ctrl->data_t_hs_trial) + 3;
> +
> +	phy_ctrl->lane_byte_clk = lane_clock / 8;
> +	phy_ctrl->clk_division = (((phy_ctrl->lane_byte_clk / 2) % mipi->max_tx_esc_clk) > 0) ?
> +		(uint32_t)(phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk + 1) :
> +		(uint32_t)(phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk);
> +
> +	DRM_DEBUG("DPHY clock_lane and data_lane config :\n"
> +		"lane_clock = %llu, n_pll=%d, m_pll=%d\n"
> +		"rg_cp=%d\n"
> +		"rg_band_sel=%d\n"
> +		"rg_vrefsel_vcm=%d\n"
> +		"clk_pre_delay=%d\n"
> +		"clk_post_delay=%d\n"
> +		"clk_t_hs_prepare=%d\n"
> +		"clk_t_lpx=%d\n"
> +		"clk_t_hs_zero=%d\n"
> +		"clk_t_hs_trial=%d\n"
> +		"data_pre_delay=%d\n"
> +		"data_post_delay=%d\n"
> +		"data_t_hs_prepare=%d\n"
> +		"data_t_lpx=%d\n"
> +		"data_t_hs_zero=%d\n"
> +		"data_t_hs_trial=%d\n"
> +		"clk_lane_lp2hs_time=%d\n"
> +		"clk_lane_hs2lp_time=%d\n"
> +		"data_lane_lp2hs_time=%d\n"
> +		"data_lane_hs2lp_time=%d\n"
> +		"phy_stop_wait_time=%d\n",
> +		lane_clock, n_pll, m_pll,
> +		phy_ctrl->rg_cp,
> +		phy_ctrl->rg_band_sel,
> +		phy_ctrl->rg_vrefsel_vcm,
> +		phy_ctrl->clk_pre_delay,
> +		phy_ctrl->clk_post_delay,
> +		phy_ctrl->clk_t_hs_prepare,
> +		phy_ctrl->clk_t_lpx,
> +		phy_ctrl->clk_t_hs_zero,
> +		phy_ctrl->clk_t_hs_trial,
> +		phy_ctrl->data_pre_delay,
> +		phy_ctrl->data_post_delay,
> +		phy_ctrl->data_t_hs_prepare,
> +		phy_ctrl->data_t_lpx,
> +		phy_ctrl->data_t_hs_zero,
> +		phy_ctrl->data_t_hs_trial,
> +		phy_ctrl->clk_lane_lp2hs_time,
> +		phy_ctrl->clk_lane_hs2lp_time,
> +		phy_ctrl->data_lane_lp2hs_time,
> +		phy_ctrl->data_lane_hs2lp_time,
> +		phy_ctrl->phy_stop_wait_time);
> +}
> +
> +static void kirin960_get_dsi_phy_ctrl(struct dw_dsi *dsi,
> +				      struct mipi_phy_params *phy_ctrl, u32 id)
> +{
> +	struct mipi_panel_info *mipi = NULL;
> +	struct drm_display_mode *mode = NULL;
> +	u32 dphy_req_kHz;
> +	int bpp;
> +	u32 ui = 0;
> +	u32 m_pll = 0;
> +	u32 n_pll = 0;
> +	u32 m_n_fract = 0;
> +	u32 m_n_int = 0;
> +	u64 lane_clock = 0;
> +	u64 vco_div = 1;
> +
> +	u32 accuracy = 0;
> +	u32 unit_tx_byte_clk_hs = 0;
> +	u32 clk_post = 0;
> +	u32 clk_pre = 0;
> +	u32 clk_t_hs_exit = 0;
> +	u32 clk_pre_delay = 0;
> +	u32 clk_t_hs_prepare = 0;
> +	u32 clk_t_lpx = 0;
> +	u32 clk_t_hs_zero = 0;
> +	u32 clk_t_hs_trial = 0;
> +	u32 data_post_delay = 0;
> +	u32 data_t_hs_prepare = 0;
> +	u32 data_t_hs_zero = 0;
> +	u32 data_t_hs_trial = 0;
> +	u32 data_t_lpx = 0;
> +	u32 clk_pre_delay_reality = 0;
> +	u32 clk_t_hs_zero_reality = 0;
> +	u32 clk_post_delay_reality = 0;
> +	u32 data_t_hs_zero_reality = 0;
> +	u32 data_post_delay_reality = 0;
> +	u32 data_pre_delay_reality = 0;
> +
> +	WARN_ON(!phy_ctrl);
> +	WARN_ON(!dsi);
> +
> +	mode = &dsi->cur_mode;
> +	mipi = &dsi->mipi;
> +
> +	/*
> +	 * count phy params
> +	 */
> +	bpp = mipi_dsi_pixel_format_to_bpp(dsi->client[id].format);
> +	if (bpp < 0)
> +		return;
> +
> +	if (mode->clock > 80000)
> +		dsi->client[id].lanes = 4;
> +	else
> +		dsi->client[id].lanes = 3;
> +
> +	if (dsi->client[id].phy_clock)
> +		dphy_req_kHz = dsi->client[id].phy_clock;
> +	else
> +		dphy_req_kHz = mode->clock * bpp / dsi->client[id].lanes;
> +
> +	lane_clock = dphy_req_kHz / 1000;
> +	DRM_INFO("Expected : lane_clock = %llu M\n", lane_clock);
> +
> +	/************************  PLL parameters config  *********************/
> +
> +	/*
> +	 * chip spec :
> +	 *	If the output data rate is below 320 Mbps,
> +	 *	RG_BNAD_SEL should be set to 1.
> +	 *	At this mode a post divider of 1/4 will be applied to VCO.
> +	 */
> +	if ((lane_clock >= 320) && (lane_clock <= 2500)) {
> +		phy_ctrl->rg_band_sel = 0;	/* 0x1E[2] */
> +		vco_div = 1;
> +	} else if ((lane_clock >= 80) && (lane_clock < 320)) {
> +		phy_ctrl->rg_band_sel = 1;
> +		vco_div = 4;
> +	} else {
> +		DRM_ERROR("80M <= lane_clock< = 2500M, not support lane_clock = %llu M\n",
> +			  lane_clock);
> +	}
> +
> +	m_n_int = lane_clock * vco_div * 1000000UL / DEFAULT_MIPI_CLK_RATE;
> +	m_n_fract = ((lane_clock * vco_div * 1000000UL * 1000UL / DEFAULT_MIPI_CLK_RATE) % 1000) * 10 / 1000;
> +
> +	if (m_n_int % 2 == 0) {
> +		if (m_n_fract * 6 >= 50) {
> +			n_pll = 2;
> +			m_pll = (m_n_int + 1) * n_pll;
> +		} else if (m_n_fract * 6 >= 30) {
> +			n_pll = 3;
> +			m_pll = m_n_int * n_pll + 2;
> +		} else {
> +			n_pll = 1;
> +			m_pll = m_n_int * n_pll;
> +		}
> +	} else {
> +		if (m_n_fract * 6 >= 50) {
> +			n_pll = 1;
> +			m_pll = (m_n_int + 1) * n_pll;
> +		} else if (m_n_fract * 6 >= 30) {
> +			n_pll = 1;
> +			m_pll = (m_n_int + 1) * n_pll;
> +		} else if (m_n_fract * 6 >= 10) {
> +			n_pll = 3;
> +			m_pll = m_n_int * n_pll + 1;
> +		} else {
> +			n_pll = 2;
> +			m_pll = m_n_int * n_pll;
> +		}
> +	}
> +
> +	/* if set rg_pll_enswc=1, rg_pll_fbd_s can't be 0 */
> +	if (m_pll <= 8) {
> +		phy_ctrl->rg_pll_fbd_s = 1;
> +		phy_ctrl->rg_pll_enswc = 0;
> +
> +		if (m_pll % 2 == 0) {
> +			phy_ctrl->rg_pll_fbd_p = m_pll / 2;
> +		} else {
> +			if (n_pll == 1) {
> +				n_pll *= 2;
> +				phy_ctrl->rg_pll_fbd_p = (m_pll  * 2) / 2;
> +			} else {
> +				DRM_ERROR("phy m_pll not support!m_pll = %d\n", m_pll);
> +				return;
> +			}
> +		}
> +	} else if (m_pll <= 300) {
> +		if (m_pll % 2 == 0)
> +			phy_ctrl->rg_pll_enswc = 0;
> +		else
> +			phy_ctrl->rg_pll_enswc = 1;
> +
> +		phy_ctrl->rg_pll_fbd_s = 1;
> +		phy_ctrl->rg_pll_fbd_p = m_pll / 2;
> +	} else if (m_pll <= 315) {
> +		phy_ctrl->rg_pll_fbd_p = 150;
> +		phy_ctrl->rg_pll_fbd_s = m_pll - 2 * phy_ctrl->rg_pll_fbd_p;
> +		phy_ctrl->rg_pll_enswc = 1;
> +	} else {
> +		DRM_ERROR("phy m_pll not support!m_pll = %d\n", m_pll);
> +		return;
> +	}
> +
> +	phy_ctrl->rg_pll_pre_p = n_pll;
> +
> +	lane_clock = m_pll * (DEFAULT_MIPI_CLK_RATE / n_pll) / vco_div;
> +	DRM_INFO("Config : lane_clock = %llu\n", lane_clock);
> +
> +	/* FIXME : */
> +	phy_ctrl->rg_pll_cp = 1;		/* 0x16[7:5] */
> +	phy_ctrl->rg_pll_cp_p = 3;		/* 0x1E[7:5] */
> +
> +	/* test_code_0x14 other parameters config */
> +	phy_ctrl->rg_pll_enbwt = 0;	/* 0x14[2] */
> +	phy_ctrl->rg_pll_chp = 0;		/* 0x14[1:0] */
> +
> +	/* test_code_0x16 other parameters config,  0x16[3:2] reserved */
> +	phy_ctrl->rg_pll_lpf_cs = 0;	/* 0x16[4] */
> +	phy_ctrl->rg_pll_refsel = 1;		/* 0x16[1:0] */
> +
> +	/* test_code_0x1E other parameters config */
> +	phy_ctrl->reload_sel = 1;			/* 0x1E[4] */
> +	phy_ctrl->rg_phase_gen_en = 1;	/* 0x1E[3] */
> +	phy_ctrl->pll_power_down = 0;		/* 0x1E[1] */
> +	phy_ctrl->pll_register_override = 1;	/* 0x1E[0] */
> +
> +	/* HSTX select VCM VREF */
> +	phy_ctrl->rg_vrefsel_vcm = 0x55;
> +	if (mipi->rg_vrefsel_vcm_clk_adjust != 0)
> +		phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0x0F) |
> +			((mipi->rg_vrefsel_vcm_clk_adjust & 0x0F) << 4);
> +
> +	if (mipi->rg_vrefsel_vcm_data_adjust != 0)
> +		phy_ctrl->rg_vrefsel_vcm = (phy_ctrl->rg_vrefsel_vcm & 0xF0) |
> +			(mipi->rg_vrefsel_vcm_data_adjust & 0x0F);
> +
> +	/* if reload_sel = 1, need to set load_command */
> +	phy_ctrl->load_command = 0x5A;
> +
> +	/********************  clock/data lane parameters config  ******************/
> +	accuracy = 10;
> +	ui =  10 * 1000000000UL * accuracy / lane_clock;
> +	/* unit of measurement */
> +	unit_tx_byte_clk_hs = 8 * ui;
> +
> +	/* D-PHY Specification : 60ns + 52*UI <= clk_post */
> +	clk_post = 600 * accuracy + 52 * ui + mipi->clk_post_adjust * ui;
> +
> +	/* D-PHY Specification : clk_pre >= 8*UI */
> +	clk_pre = 8 * ui + mipi->clk_pre_adjust * ui;
> +
> +	/* D-PHY Specification : clk_t_hs_exit >= 100ns */
> +	clk_t_hs_exit = 1000 * accuracy + mipi->clk_t_hs_exit_adjust * ui;
> +
> +	/* clocked by TXBYTECLKHS */
> +	clk_pre_delay = 0 + mipi->clk_pre_delay_adjust * ui;
> +
> +	/* D-PHY Specification : clk_t_hs_trial >= 60ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_hs_trial = 600 * accuracy + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_trial_adjust * ui;
> +
> +	/* D-PHY Specification : 38ns <= clk_t_hs_prepare <= 95ns */
> +	/* clocked by TXBYTECLKHS */
> +	if (mipi->clk_t_hs_prepare_adjust == 0)
> +		mipi->clk_t_hs_prepare_adjust = 43;
> +
> +	clk_t_hs_prepare = ((380 * accuracy + mipi->clk_t_hs_prepare_adjust * ui) <= (950 * accuracy - 8 * ui)) ?
> +		(380 * accuracy + mipi->clk_t_hs_prepare_adjust * ui) : (950 * accuracy - 8 * ui);
> +
> +	/* clocked by TXBYTECLKHS */
> +	data_post_delay = 0 + mipi->data_post_delay_adjust * ui;
> +
> +	/* D-PHY Specification : data_t_hs_trial >= max( n*8*UI, 60ns + n*4*UI ), n = 1 */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_hs_trial = ((600 * accuracy + 4 * ui) >= (8 * ui) ? (600 * accuracy + 4 * ui) : (8 * ui)) + 8 * ui +
> +		3 * unit_tx_byte_clk_hs + mipi->data_t_hs_trial_adjust * ui;
> +
> +	/* D-PHY Specification : 40ns + 4*UI <= data_t_hs_prepare <= 85ns + 6*UI */
> +	/* clocked by TXBYTECLKHS */
> +	if (mipi->data_t_hs_prepare_adjust == 0)
> +		mipi->data_t_hs_prepare_adjust = 35;
> +
> +	data_t_hs_prepare = ((400  * accuracy + 4 * ui + mipi->data_t_hs_prepare_adjust * ui) <= (850 * accuracy + 6 * ui - 8 * ui)) ?
> +		(400  * accuracy + 4 * ui + mipi->data_t_hs_prepare_adjust * ui) : (850 * accuracy + 6 * ui - 8 * ui);
> +
> +	/* D-PHY chip spec : clk_t_lpx + clk_t_hs_prepare > 200ns */
> +	/* D-PHY Specification : clk_t_lpx >= 50ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_lpx = (((2000 * accuracy - clk_t_hs_prepare) >= 500 * accuracy) ?
> +		((2000 * accuracy - clk_t_hs_prepare)) : (500 * accuracy)) +
> +		mipi->clk_t_lpx_adjust * ui;
> +
> +	/* D-PHY Specification : clk_t_hs_zero + clk_t_hs_prepare >= 300 ns */
> +	/* clocked by TXBYTECLKHS */
> +	clk_t_hs_zero = 3000 * accuracy - clk_t_hs_prepare + 3 * unit_tx_byte_clk_hs + mipi->clk_t_hs_zero_adjust * ui;
> +
> +	/* D-PHY chip spec : data_t_lpx + data_t_hs_prepare > 200ns */
> +	/* D-PHY Specification : data_t_lpx >= 50ns */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_lpx = clk_t_lpx + mipi->data_t_lpx_adjust * ui; /* 2000 * accuracy - data_t_hs_prepare; */
> +
> +	/* D-PHY Specification : data_t_hs_zero + data_t_hs_prepare >= 145ns + 10*UI */
> +	/* clocked by TXBYTECLKHS */
> +	data_t_hs_zero = 1450 * accuracy + 10 * ui - data_t_hs_prepare +
> +		3 * unit_tx_byte_clk_hs + mipi->data_t_hs_zero_adjust * ui;
> +
> +	phy_ctrl->clk_pre_delay = DIV_ROUND_UP(clk_pre_delay, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_prepare = DIV_ROUND_UP(clk_t_hs_prepare, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_lpx = DIV_ROUND_UP(clk_t_lpx, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_zero = DIV_ROUND_UP(clk_t_hs_zero, unit_tx_byte_clk_hs);
> +	phy_ctrl->clk_t_hs_trial = DIV_ROUND_UP(clk_t_hs_trial, unit_tx_byte_clk_hs);
> +
> +	phy_ctrl->data_post_delay = DIV_ROUND_UP(data_post_delay, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_prepare = DIV_ROUND_UP(data_t_hs_prepare, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_lpx = DIV_ROUND_UP(data_t_lpx, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_zero = DIV_ROUND_UP(data_t_hs_zero, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_hs_trial = DIV_ROUND_UP(data_t_hs_trial, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_t_ta_go = 4;
> +	phy_ctrl->data_t_ta_get = 5;
> +
> +	clk_pre_delay_reality = phy_ctrl->clk_pre_delay + 2;
> +	clk_t_hs_zero_reality = phy_ctrl->clk_t_hs_zero + 8;
> +	data_t_hs_zero_reality = phy_ctrl->data_t_hs_zero + 4;
> +	data_post_delay_reality = phy_ctrl->data_post_delay + 4;
> +
> +	phy_ctrl->clk_post_delay = phy_ctrl->data_t_hs_trial + DIV_ROUND_UP(clk_post, unit_tx_byte_clk_hs);
> +	phy_ctrl->data_pre_delay = clk_pre_delay_reality + phy_ctrl->clk_t_lpx +
> +		phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + DIV_ROUND_UP(clk_pre, unit_tx_byte_clk_hs);
> +
> +	clk_post_delay_reality = phy_ctrl->clk_post_delay + 4;
> +	data_pre_delay_reality = phy_ctrl->data_pre_delay + 2;
> +
> +	phy_ctrl->clk_lane_lp2hs_time = clk_pre_delay_reality + phy_ctrl->clk_t_lpx +
> +		phy_ctrl->clk_t_hs_prepare + clk_t_hs_zero_reality + 3;
> +	phy_ctrl->clk_lane_hs2lp_time = clk_post_delay_reality + phy_ctrl->clk_t_hs_trial + 3;
> +	phy_ctrl->data_lane_lp2hs_time = data_pre_delay_reality + phy_ctrl->data_t_lpx +
> +		phy_ctrl->data_t_hs_prepare + data_t_hs_zero_reality + 3;
> +	phy_ctrl->data_lane_hs2lp_time = data_post_delay_reality + phy_ctrl->data_t_hs_trial + 3;
> +	phy_ctrl->phy_stop_wait_time = clk_post_delay_reality +
> +		phy_ctrl->clk_t_hs_trial + DIV_ROUND_UP(clk_t_hs_exit, unit_tx_byte_clk_hs) -
> +		(data_post_delay_reality + phy_ctrl->data_t_hs_trial) + 3;
> +
> +	phy_ctrl->lane_byte_clk = lane_clock / 8;
> +	phy_ctrl->clk_division = (((phy_ctrl->lane_byte_clk / 2) % mipi->max_tx_esc_clk) > 0) ?
> +		(phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk + 1) :
> +		(phy_ctrl->lane_byte_clk / 2 / mipi->max_tx_esc_clk);
> +
> +	DRM_DEBUG("PHY clock_lane and data_lane config :\n"
> +		"rg_vrefsel_vcm=%u\n"
> +		"clk_pre_delay=%u\n"
> +		"clk_post_delay=%u\n"
> +		"clk_t_hs_prepare=%u\n"
> +		"clk_t_lpx=%u\n"
> +		"clk_t_hs_zero=%u\n"
> +		"clk_t_hs_trial=%u\n"
> +		"data_pre_delay=%u\n"
> +		"data_post_delay=%u\n"
> +		"data_t_hs_prepare=%u\n"
> +		"data_t_lpx=%u\n"
> +		"data_t_hs_zero=%u\n"
> +		"data_t_hs_trial=%u\n"
> +		"data_t_ta_go=%u\n"
> +		"data_t_ta_get=%u\n",
> +		phy_ctrl->rg_vrefsel_vcm,
> +		phy_ctrl->clk_pre_delay,
> +		phy_ctrl->clk_post_delay,
> +		phy_ctrl->clk_t_hs_prepare,
> +		phy_ctrl->clk_t_lpx,
> +		phy_ctrl->clk_t_hs_zero,
> +		phy_ctrl->clk_t_hs_trial,
> +		phy_ctrl->data_pre_delay,
> +		phy_ctrl->data_post_delay,
> +		phy_ctrl->data_t_hs_prepare,
> +		phy_ctrl->data_t_lpx,
> +		phy_ctrl->data_t_hs_zero,
> +		phy_ctrl->data_t_hs_trial,
> +		phy_ctrl->data_t_ta_go,
> +		phy_ctrl->data_t_ta_get);
> +	DRM_DEBUG("clk_lane_lp2hs_time=%u\n"
> +		"clk_lane_hs2lp_time=%u\n"
> +		"data_lane_lp2hs_time=%u\n"
> +		"data_lane_hs2lp_time=%u\n"
> +		"phy_stop_wait_time=%u\n",
> +		phy_ctrl->clk_lane_lp2hs_time,
> +		phy_ctrl->clk_lane_hs2lp_time,
> +		phy_ctrl->data_lane_lp2hs_time,
> +		phy_ctrl->data_lane_hs2lp_time,
> +		phy_ctrl->phy_stop_wait_time);
> +}
> +
> +static void dw_dsi_set_mode(struct dw_dsi *dsi, enum dsi_work_mode mode)
> +{
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	void __iomem *base = ctx->base;
> +
> +	writel(RESET, base + PWR_UP);
> +	writel(mode, base + MODE_CFG);
> +	writel(POWERUP, base + PWR_UP);
> +}
> +
> +static void dsi_set_burst_mode(void __iomem *base, unsigned long burst_flags)
> +{
> +	unsigned long flags;
> +	u32 val;
> +
> +	flags = burst_flags;
> +	flags &= MIPI_DSI_MODE_VIDEO |
> +		 MIPI_DSI_MODE_VIDEO_BURST |
> +		 MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
> +
> +	if (!(flags & MIPI_DSI_MODE_VIDEO)) {
> +		DRM_WARN("MIPI_DSI_MODE_VIDEO was not set! Using DSI_NON_BURST_SYNC_PULSES");
> +		val = DSI_NON_BURST_SYNC_PULSES;
> +	} else if (flags & MIPI_DSI_MODE_VIDEO_BURST) {
> +		val = DSI_BURST_SYNC_PULSES_1;
> +	} else if (flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
> +		val = DSI_NON_BURST_SYNC_PULSES;
> +	} else {
> +		val = DSI_NON_BURST_SYNC_EVENTS;
> +	}
> +
> +	DRM_INFO("burst_mode = 0x%x (flags: 0x%04lx)", val, burst_flags);
> +	set_reg(base + MIPIDSI_VID_MODE_CFG_OFFSET, val, 2, 0);
> +}
> +
> +/*
> + * dsi phy reg write function
> + */
> +static void dsi_phy_tst_set(void __iomem *base, u32 reg, u32 val)
> +{
> +	u32 reg_write = 0x10000 + reg;
> +
> +	/*
> +	 * latch reg first
> +	 */
> +	writel(reg_write, base + MIPIDSI_PHY_TST_CTRL1_OFFSET);
> +	writel(0x02, base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +	writel(0x00, base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +
> +	/*
> +	 * then latch value
> +	 */
> +	writel(val, base + MIPIDSI_PHY_TST_CTRL1_OFFSET);
> +	writel(0x02, base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +	writel(0x00, base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +}
> +
> +static void mipi_config_dphy_spec1v2_parameter(struct dw_dsi *dsi,
> +					       char __iomem *mipi_dsi_base,
> +					       u32 id)
> +{
> +	u32 i;
> +	u32 addr = 0;
> +	u32 lanes;
> +
> +	lanes =  dsi->client[id].lanes - 1;
> +	for (i = 0; i <= (lanes + 1); i++) {
> +		/* Lane Transmission Property */
> +		addr = MIPIDSI_PHY_TST_LANE_TRANSMISSION_PROPERTY + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, 0x43);
> +	}
> +
> +	/* pre_delay of clock lane request setting */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_PRE_DELAY, DSS_REDUCE(dsi->phy.clk_pre_delay));
> +
> +	/* post_delay of clock lane request setting */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_POST_DELAY, DSS_REDUCE(dsi->phy.clk_post_delay));
> +
> +	/* clock lane timing ctrl - t_lpx */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_TLPX, DSS_REDUCE(dsi->phy.clk_t_lpx));
> +
> +	/* clock lane timing ctrl - t_hs_prepare */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_PREPARE, DSS_REDUCE(dsi->phy.clk_t_hs_prepare));
> +
> +	/* clock lane timing ctrl - t_hs_zero */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_ZERO, DSS_REDUCE(dsi->phy.clk_t_hs_zero));
> +
> +	/* clock lane timing ctrl - t_hs_trial */
> +	dsi_phy_tst_set(mipi_dsi_base, MIPIDSI_PHY_TST_CLK_TRAIL, DSS_REDUCE(dsi->phy.clk_t_hs_trial));
> +
> +	for (i = 0; i <= 4; i++) {
> +		if (lanes == 2 && i == 1) /* init mipi dsi 3 lanes should skip lane3 */
> +			i++;
> +
> +		if (i == 2) /* skip clock lane */
> +			i++;  /* addr: lane0:0x60; lane1:0x80; lane2:0xC0; lane3:0xE0 */
> +
> +		/* data lane pre_delay */
> +		addr = MIPIDSI_PHY_TST_DATA_PRE_DELAY + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_pre_delay));
> +
> +		/* data lane post_delay */
> +		addr = MIPIDSI_PHY_TST_DATA_POST_DELAY + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_post_delay));
> +
> +		/* data lane timing ctrl - t_lpx */
> +		addr = MIPIDSI_PHY_TST_DATA_TLPX + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_t_lpx));
> +
> +		/* data lane timing ctrl - t_hs_prepare */
> +		addr = MIPIDSI_PHY_TST_DATA_PREPARE + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_t_hs_prepare));
> +
> +		/* data lane timing ctrl - t_hs_zero */
> +		addr = MIPIDSI_PHY_TST_DATA_ZERO + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_t_hs_zero));
> +
> +		/* data lane timing ctrl - t_hs_trial */
> +		addr = MIPIDSI_PHY_TST_DATA_TRAIL + (i << 5);
> +		dsi_phy_tst_set(mipi_dsi_base, addr, DSS_REDUCE(dsi->phy.data_t_hs_trial));
> +
> +		DRM_DEBUG("DPHY spec1v2 config :\n"
> +			"addr=0x%x\n"
> +			"clk_pre_delay=%u\n"
> +			"clk_t_hs_trial=%u\n"
> +			"data_t_hs_zero=%u\n"
> +			"data_t_lpx=%u\n"
> +			"data_t_hs_prepare=%u\n",
> +			addr,
> +			dsi->phy.clk_pre_delay,
> +			dsi->phy.clk_t_hs_trial,
> +			dsi->phy.data_t_hs_zero,
> +			dsi->phy.data_t_lpx,
> +			dsi->phy.data_t_hs_prepare);
> +	}
> +}
> +
> +static void dsi_mipi_init(struct dw_dsi *dsi, char __iomem *mipi_dsi_base,
> +			  u32 id)
> +{
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	u32 hline_time = 0;
> +	u32 hsa_time = 0;
> +	u32 hbp_time = 0;
> +	u64 pixel_clk = 0;
> +	unsigned long dw_jiffies = 0;
> +	u32 tmp = 0;
> +	bool is_ready = false;
> +	struct mipi_panel_info *mipi = NULL;
> +	struct dss_rect rect;
> +	u32 cmp_stopstate_val = 0;
> +	u32 lanes;
> +
> +	WARN_ON(!dsi);
> +	WARN_ON(!mipi_dsi_base);
> +
> +	DRM_INFO("%s: id=%d\n", __func__, id);
> +
> +	mipi = &dsi->mipi;
> +
> +	if (mipi->max_tx_esc_clk == 0) {
> +		DRM_INFO("max_tx_esc_clk is invalid!");
> +		mipi->max_tx_esc_clk = DEFAULT_MAX_TX_ESC_CLK;
> +	}
> +
> +	memset(&dsi->phy, 0, sizeof(struct mipi_phy_params));
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)
> +		kirin970_get_dsi_dphy_ctrl(dsi, &dsi->phy, id);
> +	else
> +		kirin960_get_dsi_phy_ctrl(dsi, &dsi->phy, id);
> +
> +	rect.x = 0;
> +	rect.y = 0;
> +	rect.w = dsi->cur_mode.hdisplay;
> +	rect.h = dsi->cur_mode.vdisplay;
> +	lanes = dsi->client[id].lanes - 1;
> +	/***************Configure the DPHY start**************/
> +
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, lanes, 2, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, dsi->phy.clk_division, 8, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, dsi->phy.clk_division, 8, 8);
> +
> +	writel(0x00000000, mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET);
> +
> +	writel(0x00000000, mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +	writel(0x00000001, mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +	writel(0x00000000, mipi_dsi_base + MIPIDSI_PHY_TST_CTRL0_OFFSET);
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0042, 0x21);
> +		/* PLL configuration I */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0046,
> +				dsi->phy.rg_cp + (dsi->phy.rg_lpf_r << 4));
> +
> +		/* PLL configuration II */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0048,
> +				dsi->phy.rg_0p8v + (dsi->phy.rg_2p5g << 1) +
> +				(dsi->phy.rg_320m << 2) + (dsi->phy.rg_band_sel << 3));
> +
> +		/* PLL configuration III */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0049, dsi->phy.rg_pre_div);
> +
> +		/* PLL configuration IV */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x004A, dsi->phy.rg_div);
> +
> +		dsi_phy_tst_set(mipi_dsi_base, 0x004F, 0xf0);
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0050, 0xc0);
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0051, 0x22);
> +
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0053, dsi->phy.rg_vrefsel_vcm);
> +
> +		/* enable BTA */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x0054, 0x03);
> +
> +		/* PLL update control */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x004B, 0x1);
> +
> +		/* set dphy spec parameter */
> +		mipi_config_dphy_spec1v2_parameter(dsi, mipi_dsi_base, id);
> +	} else {
> +		int i = 0;
> +
> +		/* physical configuration PLL I */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x14,
> +				(dsi->phy.rg_pll_fbd_s << 4) + (dsi->phy.rg_pll_enswc << 3) +
> +				(dsi->phy.rg_pll_enbwt << 2) + dsi->phy.rg_pll_chp);
> +
> +		/* physical configuration PLL II, M */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x15, dsi->phy.rg_pll_fbd_p);
> +
> +		/* physical configuration PLL III */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x16,
> +				(dsi->phy.rg_pll_cp << 5) + (dsi->phy.rg_pll_lpf_cs << 4) +
> +				dsi->phy.rg_pll_refsel);
> +
> +		/* physical configuration PLL IV, N */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x17, dsi->phy.rg_pll_pre_p);
> +
> +		/* sets the analog characteristic of V reference in D-PHY TX */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x1D, dsi->phy.rg_vrefsel_vcm);
> +
> +		/* MISC AFE Configuration */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x1E,
> +				(dsi->phy.rg_pll_cp_p << 5) + (dsi->phy.reload_sel << 4) +
> +				(dsi->phy.rg_phase_gen_en << 3) + (dsi->phy.rg_band_sel << 2) +
> +				(dsi->phy.pll_power_down << 1) + dsi->phy.pll_register_override);
> +
> +		/* reload_command */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x1F, dsi->phy.load_command);
> +
> +		/* pre_delay of clock lane request setting */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x20, DSS_REDUCE(dsi->phy.clk_pre_delay));
> +
> +		/* post_delay of clock lane request setting */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x21, DSS_REDUCE(dsi->phy.clk_post_delay));
> +
> +		/* clock lane timing ctrl - t_lpx */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x22, DSS_REDUCE(dsi->phy.clk_t_lpx));
> +
> +		/* clock lane timing ctrl - t_hs_prepare */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x23, DSS_REDUCE(dsi->phy.clk_t_hs_prepare));
> +
> +		/* clock lane timing ctrl - t_hs_zero */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x24, DSS_REDUCE(dsi->phy.clk_t_hs_zero));
> +
> +		/* clock lane timing ctrl - t_hs_trial */
> +		dsi_phy_tst_set(mipi_dsi_base, 0x25, dsi->phy.clk_t_hs_trial);
> +
> +		for (i = 0; i <= lanes; i++) {
> +			/* data lane pre_delay */
> +			tmp = 0x30 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_pre_delay));
> +
> +			/* data lane post_delay */
> +			tmp = 0x31 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_post_delay));
> +
> +			/* data lane timing ctrl - t_lpx */
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_lpx));
> +
> +			/* data lane timing ctrl - t_hs_prepare */
> +			tmp = 0x33 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_prepare));
> +
> +			/* data lane timing ctrl - t_hs_zero */
> +			tmp = 0x34 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_zero));
> +
> +			/* data lane timing ctrl - t_hs_trial */
> +			tmp = 0x35 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_hs_trial));
> +
> +			/* data lane timing ctrl - t_ta_go */
> +			tmp = 0x36 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_ta_go));
> +
> +			/* data lane timing ctrl - t_ta_get */
> +			tmp = 0x37 + (i << 4);
> +			dsi_phy_tst_set(mipi_dsi_base, tmp, DSS_REDUCE(dsi->phy.data_t_ta_get));
> +		}
> +	}
> +
> +	writel(0x00000007, mipi_dsi_base + MIPIDSI_PHY_RSTZ_OFFSET);
> +
> +	is_ready = false;
> +	dw_jiffies = jiffies + HZ / 2;
> +	do {
> +		tmp = readl(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET);
> +		if ((tmp & 0x00000001) == 0x00000001) {
> +			is_ready = true;
> +			break;
> +		}
> +	} while (time_after(dw_jiffies, jiffies));
Use jiffies + msecs_to_jiffies(xxx) - and drop HZ /2.
Same goes for other similar HZ uses.

> +
> +	if (!is_ready) {
> +		DRM_INFO("phylock is not ready!MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n",
> +			 tmp);
> +	}
> +
> +	if (lanes >= DSI_4_LANES)
> +		cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9) | BIT(11));
> +	else if (lanes >= DSI_3_LANES)
> +		cmp_stopstate_val = (BIT(4) | BIT(7) | BIT(9));
> +	else if (lanes >= DSI_2_LANES)
> +		cmp_stopstate_val = (BIT(4) | BIT(7));
> +	else
> +		cmp_stopstate_val = (BIT(4));
> +
> +	is_ready = false;
> +	dw_jiffies = jiffies + HZ / 2;
> +	do {
> +		tmp = readl(mipi_dsi_base + MIPIDSI_PHY_STATUS_OFFSET);
> +		if ((tmp & cmp_stopstate_val) == cmp_stopstate_val) {
> +			is_ready = true;
> +			break;
> +		}
> +	} while (time_after(dw_jiffies, jiffies));
> +
> +	if (!is_ready) {
> +		DRM_INFO("phystopstateclklane is not ready! MIPIDSI_PHY_STATUS_OFFSET=0x%x.\n",
> +			 tmp);
> +	}
> +
> +	/*************************Configure the DPHY end*************************/
> +
> +	/* phy_stop_wait_time */
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_IF_CFG_OFFSET, dsi->phy.phy_stop_wait_time, 8, 8);
> +
> +	/*--------------configuring the DPI packet transmission---------------- */
> +
> +	/*
> +	 * 2. Configure the DPI Interface:
> +	 * This defines how the DPI interface interacts with the controller.
> +	 */
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_VCID_OFFSET, mipi->vc, 2, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_COLOR_CODING_OFFSET, mipi->color_mode, 4, 0);
> +
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.data_en_plr, 1, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.vsync_plr, 1, 1);
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, dsi->ldi.hsync_plr, 1, 2);
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 3);
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_CFG_POL_OFFSET, 0x0, 1, 4);
> +
> +	/*
> +	 * 3. Select the Video Transmission Mode:
> +	 * This defines how the processor requires the video line to be
> +	 * transported through the DSI link.
> +	 */
> +	/* video mode: low power mode */
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x3f, 6, 8);
> +
> +	/* TODO: fix blank display bug when set backlight */
> +	set_reg(mipi_dsi_base + MIPIDSI_DPI_LP_CMD_TIM_OFFSET, 0x4, 8, 16);
> +	/* video mode: send read cmd by lp mode */
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_MODE_CFG_OFFSET, 0x1, 1, 15);
> +
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_PKT_SIZE_OFFSET, rect.w, 14, 0);
> +
> +	/* burst mode */
> +	dsi_set_burst_mode(mipi_dsi_base, dsi->client[id].mode_flags);
> +	/* for dsi read, BTA enable */
> +	set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 2);
> +
> +	/*
> +	 * 4. Define the DPI Horizontal timing configuration:
> +	 *
> +	 * Hsa_time = HSA*(PCLK period/Clk Lane Byte Period);
> +	 * Hbp_time = HBP*(PCLK period/Clk Lane Byte Period);
> +	 * Hline_time = (HSA+HBP+HACT+HFP)*(PCLK period/Clk Lane Byte Period);
> +	 */
> +	pixel_clk = dsi->cur_mode.clock * 1000;
> +	/* htot = dsi->cur_mode.htotal; */
> +	/* vtot = dsi->cur_mode.vtotal; */
> +	dsi->ldi.h_front_porch = dsi->cur_mode.hsync_start - dsi->cur_mode.hdisplay;
> +	dsi->ldi.h_back_porch = dsi->cur_mode.htotal - dsi->cur_mode.hsync_end;
> +	dsi->ldi.h_pulse_width = dsi->cur_mode.hsync_end - dsi->cur_mode.hsync_start;
> +	dsi->ldi.v_front_porch = dsi->cur_mode.vsync_start - dsi->cur_mode.vdisplay;
> +	dsi->ldi.v_back_porch = dsi->cur_mode.vtotal - dsi->cur_mode.vsync_end;
> +	dsi->ldi.v_pulse_width = dsi->cur_mode.vsync_end - dsi->cur_mode.vsync_start;
> +	if (dsi->ldi.v_pulse_width > 15) {
> +		DRM_DEBUG_DRIVER("vsw exceeded 15\n");
> +		dsi->ldi.v_pulse_width = 15;
> +	}
> +	hsa_time = dsi->ldi.h_pulse_width * dsi->phy.lane_byte_clk / pixel_clk;
> +	hbp_time = dsi->ldi.h_back_porch * dsi->phy.lane_byte_clk / pixel_clk;
> +	hline_time = DIV_ROUND_UP((dsi->ldi.h_pulse_width + dsi->ldi.h_back_porch +
> +		rect.w + dsi->ldi.h_front_porch) * dsi->phy.lane_byte_clk, pixel_clk);
> +
> +	DRM_INFO("hsa_time=%d, hbp_time=%d, hline_time=%d\n",
> +		 hsa_time, hbp_time, hline_time);
> +	DRM_INFO("lane_byte_clk=%llu, pixel_clk=%llu\n",
> +		 dsi->phy.lane_byte_clk, pixel_clk);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_HSA_TIME_OFFSET, hsa_time, 12, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_HBP_TIME_OFFSET, hbp_time, 12, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_HLINE_TIME_OFFSET, hline_time, 15, 0);
> +
> +	/* Define the Vertical line configuration */
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_VSA_LINES_OFFSET,
> +		dsi->ldi.v_pulse_width, 10, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_VBP_LINES_OFFSET,
> +		dsi->ldi.v_back_porch, 10, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_VFP_LINES_OFFSET,
> +		dsi->ldi.v_front_porch, 10, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_VID_VACTIVE_LINES_OFFSET,
> +		rect.h, 14, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_TO_CNT_CFG_OFFSET,
> +		0x7FF, 16, 0);
> +
> +	/* Configure core's phy parameters */
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET,
> +		dsi->phy.clk_lane_lp2hs_time, 10, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_LPCLK_CFG_OFFSET,
> +		dsi->phy.clk_lane_hs2lp_time, 10, 16);
> +
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_RD_CFG_OFFSET,
> +		0x7FFF, 15, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET,
> +		dsi->phy.data_lane_lp2hs_time, 10, 0);
> +	set_reg(mipi_dsi_base + MIPIDSI_PHY_TMR_CFG_OFFSET,
> +		dsi->phy.data_lane_hs2lp_time, 10, 16);
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)  {
> +		/* 16~19bit:pclk_en, pclk_sel, dpipclk_en, dpipclk_sel */
> +		set_reg(mipi_dsi_base + MIPIDSI_CLKMGR_CFG_OFFSET, 0x5, 4, 16);
> +		/* 0:dphy */
> +		set_reg(mipi_dsi_base + KIRIN970_PHY_MODE, 0x0, 1, 0);
> +	}
> +
> +	/* Waking up Core */
> +	set_reg(mipi_dsi_base + MIPIDSI_PWR_UP_OFFSET, 0x1, 1, 0);
> +}
> +
> +static void dsi_encoder_disable(struct drm_encoder *encoder)
> +{
> +	struct dw_dsi *dsi = encoder_to_dsi(encoder);
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	void __iomem *base = ctx->base;
> +
> +	if (!dsi->enable)
> +		return;
> +
> +	dw_dsi_set_mode(dsi, DSI_COMMAND_MODE);
> +	/* turn off panel's backlight */
> +	if (dsi->panel && drm_panel_disable(dsi->panel))
> +		DRM_ERROR("failed to disable panel\n");
> +
> +	/* turn off panel */
> +	if (dsi->panel && drm_panel_unprepare(dsi->panel))
> +		DRM_ERROR("failed to unprepare panel\n");
> +
> +	writel(0, base + PWR_UP);
> +	writel(0, base + LPCLK_CTRL);
> +	writel(0, base + PHY_RSTZ);
> +	clk_disable_unprepare(ctx->dss_dphy0_ref_clk);
> +	clk_disable_unprepare(ctx->dss_dphy0_cfg_clk);
> +	clk_disable_unprepare(ctx->dss_pclk_dsi0_clk);
> +
> +	dsi->enable = false;
> +}
> +
> +static int mipi_dsi_on_sub1(struct dw_dsi *dsi, char __iomem *mipi_dsi_base,
> +			    u32 id)
> +{
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +
> +	WARN_ON(!mipi_dsi_base);
> +
> +	/* mipi init */
> +	dsi_mipi_init(dsi, mipi_dsi_base, id);
> +
> +	/* dsi memory init */
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)
> +		writel(0x02600008, mipi_dsi_base + KIRIN970_DSI_MEM_CTRL);
> +
> +	/* switch to cmd mode */
> +	set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x1, 1, 0);
> +	/* cmd mode: low power mode */
> +	set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x7f, 7, 8);
> +	set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0xf, 4, 16);
> +	set_reg(mipi_dsi_base + MIPIDSI_CMD_MODE_CFG_OFFSET, 0x1, 1, 24);
> +	/* disable generate High Speed clock */
> +	/* delete? */
> +	set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x0, 1, 0);
> +
> +	return 0;
> +}
> +
> +static int mipi_dsi_on_sub2(struct dw_dsi *dsi, char __iomem *mipi_dsi_base)
> +{
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +
> +	u64 pctrl_dphytx_stopcnt = 0;
> +
> +	WARN_ON(!mipi_dsi_base);
> +
> +	/* switch to video mode */
> +	set_reg(mipi_dsi_base + MIPIDSI_MODE_CFG_OFFSET, 0x0, 1, 0);
> +
> +	/* enable EOTP TX */
> +	set_reg(mipi_dsi_base + MIPIDSI_PCKHDL_CFG_OFFSET, 0x1, 1, 0);
> +
> +	/* enable generate High Speed clock, continue clock */
> +	set_reg(mipi_dsi_base + MIPIDSI_LPCLK_CTRL_OFFSET, 0x1, 2, 0);
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		/* init: wait DPHY 4 data lane stopstate */
> +		pctrl_dphytx_stopcnt = (u64)(dsi->ldi.h_back_porch +
> +			dsi->ldi.h_front_porch + dsi->ldi.h_pulse_width + dsi->cur_mode.hdisplay + 5) *
> +			DEFAULT_PCLK_PCTRL_RATE / (dsi->cur_mode.clock * 1000);
> +		DRM_DEBUG("pctrl_dphytx_stopcnt = %llu\n", pctrl_dphytx_stopcnt);
> +
> +		/* FIXME: */
> +		writel((u32)pctrl_dphytx_stopcnt, dsi->ctx->pctrl_base + PERI_CTRL29);
> +	}
> +
> +	return 0;
> +}
> +
> +static void dsi_encoder_enable(struct drm_encoder *encoder)
> +{
> +	struct dw_dsi *dsi = encoder_to_dsi(encoder);
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	int ret;
> +
> +	if (dsi->enable)
> +		return;
> +
> +	ret = clk_prepare_enable(ctx->dss_dphy0_ref_clk);
> +	if (ret) {
> +		DRM_ERROR("fail to enable dss_dphy0_ref_clk: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = clk_prepare_enable(ctx->dss_dphy0_cfg_clk);
> +	if (ret) {
> +		DRM_ERROR("fail to enable dss_dphy0_cfg_clk: %d\n", ret);
> +		return;
> +	}
> +
> +	ret = clk_prepare_enable(ctx->dss_pclk_dsi0_clk);
> +	if (ret) {
> +		DRM_ERROR("fail to enable dss_pclk_dsi0_clk: %d\n", ret);
> +		return;
> +	}
> +
> +	mipi_dsi_on_sub1(dsi, ctx->base, dsi->attached_client);
> +
> +	mipi_dsi_on_sub2(dsi, ctx->base);
> +
> +	/* turn on panel */
> +	if (dsi->panel && drm_panel_prepare(dsi->panel))
> +		DRM_ERROR("failed to prepare panel\n");
> +
> +	/* dw_dsi_set_mode(dsi, DSI_VIDEO_MODE); */
> +
> +	/* turn on panel's back light */
> +	if (dsi->panel && drm_panel_enable(dsi->panel))
> +		DRM_ERROR("failed to enable panel\n");
> +
> +	dsi->enable = true;
> +}
> +
> +static enum drm_mode_status dsi_encoder_mode_valid(struct drm_encoder *encoder,
> +						   const struct drm_display_mode *mode)
> +
> +{
> +	const struct drm_crtc_helper_funcs *crtc_funcs;
> +	struct drm_display_mode adj_mode;
> +	int clock = mode->clock;
> +	struct drm_crtc *crtc;
> +
> +	drm_for_each_crtc(crtc, encoder->dev) {
> +		drm_mode_copy(&adj_mode, mode);
> +
> +		crtc_funcs = crtc->helper_private;
> +		if (crtc_funcs && crtc_funcs->mode_fixup) {
> +			if (!crtc_funcs->mode_fixup(crtc, mode, &adj_mode)) {
> +				DRM_INFO("Discarded mode: %ix%i@%i, clock: %i (adjusted to %i)",
> +					 mode->hdisplay, mode->vdisplay,
> +					 drm_mode_vrefresh(mode),
> +					 mode->clock, clock);
> +
> +				return MODE_BAD;
> +			}
> +			clock = adj_mode.clock;
> +		}
> +	}
> +
> +	DRM_INFO("Valid mode: %ix%i@%i, clock %i (adjusted to %i)",
> +		 mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
> +		 mode->clock, clock);
> +
> +	return MODE_OK;
> +}
> +
> +static void dsi_encoder_mode_set(struct drm_encoder *encoder,
> +				 struct drm_display_mode *mode,
> +				 struct drm_display_mode *adj_mode)
> +{
> +	struct dw_dsi *dsi = encoder_to_dsi(encoder);
> +
> +	drm_mode_copy(&dsi->cur_mode, adj_mode);
> +}
> +
> +static int dsi_encoder_atomic_check(struct drm_encoder *encoder,
> +				    struct drm_crtc_state *crtc_state,
> +				    struct drm_connector_state *conn_state)
> +{
> +	/* do nothing */
> +	return 0;
> +}
> +
> +static const struct drm_encoder_helper_funcs dw_encoder_helper_funcs = {
> +	.atomic_check	= dsi_encoder_atomic_check,
> +	.mode_valid	= dsi_encoder_mode_valid,
> +	.mode_set	= dsi_encoder_mode_set,
> +	.enable		= dsi_encoder_enable,
> +	.disable	= dsi_encoder_disable
> +};
> +
> +static const struct drm_encoder_funcs dw_encoder_funcs = {
> +	.destroy = drm_encoder_cleanup,
> +};
> +
> +static int dw_drm_encoder_init(struct device *dev,
> +			       struct drm_device *drm_dev,
> +			       struct drm_encoder *encoder,
> +			       struct drm_bridge *bridge)
> +{
> +	int ret;
> +	u32 crtc_mask;
> +
> +	drm_info(drm_dev, "%s:\n", __func__);
> +
> +	/* Link drm_bridge to encoder */
> +	if (!bridge) {
> +		drm_info(drm_dev, "no dsi bridge to attach the encoder\n");
> +		return 0;
> +	}
> +
> +	crtc_mask = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
> +	if (!crtc_mask) {
> +		DRM_ERROR("failed to find crtc mask\n");
> +		return -EINVAL;
> +	}
> +
> +	drm_info(drm_dev, "Initializing CRTC encoder: %d\n",
> +		 crtc_mask);
> +
> +	encoder->possible_crtcs = crtc_mask;
> +	encoder->possible_clones = 0;
> +	ret = drm_encoder_init(drm_dev, encoder, &dw_encoder_funcs,
> +			       DRM_MODE_ENCODER_DSI, NULL);
> +	if (ret) {
> +		drm_info(drm_dev, "failed to init dsi encoder\n");
> +		return ret;
> +	}
> +
> +	drm_encoder_helper_add(encoder, &dw_encoder_helper_funcs);
> +
> +	/* associate the bridge to dsi encoder */
> +	ret = drm_bridge_attach(encoder, bridge, NULL, 0);
> +	if (ret) {
> +		drm_info(drm_dev, "failed to attach external bridge\n");
> +		drm_encoder_cleanup(encoder);
> +	}
> +
> +	return ret;
> +}
All the encoder stuff can, I think, be repalced by the simple encoder.
See how other drivers uses drm_simple_encoder_init()

> +
> +static int dsi_host_attach(struct mipi_dsi_host *host,
> +			   struct mipi_dsi_device *mdsi)
> +{
> +	struct dw_dsi *dsi = host_to_dsi(host);
> +	u32 id = mdsi->channel >= 1 ? OUT_PANEL : OUT_HDMI;
> +
> +	if (mdsi->lanes < 1 || mdsi->lanes > 4) {
> +		DRM_ERROR("dsi device params invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	dsi->client[id].lanes = mdsi->lanes;
> +	dsi->client[id].format = mdsi->format;
> +	dsi->client[id].mode_flags = mdsi->mode_flags;
> +	dsi->client[id].phy_clock = 0;
> +
> +	dsi->attached_client = id;
> +
> +	DRM_DEBUG("host attach, client name=[%s], id=%d\n", mdsi->name, id);
> +
> +	return 0;
> +}
> +
> +static int dsi_host_detach(struct mipi_dsi_host *host,
> +			   struct mipi_dsi_device *mdsi)
> +{
> +	/* do nothing */
> +	return 0;
> +}
> +
> +static int dsi_gen_pkt_hdr_write(void __iomem *base, u32 val)
> +{
> +	u32 status;
> +	int ret;
> +
> +	ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS, status,
> +				 !(status & GEN_CMD_FULL), 1000,
> +				 CMD_PKT_STATUS_TIMEOUT_US);
> +	if (ret < 0) {
> +		DRM_ERROR("failed to get available command FIFO\n");
> +		return ret;
> +	}
> +
> +	writel(val, base + GEN_HDR);
> +
> +	ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS, status,
> +				 status & (GEN_CMD_EMPTY | GEN_PLD_W_EMPTY),
> +				 1000, CMD_PKT_STATUS_TIMEOUT_US);
> +	if (ret < 0) {
> +		DRM_ERROR("failed to write command FIFO\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dsi_dcs_short_write(void __iomem *base,
> +			       const struct mipi_dsi_msg *msg)
> +{
> +	const u16 *tx_buf = msg->tx_buf;
> +	u32 val = GEN_HDATA(*tx_buf) | GEN_HTYPE(msg->type);
> +
> +	if (msg->tx_len > 2) {
> +		DRM_ERROR("too long tx buf length %zu for short write\n",
> +			  msg->tx_len);
> +		return -EINVAL;
> +	}
> +
> +	return dsi_gen_pkt_hdr_write(base, val);
> +}
> +
> +static int dsi_dcs_long_write(void __iomem *base,
> +			      const struct mipi_dsi_msg *msg)
> +{
> +	const u32 *tx_buf = msg->tx_buf;
> +	int len = msg->tx_len, pld_data_bytes = sizeof(*tx_buf), ret;
> +	u32 val = GEN_HDATA(msg->tx_len) | GEN_HTYPE(msg->type);
> +	u32 remainder = 0;
> +	u32 status;
> +
> +	if (msg->tx_len < 3) {
> +		DRM_ERROR("wrong tx buf length %zu for long write\n",
> +			  msg->tx_len);
> +		return -EINVAL;
> +	}
> +
> +	while (DIV_ROUND_UP(len, pld_data_bytes)) {
> +		if (len < pld_data_bytes) {
> +			memcpy(&remainder, tx_buf, len);
> +			writel(remainder, base + GEN_PLD_DATA);
> +			len = 0;
> +		} else {
> +			writel(*tx_buf, base + GEN_PLD_DATA);
> +			tx_buf++;
> +			len -= pld_data_bytes;
> +		}
> +
> +		ret = readx_poll_timeout(readl, base + CMD_PKT_STATUS,
> +					 status, !(status & GEN_PLD_W_FULL), 1000,
> +					 CMD_PKT_STATUS_TIMEOUT_US);
> +		if (ret < 0) {
> +			DRM_ERROR("failed to get available write payload FIFO\n");
> +			return ret;
> +		}
> +	}
> +
> +	return dsi_gen_pkt_hdr_write(base, val);
> +}
> +
> +static ssize_t dsi_host_transfer(struct mipi_dsi_host *host,
> +				 const struct mipi_dsi_msg *msg)
> +{
> +	struct dw_dsi *dsi = host_to_dsi(host);
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	void __iomem *base = ctx->base;
> +	int ret;
> +
> +	switch (msg->type) {
> +	case MIPI_DSI_DCS_SHORT_WRITE:
> +	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
> +	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
> +		ret = dsi_dcs_short_write(base, msg);
> +		break;
> +	case MIPI_DSI_DCS_LONG_WRITE:
> +		ret = dsi_dcs_long_write(base, msg);
> +		break;
> +	default:
> +		DRM_ERROR("unsupported message type\n");
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static const struct mipi_dsi_host_ops dsi_host_ops = {
> +	.attach = dsi_host_attach,
> +	.detach = dsi_host_detach,
> +	.transfer = dsi_host_transfer,
> +};
> +
> +static int dsi_host_init(struct device *dev, struct dw_dsi *dsi)
> +{
> +	struct mipi_dsi_host *host = &dsi->host;
> +	struct mipi_panel_info *mipi = &dsi->mipi;
> +	int ret;
> +
> +	host->dev = dev;
> +	host->ops = &dsi_host_ops;
> +
> +	mipi->max_tx_esc_clk = 10 * 1000000UL;
> +	mipi->vc = 0;
> +	mipi->color_mode = DSI_24BITS_1;
> +	mipi->clk_post_adjust = 120;
> +	mipi->clk_pre_adjust = 0;
> +	mipi->clk_t_hs_prepare_adjust = 0;
> +	mipi->clk_t_lpx_adjust = 0;
> +	mipi->clk_t_hs_trial_adjust = 0;
> +	mipi->clk_t_hs_exit_adjust = 0;
> +	mipi->clk_t_hs_zero_adjust = 0;
> +
> +	dsi->ldi.data_en_plr = 0;
> +	dsi->ldi.vsync_plr = 0;
> +	dsi->ldi.hsync_plr = 0;
> +
> +	ret = mipi_dsi_host_register(host);
> +	if (ret) {
> +		dev_info(dev, "failed to register dsi host\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dsi_connector_get_modes(struct drm_connector *connector)
> +{
> +	struct dw_dsi *dsi = connector_to_dsi(connector);
> +
> +	return drm_panel_get_modes(dsi->panel, connector);
> +}
> +
> +static enum drm_mode_status
> +dsi_connector_mode_valid(struct drm_connector *connector,
> +			 struct drm_display_mode *mode)
> +{
> +	enum drm_mode_status mode_status = MODE_OK;
> +
> +	return mode_status;
> +}
> +
> +static struct drm_encoder *
> +dsi_connector_best_encoder(struct drm_connector *connector)
> +{
> +	struct dw_dsi *dsi = connector_to_dsi(connector);
> +
> +	return &dsi->encoder;
> +}
> +
> +static const struct drm_connector_helper_funcs dsi_connector_helper_funcs = {
> +	.get_modes = dsi_connector_get_modes,
> +	.mode_valid = dsi_connector_mode_valid,
> +	.best_encoder = dsi_connector_best_encoder,
> +};
> +
> +static enum drm_connector_status
> +dsi_connector_detect(struct drm_connector *connector, bool force)
> +{
> +	struct dw_dsi *dsi = connector_to_dsi(connector);
> +	enum drm_connector_status status;
> +
> +	status = dsi->cur_client == OUT_PANEL ?	connector_status_connected :
> +		connector_status_disconnected;
> +
> +	return status;
> +}
> +
> +static void dsi_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +static struct drm_connector_funcs dsi_atomic_connector_funcs = {
> +	.fill_modes = drm_helper_probe_single_connector_modes,
> +	.detect = dsi_connector_detect,
> +	.destroy = dsi_connector_destroy,
> +	.reset = drm_atomic_helper_connector_reset,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int dsi_connector_init(struct drm_device *dev, struct dw_dsi *dsi)
> +{
> +	struct drm_encoder *encoder = &dsi->encoder;
> +	struct drm_connector *connector = &dsi->connector;
> +	int ret;
> +
> +	connector->polled = DRM_CONNECTOR_POLL_HPD;
> +	drm_connector_helper_add(connector,
> +				 &dsi_connector_helper_funcs);
> +
> +	ret = drm_connector_init(dev, &dsi->connector,
> +				 &dsi_atomic_connector_funcs,
> +				 DRM_MODE_CONNECTOR_DSI);
> +	if (ret)
> +		return ret;
> +
> +	drm_dbg(dev, "Attaching CRTC encoder\n");
> +	ret = drm_connector_attach_encoder(connector, encoder);
> +	if (ret)
> +		return ret;
> +
> +	ret = drm_panel_attach(dsi->panel, connector);
> +	if (ret)
> +		return ret;
> +
> +	drm_connector_register(&dsi->connector);
> +
> +	drm_dbg(dev, "connector init\n");
> +	return 0;
> +}
> +
> +static int dsi_bind(struct device *dev, struct device *master, void *data)
> +{
> +	struct dsi_data *ddata = dev_get_drvdata(dev);
> +	struct dw_dsi *dsi = &ddata->dsi;
> +	struct drm_device *drm_dev = data;
> +	int ret;
> +
> +	ret = dw_drm_encoder_init(dev, drm_dev, &dsi->encoder,
> +				  dsi->bridge);
> +	if (ret)
> +		return ret;
> +
> +	if (dsi->panel) {
> +		ret = dsi_connector_init(drm_dev, dsi);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void dsi_unbind(struct device *dev, struct device *master, void *data)
> +{
> +	/* do nothing */
> +}
> +
> +static const struct component_ops dsi_ops = {
> +	.bind	= dsi_bind,
> +	.unbind	= dsi_unbind,
> +};
> +
> +static int dsi_parse_bridge_endpoint(struct dw_dsi *dsi,
> +				     struct device_node *endpoint)
> +{
> +	struct device_node *bridge_node;
> +	struct drm_bridge *bridge;
> +
> +	bridge_node = of_graph_get_remote_port_parent(endpoint);
> +	if (!bridge_node) {
> +		DRM_ERROR("no valid bridge node\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(bridge_node);
> +
> +	bridge = of_drm_find_bridge(bridge_node);
> +	if (!bridge) {
> +		DRM_INFO("wait for external HDMI bridge driver.\n");
> +		return -EPROBE_DEFER;
> +	}
> +	dsi->bridge = bridge;
> +
> +	return 0;
> +}
> +
> +static int dsi_parse_panel_endpoint(struct dw_dsi *dsi,
> +				    struct device_node *endpoint)
> +{
> +	struct device_node *panel_node;
> +	struct drm_panel *panel;
> +
> +	panel_node = of_graph_get_remote_port_parent(endpoint);
> +	if (!panel_node) {
> +		DRM_ERROR("no valid panel node\n");
> +		return -ENODEV;
> +	}
> +	of_node_put(panel_node);
> +
> +	panel = of_drm_find_panel(panel_node);
> +	if (!panel) {
> +		DRM_DEBUG_DRIVER("skip this panel endpoint.\n");
> +		return 0;
> +	}
> +	dsi->panel = panel;
> +
> +	return 0;
> +}
> +
> +static int dsi_parse_endpoint(struct dw_dsi *dsi,
> +			      struct device_node *np,
> +			      enum dsi_output_client client)
> +{
> +	struct device_node *ep_node;
> +	struct of_endpoint ep;
> +	int ret = 0;
> +
> +	if (client == OUT_MAX)
> +		return -EINVAL;
> +
> +	for_each_endpoint_of_node(np, ep_node) {
> +		ret = of_graph_parse_endpoint(ep_node, &ep);
> +		if (ret) {
> +			of_node_put(ep_node);
> +			return ret;
> +		}
> +
> +		/* skip dsi input port, port == 0 is input port */
> +		if (ep.port == 0)
> +			continue;
> +
> +		/* parse bridge endpoint */
> +		if (client == OUT_HDMI) {
> +			if (ep.id == 0) {
> +				ret = dsi_parse_bridge_endpoint(dsi, ep_node);
> +				if (dsi->bridge)
> +					break;
> +			}
> +		} else { /* parse panel endpoint */
> +			if (ep.id > 0) {
> +				ret = dsi_parse_panel_endpoint(dsi, ep_node);
> +				if (dsi->panel)
> +					break;
> +			}
> +		}
> +
> +		if (ret) {
> +			of_node_put(ep_node);
> +			return ret;
> +		}
> +	}
> +
> +	if (!dsi->bridge && !dsi->panel) {
> +		DRM_ERROR("at least one bridge or panel node is required\n");
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi)
> +{
> +	struct dsi_hw_ctx *ctx = dsi->ctx;
> +	const char *compatible;
> +	int ret = 0;
> +	struct device_node *np = NULL;
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970)
> +		compatible = "hisilicon,kirin970-dsi";
> +	else
> +		compatible = "hisilicon,kirin960-dsi";
> +
> +	np = of_find_compatible_node(NULL, NULL, compatible);
> +	if (!np) {
> +		dev_err(&pdev->dev, "NOT FOUND device node %s!\n", compatible);
> +		return -ENXIO;
> +	}
> +
> +	ctx->base = of_iomap(np, 0);
> +	if (!(ctx->base)) {
> +		dev_err(&pdev->dev, "failed to get dsi base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	ctx->peri_crg_base = of_iomap(np, 1);
> +	if (!(ctx->peri_crg_base)) {
> +		dev_err(&pdev->dev, "failed to get peri_crg_base resource.\n");
> +		return -ENXIO;
> +	}
> +
> +	if (ctx->g_dss_version_tag == FB_ACCEL_KIRIN970) {
> +		ctx->pctrl_base = of_iomap(np, 2);
> +		if (!(ctx->pctrl_base)) {
> +			dev_err(&pdev->dev,
> +				"failed to get dss pctrl_base resource.\n");
> +			return -ENXIO;
> +		}
> +	}
> +
> +	dsi->gpio_mux = devm_gpiod_get(&pdev->dev, "mux", GPIOD_OUT_HIGH);
> +	if (IS_ERR(dsi->gpio_mux))
> +		return PTR_ERR(dsi->gpio_mux);
> +
> +	/* set dsi default output to panel */
> +	dsi->cur_client = OUT_PANEL;
> +	dsi->attached_client = dsi->cur_client;
> +
> +	DRM_INFO("dsi  cur_client is %d  <0->hdmi;1->panel>\n",
> +		 dsi->cur_client);
> +	/* dis-reset */
> +	/* ip_reset_dis_dsi0, ip_reset_dis_dsi1 */
> +	writel(0x30000000, ctx->peri_crg_base + PERRSTDIS3);
> +
> +	ctx->dss_dphy0_ref_clk = devm_clk_get(&pdev->dev, "clk_txdphy0_ref");
> +	if (IS_ERR(ctx->dss_dphy0_ref_clk)) {
> +		dev_err(&pdev->dev, "failed to get dss_dphy0_ref_clk clock\n");
> +		return PTR_ERR(ctx->dss_dphy0_ref_clk);
> +	}
> +
> +	ret = clk_set_rate(ctx->dss_dphy0_ref_clk, DEFAULT_MIPI_CLK_RATE);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "dss_dphy0_ref_clk clk_set_rate(%lu) failed, error=%d!\n",
> +			  DEFAULT_MIPI_CLK_RATE, ret);
> +		return -EINVAL;
> +	}
> +
> +	DRM_DEBUG("dss_dphy0_ref_clk:[%lu]->[%lu].\n",
> +		  DEFAULT_MIPI_CLK_RATE, clk_get_rate(ctx->dss_dphy0_ref_clk));
> +
> +	ctx->dss_dphy0_cfg_clk = devm_clk_get(&pdev->dev, "clk_txdphy0_cfg");
> +	if (IS_ERR(ctx->dss_dphy0_cfg_clk)) {
> +		dev_err(&pdev->dev, "failed to get dss_dphy0_cfg_clk clock\n");
> +		return PTR_ERR(ctx->dss_dphy0_cfg_clk);
> +	}
> +
> +	ret = clk_set_rate(ctx->dss_dphy0_cfg_clk, DEFAULT_MIPI_CLK_RATE);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "dss_dphy0_cfg_clk clk_set_rate(%lu) failed, error=%d!\n",
> +			  DEFAULT_MIPI_CLK_RATE, ret);
> +		return -EINVAL;
> +	}
> +
> +	DRM_DEBUG("dss_dphy0_cfg_clk:[%lu]->[%lu].\n",
> +		  DEFAULT_MIPI_CLK_RATE, clk_get_rate(ctx->dss_dphy0_cfg_clk));
> +
> +	ctx->dss_pclk_dsi0_clk = devm_clk_get(&pdev->dev, "pclk_dsi0");
> +	if (IS_ERR(ctx->dss_pclk_dsi0_clk)) {
> +		dev_err(&pdev->dev, "failed to get dss_pclk_dsi0_clk clock\n");
> +		return PTR_ERR(ctx->dss_pclk_dsi0_clk);
> +	}
> +
> +	return 0;
> +}
> +
> +static int dsi_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node;
> +	struct device *dev = &pdev->dev;
> +	struct dsi_data *data;
> +	struct dw_dsi *dsi;
> +	struct dsi_hw_ctx *ctx;
> +	int ret;
> +
> +	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +	if (!data) {
> +		dev_err(&pdev->dev, "failed to allocate dsi data.\n");
> +		return -ENOMEM;
> +	}
> +	dsi = &data->dsi;
> +	ctx = &data->ctx;
> +	dsi->ctx = ctx;
> +
> +	ctx->g_dss_version_tag = (long)of_device_get_match_data(dev);
> +
> +	/* parse HDMI bridge endpoint */
> +	ret = dsi_parse_endpoint(dsi, np, OUT_HDMI);
> +	if (ret)
> +		return ret;
> +
> +	ret = dsi_host_init(dev, dsi);
> +	if (ret)
> +		return ret;
> +
> +	/* parse panel endpoint */
> +	ret = dsi_parse_endpoint(dsi, np, OUT_PANEL);
> +	if (ret)
> +		goto err_host_unregister;
> +
> +	ret = dsi_parse_dt(pdev, dsi);
> +	if (ret)
> +		goto err_host_unregister;
> +
> +	platform_set_drvdata(pdev, data);
> +
> +	ret = component_add(dev, &dsi_ops);
> +	if (ret)
> +		goto err_host_unregister;
> +
> +	return 0;
> +
> +err_host_unregister:
> +	mipi_dsi_host_unregister(&dsi->host);
> +	return ret;
> +}
> +
> +static int dsi_remove(struct platform_device *pdev)
> +{
> +	component_del(&pdev->dev, &dsi_ops);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dsi_of_match[] = {
> +	{
> +		.compatible = "hisilicon,kirin960-dsi",
> +		.data = (void *)FB_ACCEL_HI366x
> +	}, {
> +		.compatible = "hisilicon,kirin970-dsi",
> +		.data = (void *)FB_ACCEL_KIRIN970
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, dsi_of_match);
> +
> +static struct platform_driver dsi_driver = {
> +	.probe = dsi_probe,
> +	.remove = dsi_remove,
> +	.driver = {
> +		.name = "kirin9xx-dw-dsi",
> +		.of_match_table = dsi_of_match,
> +	},
> +};
> +
> +module_platform_driver(dsi_driver);
> +
> +MODULE_DESCRIPTION("DesignWare MIPI DSI Host Controller v1.02 driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h
> new file mode 100644
> index 000000000000..0e3971ca328c
> --- /dev/null
> +++ b/drivers/staging/hikey9xx/gpu/kirin9xx_dw_dsi_reg.h
> @@ -0,0 +1,142 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2016 Linaro Limited.
> + * Copyright (c) 2014-2016 Hisilicon Limited.
> + * Copyright (c) 2014-2020, Huawei Technologies Co., Ltd
> + */
> +
> +#ifndef __DW_DSI_REG_H__
> +#define __DW_DSI_REG_H__
> +
> +#define MASK(x)				(BIT(x) - 1)
Not used, delete.

> +#define DEFAULT_MAX_TX_ESC_CLK	(10 * 1000000UL)
> +/*
> + * regs
> + */
> +#define PWR_UP                  0x04  /* Core power-up */
> +#define RESET                   0
> +#define POWERUP                 BIT(0)
> +#define PHY_IF_CFG              0xA4  /* D-PHY interface configuration */
> +#define CLKMGR_CFG              0x08  /* the internal clock dividers */
> +#define PHY_RSTZ                0xA0  /* D-PHY reset control */
> +#define PHY_ENABLECLK           BIT(2)
> +#define PHY_UNRSTZ              BIT(1)
> +#define PHY_UNSHUTDOWNZ         BIT(0)
> +#define PHY_TST_CTRL0           0xB4  /* D-PHY test interface control 0 */
> +#define PHY_TST_CTRL1           0xB8  /* D-PHY test interface control 1 */
> +#define CLK_TLPX                0x10
> +#define CLK_THS_PREPARE         0x11
> +#define CLK_THS_ZERO            0x12
> +#define CLK_THS_TRAIL           0x13
> +#define CLK_TWAKEUP             0x14
> +#define DATA_TLPX(x)            (0x20 + ((x) << 4))
> +#define DATA_THS_PREPARE(x)     (0x21 + ((x) << 4))
> +#define DATA_THS_ZERO(x)        (0x22 + ((x) << 4))
> +#define DATA_THS_TRAIL(x)       (0x23 + ((x) << 4))
> +#define DATA_TTA_GO(x)          (0x24 + ((x) << 4))
> +#define DATA_TTA_GET(x)         (0x25 + ((x) << 4))
> +#define DATA_TWAKEUP(x)         (0x26 + ((x) << 4))
> +#define PHY_CFG_I               0x60
> +#define PHY_CFG_PLL_I           0x63
> +#define PHY_CFG_PLL_II          0x64
> +#define PHY_CFG_PLL_III         0x65
> +#define PHY_CFG_PLL_IV          0x66
> +#define PHY_CFG_PLL_V           0x67
> +#define DPI_COLOR_CODING        0x10  /* DPI color coding */
> +#define DPI_CFG_POL             0x14  /* DPI polarity configuration */
> +#define VID_HSA_TIME            0x48  /* Horizontal Sync Active time */
> +#define VID_HBP_TIME            0x4C  /* Horizontal Back Porch time */
> +#define VID_HLINE_TIME          0x50  /* Line time */
> +#define VID_VSA_LINES           0x54  /* Vertical Sync Active period */
> +#define VID_VBP_LINES           0x58  /* Vertical Back Porch period */
> +#define VID_VFP_LINES           0x5C  /* Vertical Front Porch period */
> +#define VID_VACTIVE_LINES       0x60  /* Vertical resolution */
> +#define VID_PKT_SIZE            0x3C  /* Video packet size */
> +#define VID_MODE_CFG            0x38  /* Video mode configuration */
> +#define GEN_HDR			0x6c
> +#define GEN_HDATA(data)		(((data) & 0xffff) << 8)
> +#define GEN_HDATA_MASK		(0xffff << 8)
> +#define GEN_HTYPE(type)		(((type) & 0xff) << 0)
> +#define GEN_HTYPE_MASK		0xff
> +#define GEN_PLD_DATA		0x70
> +#define CMD_PKT_STATUS		0x74
> +#define GEN_CMD_EMPTY		BIT(0)
> +#define GEN_CMD_FULL		BIT(1)
> +#define GEN_PLD_W_EMPTY		BIT(2)
> +#define GEN_PLD_W_FULL		BIT(3)
> +#define GEN_PLD_R_EMPTY		BIT(4)
> +#define GEN_PLD_R_FULL		BIT(5)
> +#define GEN_RD_CMD_BUSY		BIT(6)
> +#define CMD_MODE_CFG		0x68
> +#define MAX_RD_PKT_SIZE_LP	BIT(24)
> +#define DCS_LW_TX_LP		BIT(19)
> +#define DCS_SR_0P_TX_LP		BIT(18)
> +#define DCS_SW_1P_TX_LP		BIT(17)
> +#define DCS_SW_0P_TX_LP		BIT(16)
> +#define GEN_LW_TX_LP		BIT(14)
> +#define GEN_SR_2P_TX_LP		BIT(13)
> +#define GEN_SR_1P_TX_LP		BIT(12)
> +#define GEN_SR_0P_TX_LP		BIT(11)
> +#define GEN_SW_2P_TX_LP		BIT(10)
> +#define GEN_SW_1P_TX_LP		BIT(9)
> +#define GEN_SW_0P_TX_LP		BIT(8)
> +#define EN_ACK_RQST		BIT(1)
> +#define EN_TEAR_FX		BIT(0)
> +#define CMD_MODE_ALL_LP		(MAX_RD_PKT_SIZE_LP | \
> +				 DCS_LW_TX_LP | \
> +				 DCS_SR_0P_TX_LP | \
> +				 DCS_SW_1P_TX_LP | \
> +				 DCS_SW_0P_TX_LP | \
> +				 GEN_LW_TX_LP | \
> +				 GEN_SR_2P_TX_LP | \
> +				 GEN_SR_1P_TX_LP | \
> +				 GEN_SR_0P_TX_LP | \
> +				 GEN_SW_2P_TX_LP | \
> +				 GEN_SW_1P_TX_LP | \
> +				 GEN_SW_0P_TX_LP)
> +#define PHY_TMR_CFG             0x9C  /* Data lanes timing configuration */
> +#define BTA_TO_CNT              0x8C  /* Response timeout definition */
> +#define PHY_TMR_LPCLK_CFG       0x98  /* clock lane timing configuration */
> +#define CLK_DATA_TMR_CFG        0xCC
> +#define LPCLK_CTRL              0x94  /* Low-power in clock lane */
> +#define PHY_TXREQUESTCLKHS      BIT(0)
> +#define MODE_CFG                0x34  /* Video or Command mode selection */
> +#define PHY_STATUS              0xB0  /* D-PHY PPI status interface */
> +
> +#define	PHY_STOP_WAIT_TIME      0x30
> +#define CMD_PKT_STATUS_TIMEOUT_US	20000
> +
> +/*
> + * regs relevant enum
> + */
> +enum dpi_color_coding {
> +	DSI_24BITS_1 = 5,
> +};
> +
> +enum dsi_video_mode_type {
> +	DSI_NON_BURST_SYNC_PULSES = 0,
> +	DSI_NON_BURST_SYNC_EVENTS,
> +	DSI_BURST_SYNC_PULSES_1,
> +	DSI_BURST_SYNC_PULSES_2
> +};
> +
> +enum dsi_work_mode {
> +	DSI_VIDEO_MODE = 0,
> +	DSI_COMMAND_MODE
> +};
> +
> +/*
> + * Register Write/Read Helper functions
> + */
> +static inline void dw_update_bits(void __iomem *addr, u32 bit_start,
> +				  u32 mask, u32 val)

Not used I think, so delete if not.

> +{
> +	u32 tmp, orig;
> +
> +	orig = readl(addr);
> +	tmp = orig & ~(mask << bit_start);
> +	tmp |= (val & mask) << bit_start;
> +	writel(tmp, addr);
> +}
> +
> +#endif /* __DW_DRM_DSI_H__ */
Sam Ravnborg Aug. 25, 2020, 8:21 p.m. UTC | #37
Hi Mauro.

Laurent and I discussed this driver a little on irc.
Some highlights:

This parts could use register names:
+       writel(0x2, noc_dss_base + 0xc);
+       writel(0x2, noc_dss_base + 0x8c);
+       writel(0x2, noc_dss_base + 0x10c);
+       writel(0x2, noc_dss_base + 0x18c);

The two nodes in the DT for DPE and DSI uses overlapping range for reg
entries. It looks like a syscon node or some iommu thing is needed to do
this properly.

The chain will lok like this:

DPE -> DSI -> video mux -> {adv7533, panel}

But drm_bridge has not yet support for such non-linear setup.
The recommendation is to focus on the HDMI prat. Then we can later
come up with support for a video mux.

The video mux should have a dedicated node with one input node and two
output nodes. Which is also where the gpio should be.

The DSI node references two DPHY instances - should it be PHY driver(s)?

Does the DSI part contain one or two instances. Clocks looks duplicated.

Does the DPE and DSI share a lot of register blocks - or does it just
look like this from a first point of view?

You can read though the logs here:
https://people.freedesktop.org/~cbrill/dri-log/index.php

Could you please try to get back on some of the points above so we can
help you move forward in the right direction.

Thanks,
	Sam
Nicolas Dufresne Aug. 26, 2020, 2:44 p.m. UTC | #38
Le mardi 25 août 2020 à 13:30 +0200, Mauro Carvalho Chehab a écrit :
> Em Tue, 25 Aug 2020 05:29:29 +1000
> Dave Airlie <airlied@gmail.com> escreveu:
> 
> > On Thu, 20 Aug 2020 at 20:02, Laurent Pinchart
> > <laurent.pinchart@ideasonboard.com> wrote:
> > > Hi Mauro,
> > > 
> > > On Thu, Aug 20, 2020 at 09:03:26AM +0200, Mauro Carvalho Chehab wrote:  
> > > > Em Wed, 19 Aug 2020 12:52:06 -0700 John Stultz escreveu:  
> > > > > On Wed, Aug 19, 2020 at 8:31 AM Laurent Pinchart wrote:  
> > > > > > On Wed, Aug 19, 2020 at 05:21:20PM +0200, Sam Ravnborg wrote:  
> > > > > > > On Wed, Aug 19, 2020 at 01:45:28PM +0200, Mauro Carvalho Chehab wrote:  
> > > > > > > > This patch series port the out-of-tree driver for Hikey 970 (which
> > > > > > > > should also support Hikey 960) from the official 96boards tree:
> > > > > > > > 
> > > > > > > >    https://github.com/96boards-hikey/linux/tree/hikey970-v4.9
> > > > > > > > 
> > > > > > > > Based on his history, this driver seems to be originally written
> > > > > > > > for Kernel 4.4, and was later ported to Kernel 4.9. The original
> > > > > > > > driver used to depend on ION (from Kernel 4.4) and had its own
> > > > > > > > implementation for FB dev API.
> > > > > > > > 
> > > > > > > > As I need to preserve the original history (with has patches from
> > > > > > > > both HiSilicon and from Linaro),  I'm starting from the original
> > > > > > > > patch applied there. The remaining patches are incremental,
> > > > > > > > and port this driver to work with upstream Kernel.
> > > > > > > >  
> > > > > ...  
> > > > > > > > - Due to legal reasons, I need to preserve the authorship of
> > > > > > > >   each one responsbile for each patch. So, I need to start from
> > > > > > > >   the original patch from Kernel 4.4;  
> > > > > ...  
> > > > > > > I do acknowledge you need to preserve history and all -
> > > > > > > but this patchset is not easy to review.  
> > > > > > 
> > > > > > Why do we need to preserve history ? Adding relevant Signed-off-by and
> > > > > > Co-developed-by should be enough, shouldn't it ? Having a public branch
> > > > > > that contains the history is useful if anyone is interested, but I don't
> > > > > > think it's required in mainline.  
> > > > > 
> > > > > Yea. I concur with Laurent here. I'm not sure what legal reasoning you
> > > > > have on this but preserving the "absolute" history here is actively
> > > > > detrimental for review and understanding of the patch set.
> > > > > 
> > > > > Preserving Authorship, Signed-off-by lines and adding Co-developed-by
> > > > > lines should be sufficient to provide both atribution credit and DCO
> > > > > history.  
> > > > 
> > > > I'm not convinced that, from legal standpoint, folding things would
> > > > be enough. See, there are at least 3 legal systems involved here
> > > > among the different patch authors:
> > > > 
> > > >       - civil law;
> > > >       - common law;
> > > >       - customary law + common law.
> > > > 
> > > > Merging stuff altogether from different law systems can be problematic,
> > > > and trying to discuss this with experienced IP property lawyers will
> > > > for sure take a lot of time and efforts. I also bet that different
> > > > lawyers will have different opinions, because laws are subject to
> > > > interpretation. With that matter I'm not aware of any court rules
> > > > with regards to folded patches. So, it sounds to me that folding
> > > > patches is something that has yet to be proofed in courts around
> > > > the globe.
> > > > 
> > > > At least for US legal system, it sounds that the Country of
> > > > origin of a patch is relevant, as they have a concept of
> > > > "national technology" that can be subject to export regulations.
> > > > 
> > > > From my side, I really prefer to play safe and stay out of any such
> > > > legal discussions.  
> > > 
> > > Let's be serious for a moment. If you think there are legal issues in
> > > taking GPL-v2.0-only patches and squashing them while retaining
> > > authorship information through tags, the Linux kernel if *full* of that.
> > > You also routinely modify patches that you commit to the media subsystem
> > > to fix "small issues".
> > > 
> > > The country of origin argument makes no sense either, the kernel code
> > > base if full of code coming from pretty much all country on the planet.
> > > 
> > > Keeping the patches separate make this hard to review. Please squash
> > > them.  
> > 
> > I'm inclined to agree with Laurent here.
> > 
> > Patches submitted as GPL-v2 with DCO lines and author names/companies
> > should be fine to be squashed and rearranged,
> > as long as the DCO and Authorship is kept somewhere in the new patch
> > that is applied.
> > 
> > Review is more important here.
> 
> Sorry, but I can't agree that review is more important than to be able
> to properly indicate copyrights in a valid way at the legal systems that
> it would apply ;-)

Regardless of the "review-ability", our users distribute the Linux
Kernel as a whole, so who contributed which specific line of code is
already lost in a way. All we see in the distribution if a list of
copyright holder and licenses. In this context, the per patches
ownership have no legal implication. My two, non lawyer cents.

> 
> In any case, there's an easy way to make the code easy to review:
> I can write the patches against staging (where it is OK to submit
> preserving the history) and then add a final patch moving it out
> of staging.
> 
> You can then just review the last patch, as it will contain the
> entire code on it.
> 
> Another alternative, as I'm already doing with Sam, is for me to
> submit the folded code as a reply to 00/xx. You can then just 
> review the final code, without concerning about how the code reached
> there.
> 
> From review point of the view, this will be the same as reviewing
> a folded patch, but, from legal standpoint, the entire copyright
> chain will be preserved.
> 
> Thanks,
> Mauro