Re: [PATCH] drm/loongson: Add support for the DC in LS2K1000 SoC

From: Icenowy Zheng
Date: Fri Oct 13 2023 - 04:23:09 EST


在 2023-10-12星期四的 00:26 +0800,Sui Jingfeng写道:
> LS2K1000 is a low end SoC (two core 1.0gHz), it don't has dedicated
> VRAM.
> It requires the framebuffer to be phisically continguous to scanout.
> The
> display controller in LS2K1000 don't has built-in GPIO hardware, the
> structure (register bit field) of its pixel, DC, GPU, DDR PLL are
> also
> defferent from other model. But for the display controller itself,
> Most of
> hardware features of it are same with ls7a1000.
>
> Below is a simple dts for it.

Why don't you write a proper, YAML-formatted binding?

This will help handling the correctness of device trees, and a binding
is required to allow the driver to enter.

>
> aliases {
>         i2c0 = &i2c0;
>         i2c1 = &i2c1;
> };
>
> reserved-memory {
>         #address-cells = <2>;
>         #size-cells = <2>;
>         ranges;
>
>         display_reserved: framebuffer@30000000 {
>                   compatible = "shared-dma-pool";
>                   reg = <0x0 0x20000000 0x0 0x08000000>; /* 128M */
>                   linux,cma-default;
>         };
> };
>
> i2c0: i2c-gpio-0 {
>         compatible = "i2c-gpio";
>         scl-gpios = <&gpio0 0 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
>         sda-gpios = <&gpio0 1 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
>         i2c-gpio,delay-us = <5>;        /* ~100 kHz */
>         status = "okay";
> };
>
> i2c1: i2c-gpio-1 {
>         compatible = "i2c-gpio";
>         scl-gpios = <&gpio0 33 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
>         sda-gpios = <&gpio0 32 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
>         i2c-gpio,delay-us = <5>;        /* ~100 kHz */
>         status = "okay";
> };
>
> display-controller@6,0 {
>         reg = <0x3000 0x0 0x0 0x0 0x0>;
>         interrupt-parent = <&liointc0>;
>         interrupts = <28 IRQ_TYPE_LEVEL_HIGH>
>         memory-region = <&display_reserved>;

Is a system-wide CMA pool enough for this usage? And for a display
controller, will 128M be too much? (I assume the Vivante GPU do not
require contingous memory).

>         status = "okay";
> };
>
> This patch is tested on ls2k1000 evaluation board.
>
> $ dmesg | grep "0000:00:06.0"
>
>  loongson 0000:00:06.0: Found LS2K1000 SoC, revision: 0
>  loongson 0000:00:06.0: [drm] dc: 250MHz, ddr: 400MHz, gpu: 228MHz
>  loongson 0000:00:06.0: [drm] Using of reserved mem:
> 8000000@0x20000000
>  loongson 0000:00:06.0: [drm] VRAM: 8192 pages ready
>  loongson 0000:00:06.0: [drm] GTT: 32768 pages ready
>  loongson 0000:00:06.0: [drm] display pipe-0 has a DVO
>  loongson 0000:00:06.0: [drm] display pipe-1 has a DVO
>  loongson 0000:00:06.0: [drm] Total 2 outputs
>  loongson 0000:00:06.0: [drm] registered irq: 28
>  [drm] Initialized loongson 1.0.0 20220701 for 0000:00:06.0 on minor
> 0
>  loongson 0000:00:06.0: [drm] fb0: loongsondrmfb frame buffer device
>
> Signed-off-by: Sui Jingfeng <suijingfeng@xxxxxxxxxxx>
> ---
>  drivers/gpu/drm/loongson/Makefile             |   1 +
>  drivers/gpu/drm/loongson/loongson_device.c    |  59 +++++++
>  drivers/gpu/drm/loongson/lsdc_drv.c           |  44 ++++-
>  drivers/gpu/drm/loongson/lsdc_drv.h           |   9 ++
>  drivers/gpu/drm/loongson/lsdc_gfxpll.c        |  11 +-
>  drivers/gpu/drm/loongson/lsdc_gfxpll.h        |   4 +
>  drivers/gpu/drm/loongson/lsdc_gfxpll_2k1000.c | 141 ++++++++++++++++
>  drivers/gpu/drm/loongson/lsdc_i2c.c           |  41 +++++
>  drivers/gpu/drm/loongson/lsdc_i2c.h           |   4 +
>  drivers/gpu/drm/loongson/lsdc_pixpll.c        | 153
> +++++++++++++++++-
>  drivers/gpu/drm/loongson/lsdc_pixpll.h        |   4 +
>  drivers/gpu/drm/loongson/lsdc_probe.c         |  27 ++++
>  drivers/gpu/drm/loongson/lsdc_probe.h         |   2 +
>  drivers/gpu/drm/loongson/lsdc_regs.h          |  36 +++++
>  14 files changed, 528 insertions(+), 8 deletions(-)
>  create mode 100644 drivers/gpu/drm/loongson/lsdc_gfxpll_2k1000.c
>
> diff --git a/drivers/gpu/drm/loongson/Makefile
> b/drivers/gpu/drm/loongson/Makefile
> index 91e72bd900c1..d6e709e19fba 100644
> --- a/drivers/gpu/drm/loongson/Makefile
> +++ b/drivers/gpu/drm/loongson/Makefile
> @@ -7,6 +7,7 @@ loongson-y := \
>         lsdc_drv.o \
>         lsdc_gem.o \
>         lsdc_gfxpll.o \
> +       lsdc_gfxpll_2k1000.o \
>         lsdc_i2c.o \
>         lsdc_irq.o \
>         lsdc_output_7a1000.o \
> diff --git a/drivers/gpu/drm/loongson/loongson_device.c
> b/drivers/gpu/drm/loongson/loongson_device.c
> index 9986c8a2a255..67274d9e3b12 100644
> --- a/drivers/gpu/drm/loongson/loongson_device.c
> +++ b/drivers/gpu/drm/loongson/loongson_device.c
> @@ -6,6 +6,7 @@
>  #include <linux/pci.h>
>  
>  #include "lsdc_drv.h"
> +#include "lsdc_probe.h"
>  
>  static const struct lsdc_kms_funcs ls7a1000_kms_funcs = {
>         .create_i2c = lsdc_create_i2c_chan,
> @@ -25,6 +26,20 @@ static const struct lsdc_kms_funcs
> ls7a2000_kms_funcs = {
>         .crtc_init = ls7a2000_crtc_init,
>  };
>  
> +/*
> + * Most of hardware features of ls2k1000 are same with ls7a1000, we
> take the
> + * ls7a1000_kms_funcs as a prototype, copy and modify to form a
> variant for
> + * ls2k1000.
> + */
> +static const struct lsdc_kms_funcs ls2k1000_kms_funcs = {
> +       .create_i2c = ls2k1000_get_i2c,
> +       .irq_handler = ls7a1000_dc_irq_handler,
> +       .output_init = ls7a1000_output_init,
> +       .cursor_plane_init = ls7a1000_cursor_plane_init,
> +       .primary_plane_init = lsdc_primary_plane_init,
> +       .crtc_init = ls7a1000_crtc_init,
> +};
> +
>  static const struct loongson_gfx_desc ls7a1000_gfx = {
>         .dc = {
>                 .num_of_crtc = 2,
> @@ -36,6 +51,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx
> = {
>                 .hw_cursor_h = 32,
>                 .pitch_align = 256,
>                 .has_vblank_counter = false,
> +               .has_dedicated_vram = true,
>                 .funcs = &ls7a1000_kms_funcs,
>         },
>         .conf_reg_base = LS7A1000_CONF_REG_BASE,
> @@ -43,6 +59,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx
> = {
>                 .reg_offset = LS7A1000_PLL_GFX_REG,
>                 .reg_size = 8,
>         },
> +       .gfxpll_funcs = &ls7a1000_gfx_pll_funcs,
>         .pixpll = {
>                 [0] = {
>                         .reg_offset = LS7A1000_PIXPLL0_REG,
> @@ -53,6 +70,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx
> = {
>                         .reg_size = 8,
>                 },
>         },
> +       .pixpll_funcs = &ls7a1000_pixpll_funcs,
>         .chip_id = CHIP_LS7A1000,
>         .model = "LS7A1000 bridge chipset",
>  };
> @@ -68,6 +86,7 @@ static const struct loongson_gfx_desc ls7a2000_gfx
> = {
>                 .hw_cursor_h = 64,
>                 .pitch_align = 64,
>                 .has_vblank_counter = true,
> +               .has_dedicated_vram = true,
>                 .funcs = &ls7a2000_kms_funcs,
>         },
>         .conf_reg_base = LS7A2000_CONF_REG_BASE,
> @@ -75,6 +94,7 @@ static const struct loongson_gfx_desc ls7a2000_gfx
> = {
>                 .reg_offset = LS7A2000_PLL_GFX_REG,
>                 .reg_size = 8,
>         },
> +       .gfxpll_funcs = &ls7a2000_gfx_pll_funcs,
>         .pixpll = {
>                 [0] = {
>                         .reg_offset = LS7A2000_PIXPLL0_REG,
> @@ -85,18 +105,57 @@ static const struct loongson_gfx_desc
> ls7a2000_gfx = {
>                         .reg_size = 8,
>                 },
>         },
> +       .pixpll_funcs = &ls7a2000_pixpll_funcs,
>         .chip_id = CHIP_LS7A2000,
>         .model = "LS7A2000 bridge chipset",
>  };
>  
> +static const struct loongson_gfx_desc ls2k1000_gfx = {
> +       .dc = {
> +               .num_of_crtc = 2,
> +               .max_pixel_clk = 200000,
> +               .max_width = 2048,
> +               .max_height = 2048,
> +               .num_of_hw_cursor = 1,
> +               .hw_cursor_w = 32,
> +               .hw_cursor_h = 32,
> +               .pitch_align = 256,
> +               .has_vblank_counter = false,
> +               .has_dedicated_vram = false,
> +               .funcs = &ls2k1000_kms_funcs,
> +       },
> +       .conf_reg_base = LS2K1000_CONF_REG_BASE,
> +       .gfxpll = {
> +               .reg_offset = LS2K1000_DDR_PLL_REG,
> +               .reg_size = 16 + 16,
> +       },
> +       .gfxpll_funcs = &ls2k1000_gfx_pll_funcs,
> +       .pixpll = {
> +               [0] = {
> +                       .reg_offset = LS2K1000_PIX0_PLL_REG,
> +                       .reg_size = 16,
> +               },
> +               [1] = {
> +                       .reg_offset = LS2K1000_PIX1_PLL_REG,
> +                       .reg_size = 16,
> +               },
> +       },
> +       .pixpll_funcs = &ls2k1000_pixpll_funcs,
> +       .chip_id = CHIP_LS2K1000,
> +       .model = "LS2K1000 SoC",
> +};
> +
>  static const struct lsdc_desc *__chip_id_desc_table[] = {
>         [CHIP_LS7A1000] = &ls7a1000_gfx.dc,
>         [CHIP_LS7A2000] = &ls7a2000_gfx.dc,
> +       [CHIP_LS2K1000] = &ls2k1000_gfx.dc,
>         [CHIP_LS_LAST] = NULL,
>  };
>  
>  const struct lsdc_desc *
>  lsdc_device_probe(struct pci_dev *pdev, enum loongson_chip_id
> chip_id)
>  {
> +       chip_id = loongson_chip_id_fixup(chip_id);
> +
>         return __chip_id_desc_table[chip_id];
>  }
> diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c
> b/drivers/gpu/drm/loongson/lsdc_drv.c
> index 89ccc0c43169..65a4d96448bb 100644
> --- a/drivers/gpu/drm/loongson/lsdc_drv.c
> +++ b/drivers/gpu/drm/loongson/lsdc_drv.c
> @@ -5,6 +5,7 @@
>  
>  #include <linux/pci.h>
>  #include <linux/vgaarb.h>
> +#include <linux/of_address.h>
>  
>  #include <drm/drm_aperture.h>
>  #include <drm/drm_atomic.h>
> @@ -84,6 +85,8 @@ static int lsdc_modeset_init(struct lsdc_device
> *ldev,
>                 dispipe = &ldev->dispipe[i];
>                 if (dispipe->li2c)
>                         ddc = &dispipe->li2c->adapter;
> +               else
> +                       ddc = dispipe->adapter;
>  
>                 ret = funcs->output_init(ddev, dispipe, ddc, i);
>                 if (ret)
> @@ -155,8 +158,7 @@ static int lsdc_mode_config_init(struct
> drm_device *ddev,
>   * the DC could access the on-board VRAM.
>   */
>  static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
> -                                  struct pci_dev *pdev_dc,
> -                                  const struct lsdc_desc *descp)
> +                                  struct pci_dev *pdev_dc)
>  {
>         struct drm_device *ddev = &ldev->base;
>         struct pci_dev *pdev_gpu;
> @@ -187,6 +189,38 @@ static int lsdc_get_dedicated_vram(struct
> lsdc_device *ldev,
>         return 0;
>  }
>  
> +static int lsdc_of_get_reserved_mem(struct lsdc_device *ldev)
> +{
> +       struct drm_device *ddev = &ldev->base;
> +       unsigned long size = 0;
> +       struct device_node *node;
> +       struct resource r;
> +       int ret;
> +
> +       if (!ddev->dev->of_node)
> +               return -ENOENT;
> +
> +       node = of_parse_phandle(ddev->dev->of_node, "memory-region",
> 0);
> +       if (!node) {
> +               drm_err(ddev, "No memory-region property\n");
> +               return -ENOENT;
> +       }
> +
> +       ret = of_address_to_resource(node, 0, &r);
> +       of_node_put(node);
> +       if (ret)
> +               return ret;
> +
> +       size = r.end - r.start + 1;
> +
> +       ldev->vram_base = r.start;
> +       ldev->vram_size = size;
> +
> +       drm_info(ddev, "Using of reserved mem: %lx@%pa\n", size,
> &r.start);
> +
> +       return 0;
> +}
> +
>  static struct lsdc_device *
>  lsdc_create_device(struct pci_dev *pdev,
>                    const struct lsdc_desc *descp,
> @@ -207,7 +241,11 @@ lsdc_create_device(struct pci_dev *pdev,
>  
>         loongson_gfxpll_create(ddev, &ldev->gfxpll);
>  
> -       ret = lsdc_get_dedicated_vram(ldev, pdev, descp);
> +       if (descp->has_dedicated_vram)
> +               ret = lsdc_get_dedicated_vram(ldev, pdev);
> +       else
> +               ret = lsdc_of_get_reserved_mem(ldev);
> +
>         if (ret) {
>                 drm_err(ddev, "Init VRAM failed: %d\n", ret);
>                 return ERR_PTR(ret);
> diff --git a/drivers/gpu/drm/loongson/lsdc_drv.h
> b/drivers/gpu/drm/loongson/lsdc_drv.h
> index fbf2d760ef27..279de7bf6dc5 100644
> --- a/drivers/gpu/drm/loongson/lsdc_drv.h
> +++ b/drivers/gpu/drm/loongson/lsdc_drv.h
> @@ -41,6 +41,7 @@
>  enum loongson_chip_id {
>         CHIP_LS7A1000 = 0,
>         CHIP_LS7A2000 = 1,
> +       CHIP_LS2K1000 = 2,
>         CHIP_LS_LAST,
>  };
>  
> @@ -61,6 +62,7 @@ struct lsdc_desc {
>         u32 hw_cursor_h;
>         u32 pitch_align;         /* CRTC DMA alignment constraint */
>         bool has_vblank_counter; /* 32 bit hw vsync counter */
> +       bool has_dedicated_vram;
>  
>         /* device dependent ops, dc side */
>         const struct lsdc_kms_funcs *funcs;
> @@ -78,12 +80,14 @@ struct loongson_gfx_desc {
>                 u32 reg_offset;
>                 u32 reg_size;
>         } gfxpll;
> +       const struct loongson_gfxpll_funcs *gfxpll_funcs;
>  
>         /* Pixel PLL, per display pipe */
>         struct {
>                 u32 reg_offset;
>                 u32 reg_size;
>         } pixpll[LSDC_NUM_CRTC];
> +       const struct lsdc_pixpll_funcs *pixpll_funcs;

It could be better to have this kind of refactor independent of real
new HW implemetation.

>  
>         enum loongson_chip_id chip_id;
>         char model[64];
> @@ -189,6 +193,11 @@ struct lsdc_display_pipe {
>         struct lsdc_primary primary;
>         struct lsdc_cursor cursor;
>         struct lsdc_output output;
> +       /*
> +        * For device which don't has built-in GPIO hardware, such as
> ls2k1000,
> +        * we will get a i2c adapter from other module or subsystem.
> +        */
> +       struct i2c_adapter *adapter;
>         struct lsdc_i2c *li2c;
>         unsigned int index;
>  };
> diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.c
> b/drivers/gpu/drm/loongson/lsdc_gfxpll.c
> index 249c09d703ad..4a4efe696d5a 100644
> --- a/drivers/gpu/drm/loongson/lsdc_gfxpll.c
> +++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.c
> @@ -163,7 +163,14 @@ static int loongson_gfxpll_init(struct
> loongson_gfxpll * const this)
>         return 0;
>  }
>  
> -static const struct loongson_gfxpll_funcs lsdc_gmc_gpu_funcs = {
> +const struct loongson_gfxpll_funcs ls7a1000_gfx_pll_funcs = {
> +       .init = loongson_gfxpll_init,
> +       .update = loongson_gfxpll_update,
> +       .get_rates = loongson_gfxpll_get_rates,
> +       .print = loongson_gfxpll_print,
> +};
> +
> +const struct loongson_gfxpll_funcs ls7a2000_gfx_pll_funcs = {
>         .init = loongson_gfxpll_init,
>         .update = loongson_gfxpll_update,
>         .get_rates = loongson_gfxpll_get_rates,
> @@ -185,7 +192,7 @@ int loongson_gfxpll_create(struct drm_device
> *ddev,
>         this->ddev = ddev;
>         this->reg_size = gfx->gfxpll.reg_size;
>         this->reg_base = gfx->conf_reg_base + gfx->gfxpll.reg_offset;
> -       this->funcs = &lsdc_gmc_gpu_funcs;
> +       this->funcs = gfx->gfxpll_funcs;
>  
>         ret = this->funcs->init(this);
>         if (unlikely(ret)) {
> diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll.h
> b/drivers/gpu/drm/loongson/lsdc_gfxpll.h
> index 9d59cbfc145d..6a30d2039d4a 100644
> --- a/drivers/gpu/drm/loongson/lsdc_gfxpll.h
> +++ b/drivers/gpu/drm/loongson/lsdc_gfxpll.h
> @@ -49,4 +49,8 @@ struct loongson_gfxpll {
>  int loongson_gfxpll_create(struct drm_device *ddev,
>                            struct loongson_gfxpll **ppout);
>  
> +extern const struct loongson_gfxpll_funcs ls2k1000_gfx_pll_funcs;
> +extern const struct loongson_gfxpll_funcs ls7a1000_gfx_pll_funcs;
> +extern const struct loongson_gfxpll_funcs ls7a2000_gfx_pll_funcs;
> +
>  #endif
> diff --git a/drivers/gpu/drm/loongson/lsdc_gfxpll_2k1000.c
> b/drivers/gpu/drm/loongson/lsdc_gfxpll_2k1000.c
> new file mode 100644
> index 000000000000..8b442db05972
> --- /dev/null
> +++ b/drivers/gpu/drm/loongson/lsdc_gfxpll_2k1000.c
> @@ -0,0 +1,141 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2023 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/delay.h>
> +
> +#include <drm/drm_file.h>
> +#include <drm/drm_managed.h>
> +#include <drm/drm_print.h>
> +
> +#include "lsdc_drv.h"
> +
> +union ls2k1000_ddr_pll_reg_bitmap {
> +       struct ls2k1000_ddr_pll_bitmap {
> +               /* Byte 0 ~ Byte 3 */
> +               unsigned bypass       :  1;  /*  0: Bypass ddr pll
> entirely */
> +               unsigned oe_gpu       :  1;  /*  1: GPU output clk
> enable   */
> +               unsigned write_en     :  1;  /*  2: Allow software
> update   */
> +               unsigned _reserved_1_ : 23;  /*  3 : 25  Don't
> care         */
> +               unsigned div_ref      :  6;  /* 31 : 26  Input clk
> divider  */
> +               /* Byte 4 ~ Byte 7 */
> +               unsigned loopc        : 10;   /* 41 : 32 Clock
> multiplier   */
> +               unsigned _reserved_2_ : 22;   /* 42 : 63
> Useless            */
> +
> +               /* Byte 8 ~ Byte 15 */
> +               unsigned div_out_ddr  :  6;  /*  5 : 0  DDR output
> divider */
> +               unsigned _reserved_3_ : 16;  /* 21 : 6  */
> +               unsigned div_out_gpu  :  6;  /* 27 : 22 GPU output
> divider */
> +               unsigned _reserved_4_ : 16;  /* 43 : 28 */
> +               unsigned div_out_hda  :  7;  /* 50 : 44 HDA output
> divider */
> +               unsigned _reserved_5_ : 13;  /* 63 : 51 */
> +       } bitmap;
> +       u32 w[4];
> +       u64 d[2];
> +};
> +
> +union ls2k1000_dc_pll_reg_bitmap {
> +       struct ls2k1000_dc_pll_bitmap {
> +               /* Byte 0 ~ Byte 3 */
> +               unsigned _reserved_1_ : 26;  /*  0 : 25  Don't
> care         */
> +               unsigned div_ref      :  6;  /* 31 : 26  Input clk
> divider  */
> +               /* Byte 4 ~ Byte 7 */
> +               unsigned loopc        : 10;   /* 41 : 32 Clock
> multiplier   */
> +               unsigned _reserved_2_ : 22;   /* 42 : 63
> Useless            */
> +
> +               /* Byte 8 ~ Byte 15 */
> +               unsigned div_out_dc   :  6;  /*  5 : 0  DC output
> divider */
> +               unsigned _reserved_3_ : 16;  /* 21 : 6  */
> +               unsigned div_out_gmac :  6;  /* 27 : 22 GMAC output
> divider */
> +               unsigned _reserved_4_ :  4;  /* 31 : 28 */
> +               unsigned _reserved_5_ : 32;  /* 63 : 32 */
> +       } bitmap;
> +       u32 w[4];
> +       u64 d[2];
> +};
> +
> +static int ls2k1000_gfxpll_init(struct loongson_gfxpll * const this)
> +{
> +       struct drm_printer printer = drm_info_printer(this->ddev-
> >dev);
> +
> +       this->mmio = ioremap(this->reg_base, this->reg_size);
> +       if (IS_ERR_OR_NULL(this->mmio))
> +               return -ENOMEM;
> +
> +       this->funcs->print(this, &printer, false);
> +
> +       return 0;
> +}
> +
> +static inline void __rreg_u128(void __iomem *mmio, u32 w[], u64 d[])
> +{
> +#if defined(CONFIG_64BIT)
> +       d[0] = readq(mmio);
> +       d[1] = readq(mmio + 8);
> +#else
> +       w[0] = readl(mmio);
> +       w[1] = readl(mmio + 4);
> +       w[2] = readl(mmio + 8);
> +       w[3] = readl(mmio + 12);
> +#endif
> +}
> +
> +static void ls2k1000_gfxpll_get_rates(struct loongson_gfxpll * const
> this,
> +                                     unsigned int *dc,
> +                                     unsigned int *ddr,
> +                                     unsigned int *gpu)
> +{
> +       unsigned int ref_clock_mhz = LSDC_PLL_REF_CLK_KHZ / 1000;
> +       union ls2k1000_ddr_pll_reg_bitmap ddr_pll_reg_bitmap;
> +       union ls2k1000_dc_pll_reg_bitmap dc_pll_reg_bitmap;
> +       unsigned int div_ref;
> +       unsigned int loopc;
> +       unsigned int div_out_dc;
> +       unsigned int div_out_ddr;
> +       unsigned int div_out_gpu;
> +       unsigned int dc_mhz;
> +       unsigned int ddr_mhz;
> +       unsigned int gpu_mhz;
> +
> +       __rreg_u128(this->mmio, ddr_pll_reg_bitmap.w,
> ddr_pll_reg_bitmap.d);
> +       div_ref = ddr_pll_reg_bitmap.bitmap.div_ref;
> +       loopc = ddr_pll_reg_bitmap.bitmap.loopc;
> +       div_out_ddr = ddr_pll_reg_bitmap.bitmap.div_out_ddr;
> +       div_out_gpu = ddr_pll_reg_bitmap.bitmap.div_out_gpu;
> +       ddr_mhz = ref_clock_mhz / div_ref * loopc / div_out_ddr;
> +       gpu_mhz = ref_clock_mhz / div_ref * loopc / div_out_gpu;
> +
> +       __rreg_u128(this->mmio + 16, dc_pll_reg_bitmap.w,
> dc_pll_reg_bitmap.d);
> +       div_out_dc = dc_pll_reg_bitmap.bitmap.div_out_dc;
> +       div_ref = dc_pll_reg_bitmap.bitmap.div_ref;
> +       loopc = dc_pll_reg_bitmap.bitmap.loopc;
> +       dc_mhz = ref_clock_mhz / div_ref * loopc / div_out_dc;
> +
> +       if (dc)
> +               *dc = dc_mhz;
> +
> +       if (ddr)
> +               *ddr = ddr_mhz;
> +
> +       if (gpu)
> +               *gpu = gpu_mhz;
> +}
> +
> +static void ls2k1000_gfxpll_print(struct loongson_gfxpll * const
> this,
> +                                 struct drm_printer *p,
> +                                 bool verbose)
> +{
> +       unsigned int dc, ddr, gpu;
> +
> +       this->funcs->get_rates(this, &dc, &ddr, &gpu);
> +
> +       drm_printf(p, "dc: %uMHz, ddr: %uMHz, gpu: %uMHz\n", dc, ddr,
> gpu);
> +}
> +
> +const struct loongson_gfxpll_funcs ls2k1000_gfx_pll_funcs = {
> +       .init = ls2k1000_gfxpll_init,
> +       .get_rates = ls2k1000_gfxpll_get_rates,
> +       .print = ls2k1000_gfxpll_print,
> +};
> +
> diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.c
> b/drivers/gpu/drm/loongson/lsdc_i2c.c
> index 9625d0b1d0b4..726d434c6234 100644
> --- a/drivers/gpu/drm/loongson/lsdc_i2c.c
> +++ b/drivers/gpu/drm/loongson/lsdc_i2c.c
> @@ -177,3 +177,44 @@ int lsdc_create_i2c_chan(struct drm_device
> *ddev,
>  
>         return 0;
>  }
> +
> +static void ls2k1000_put_i2c(struct drm_device *ddev, void *data)
> +{
> +       struct i2c_adapter *adapter = (struct i2c_adapter *)data;
> +
> +       if (adapter)
> +               i2c_put_adapter(adapter);
> +}
> +
> +int ls2k1000_get_i2c(struct drm_device *ddev,
> +                    struct lsdc_display_pipe *dispipe,
> +                    unsigned int index)
> +{
> +       struct i2c_adapter *adapter;
> +       int ret;
> +
> +       /*
> +        * On ls2k1000, display-pipe-0 use i2c-0 and display-pipe-1
> use i2c-1
> +        * by default. We will add more complete DT support for this
> in the
> +        * future.
> +        */
> +       adapter = i2c_get_adapter(index);
> +       if (!adapter) {
> +               drm_dbg(ddev, "DDC drivers are not ready\n");
> +               /*
> +                * Should return -EPROBE_DEFER here, but we want to
> do so when
> +                * the DT support is added. Current we are focus on
> bring up,
> +                * For embedded platform, the i2c adapter is not
> strictly
> +                * required to be able to display something on the
> screen.
> +                */

I assume it could be better to return EPROBE_DEFER when an I2C
controller is explicitly specified in the DT, and silently skip it when
it's not specified.

> +               return 0;
> +       }
> +
> +       ret = drmm_add_action_or_reset(ddev, ls2k1000_put_i2c,
> adapter);
> +       if (ret)
> +               return ret;
> +
> +       dispipe->adapter = adapter;
> +
> +       return 0;
> +}
> diff --git a/drivers/gpu/drm/loongson/lsdc_i2c.h
> b/drivers/gpu/drm/loongson/lsdc_i2c.h
> index 88cd1a1817a5..97eb6227f386 100644
> --- a/drivers/gpu/drm/loongson/lsdc_i2c.h
> +++ b/drivers/gpu/drm/loongson/lsdc_i2c.h
> @@ -26,4 +26,8 @@ int lsdc_create_i2c_chan(struct drm_device *ddev,
>                          struct lsdc_display_pipe *dispipe,
>                          unsigned int index);
>  
> +int ls2k1000_get_i2c(struct drm_device *ddev,
> +                    struct lsdc_display_pipe *dispipe,
> +                    unsigned int index);
> +
>  #endif
> diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.c
> b/drivers/gpu/drm/loongson/lsdc_pixpll.c
> index 2609a2256da4..28290dcd7534 100644
> --- a/drivers/gpu/drm/loongson/lsdc_pixpll.c
> +++ b/drivers/gpu/drm/loongson/lsdc_pixpll.c
> @@ -41,6 +41,132 @@ union lsdc_pixpll_reg_bitmap {
>         u64 d;
>  };
>  
> +/*
> + * The pixel PLL structure of ls2k1000 is defferent than
> ls7a2000/ls2k2000.
> + * it take up 16 bytes, but only a few bits are useful. Sounds like
> a little
> + * bit of wasting register space, but this is the hardware already
> have been
> + * tapped.
> + */
> +union ls2k1000_pixpll_reg_bitmap {
> +       struct ls2k1000_pixpll_reg {
> +               /* Byte 0 ~ Byte 3 */
> +               unsigned sel_out      :  1;  /*  0    select this
> PLL        */
> +               unsigned _reserved_1_ :  1;  /* 
> 1                           */
> +               unsigned sw_adj_en    :  1;  /*  2    allow software
> adjust  */
> +               unsigned bypass       :  1;  /*  3    bypass L1
> PLL          */
> +               unsigned _reserved_2_ :  3;  /* 
> 4:6                         */
> +               unsigned lock_en      :  1;  /*  7    enable lock L1
> PLL     */
> +               unsigned _reserved_3_ :  2;  /* 
> 8:9                         */
> +               unsigned lock_check   :  2;  /* 10:11 precision
> check        */
> +               unsigned _reserved_4_ :  4;  /*
> 12:15                        */
> +
> +               unsigned locked       :  1;  /* 16    PLL locked flag
> bit    */
> +               unsigned _reserved_5_ :  2;  /*
> 17:18                        */
> +               unsigned powerdown    :  1;  /* 19    powerdown the
> pll      */
> +               unsigned _reserved_6_ :  6;  /*
> 20:25                        */
> +               unsigned div_ref      :  6;  /* 26:31 L1
> Prescaler           */
> +
> +               /* Byte 4 ~ Byte 7 */
> +               unsigned loopc        : 10;  /* 32:41 Clock
> Multiplier       */
> +               unsigned l1_div       :  6;  /* 42:47 no
> use                 */
> +               unsigned _reserved_7_ : 16;  /*
> 48:63                        */
> +
> +               /* Byte 8 ~ Byte 15 */
> +               unsigned div_out      :  6;  /* 64 : 69   output clk
> divider */
> +               unsigned _reserved_8_ : 26;  /* 70 : 127  no
> use             */
> +       } bitmap;
> +
> +       u32 w[4];
> +       u64 d[2];
> +};
> +
> +/*
> + * Update the pll parameters to hardware, target to the pixpll in
> ls2k1000
> + *
> + * @this: point to the object from which this function is called
> + * @pin: point to the struct of lsdc_pll_parms passed in
> + *
> + * return 0 if successful.
> + */
> +static int ls2k1000_pixpll_param_update(struct lsdc_pixpll * const
> this,
> +                                       struct lsdc_pixpll_parms
> const *pin)
> +{
> +       void __iomem *reg = this->mmio;
> +       unsigned int counter = 0;
> +       bool locked = false;
> +       u32 val;
> +
> +       val = readl(reg);
> +       /* Bypass the software configured PLL, using refclk directly
> */
> +       val &= ~(1 << 0);
> +       writel(val, reg);
> +
> +       /* Powerdown the PLL */
> +       val |= (1 << 19);
> +       writel(val, reg);
> +
> +       /* Allow the software configuration */
> +       val &= ~(1 << 2);
> +       writel(val, reg);
> +
> +       /* allow L1 PLL lock */
> +       val = (1L << 7) | (3L << 10);
> +       writel(val, reg);
> +
> +       /* clear div_ref bit field */
> +       val &= ~(0x3f << 26);
> +       /* set div_ref bit field */
> +       val |= pin->div_ref << 26;
> +       writel(val, reg);
> +
> +       val = readl(reg + 4);
> +       /* clear loopc bit field */
> +       val &= ~0x0fff;
> +       /* set loopc bit field */
> +       val |= pin->loopc;
> +       writel(val, reg + 4);
> +
> +       /* set div_out */
> +       writel(pin->div_out, reg + 8);
> +
> +       val = readl(reg);
> +       /* use this parms configured just now */
> +       val |= (1 << 2);
> +       /* powerup the PLL */
> +       val &= ~(1 << 19);
> +       writel(val, reg);
> +
> +       /* wait pll setup and locked */
> +       do {
> +               val = readl(reg);
> +               locked = val & 0x10000;
> +               counter++;
> +       } while (!locked && (counter < 2000));
> +
> +       drm_dbg(this->ddev, "%u loop waited\n", counter);
> +
> +       /* Switch to software configured PLL instead of refclk */
> +       val |= 1;
> +       writel(val, reg);
> +
> +       return 0;
> +}
> +
> +static unsigned int
> +ls2k1000_pixpll_get_clock_rate(struct lsdc_pixpll * const this)
> +{
> +       struct lsdc_pixpll_parms *ppar = this->priv;
> +       union ls2k1000_pixpll_reg_bitmap reg_bitmap;
> +       struct ls2k1000_pixpll_reg *obj = &reg_bitmap.bitmap;
> +
> +       reg_bitmap.w[0] = readl(this->mmio);
> +       reg_bitmap.w[1] = readl(this->mmio + 4);
> +       reg_bitmap.w[2] = readl(this->mmio + 8);
> +       reg_bitmap.w[3] = readl(this->mmio + 12);
> +
> +       return ppar->ref_clock / obj->div_ref * obj->loopc / obj-
> >div_out;
> +}
> +
>  struct clk_to_pixpll_parms_lookup_t {
>         unsigned int clock;        /* kHz */
>  
> @@ -452,11 +578,19 @@ static void lsdc_pixpll_print(struct
> lsdc_pixpll * const this,
>  }
>  
>  /*
> - * LS7A1000, LS7A2000 and ls2k2000's pixel pll setting register is
> same,
> + * LS7A1000, LS7A2000 and LS2K2000's pixel PLL register layout is
> same,
>   * we take this as default, create a new instance if a different
> model is
>   * introduced.
>   */
> -static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
> +const struct lsdc_pixpll_funcs ls7a1000_pixpll_funcs = {
> +       .setup = lsdc_pixel_pll_setup,
> +       .compute = lsdc_pixel_pll_compute,
> +       .update = lsdc_pixpll_update,
> +       .get_rate = lsdc_pixpll_get_freq,
> +       .print = lsdc_pixpll_print,
> +};
> +
> +const struct lsdc_pixpll_funcs ls7a2000_pixpll_funcs = {
>         .setup = lsdc_pixel_pll_setup,
>         .compute = lsdc_pixel_pll_compute,
>         .update = lsdc_pixpll_update,
> @@ -464,6 +598,19 @@ static const struct lsdc_pixpll_funcs
> __pixpll_default_funcs = {
>         .print = lsdc_pixpll_print,
>  };
>  
> +/*
> + * The bit fileds of LS2K1000's pixel PLL register are different
> from other
> + * model, due to hardware update(revision). So we introduce a new
> instance
> + * of lsdc_pixpll_funcs object function to gear the control.
> + */
> +const struct lsdc_pixpll_funcs ls2k1000_pixpll_funcs = {
> +       .setup = lsdc_pixel_pll_setup,
> +       .compute = lsdc_pixel_pll_compute,
> +       .update = ls2k1000_pixpll_param_update,
> +       .get_rate = ls2k1000_pixpll_get_clock_rate,
> +       .print = lsdc_pixpll_print,
> +};
> +
>  /* pixel pll initialization */
>  
>  int lsdc_pixpll_init(struct lsdc_pixpll * const this,
> @@ -477,7 +624,7 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const
> this,
>         this->ddev = ddev;
>         this->reg_size = 8;
>         this->reg_base = gfx->conf_reg_base + gfx-
> >pixpll[index].reg_offset;
> -       this->funcs = &__pixpll_default_funcs;
> +       this->funcs = gfx->pixpll_funcs;
>  
>         return this->funcs->setup(this);
>  }
> diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.h
> b/drivers/gpu/drm/loongson/lsdc_pixpll.h
> index ec3486d90ab6..8b6ffd0ef4fb 100644
> --- a/drivers/gpu/drm/loongson/lsdc_pixpll.h
> +++ b/drivers/gpu/drm/loongson/lsdc_pixpll.h
> @@ -83,4 +83,8 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const
> this,
>                      struct drm_device *ddev,
>                      unsigned int index);
>  
> +extern const struct lsdc_pixpll_funcs ls2k1000_pixpll_funcs;
> +extern const struct lsdc_pixpll_funcs ls7a1000_pixpll_funcs;
> +extern const struct lsdc_pixpll_funcs ls7a2000_pixpll_funcs;
> +
>  #endif
> diff --git a/drivers/gpu/drm/loongson/lsdc_probe.c
> b/drivers/gpu/drm/loongson/lsdc_probe.c
> index 48ba69bb8a98..edc3812f0957 100644
> --- a/drivers/gpu/drm/loongson/lsdc_probe.c
> +++ b/drivers/gpu/drm/loongson/lsdc_probe.c
> @@ -54,3 +54,30 @@ unsigned int loongson_cpu_get_prid(u8 *imp, u8
> *rev)
>  
>         return prid;
>  }
> +
> +enum loongson_chip_id loongson_chip_id_fixup(enum loongson_chip_id
> chip_id)
> +{
> +       u8 impl;
> +
> +       if (loongson_cpu_get_prid(&impl, NULL)) {
> +               /*
> +                * LS2K1000 has the LoongArch edition(with two LA264
> CPU core)
> +                * and the Mips edition(with two mips64r2 CPU core),
> Only the
> +                * instruction set of the CPU are changed, the
> peripheral
> +                * devices are basically same.
> +                */
> +               if (chip_id == CHIP_LS7A1000) {
> +#if defined(__loongarch__)
> +                       if (impl == LOONGARCH_CPU_IMP_LS2K1000)
> +                               return CHIP_LS2K1000;
> +#endif
> +
> +#if defined(__mips__)
> +                       if (impl == LOONGSON_CPU_MIPS_IMP_LS2K)
> +                               return CHIP_LS2K1000;
> +#endif
> +               }
> +       }
> +
> +       return chip_id;
> +}
> diff --git a/drivers/gpu/drm/loongson/lsdc_probe.h
> b/drivers/gpu/drm/loongson/lsdc_probe.h
> index 8bb6de2e3c64..8c630c5c90ce 100644
> --- a/drivers/gpu/drm/loongson/lsdc_probe.h
> +++ b/drivers/gpu/drm/loongson/lsdc_probe.h
> @@ -9,4 +9,6 @@
>  /* Helpers for chip detection */
>  unsigned int loongson_cpu_get_prid(u8 *impl, u8 *rev);
>  
> +enum loongson_chip_id loongson_chip_id_fixup(enum loongson_chip_id
> chip_id);
> +
>  #endif
> diff --git a/drivers/gpu/drm/loongson/lsdc_regs.h
> b/drivers/gpu/drm/loongson/lsdc_regs.h
> index e8ea28689c63..c11a9fc0d2f6 100644
> --- a/drivers/gpu/drm/loongson/lsdc_regs.h
> +++ b/drivers/gpu/drm/loongson/lsdc_regs.h
> @@ -39,6 +39,42 @@
>  
>  #define LS7A2000_CONF_REG_BASE          0x10010000
>  
> +/*
> + * The display controller in LS2K1000 SoC is basically same with the
> display
> + * controller in LS7A1000, except that there no built-in gpio
> hardware unit
> + * and no dedicated VRAM.
> + *       ___________________                                    
> _________
> + *      |            -------|                                  
> |         |
> + *      |  CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> |
> Display |
> + *      |  _   _     -------|        ^              ^          
> |_________|
> + *      | | | | |           |        |              |
> + *      | |_| |_|           |     +------+          |
> + *      |                   <---->| i2c0 |<---------+
> + *      |  DC in LS2K1000   |     +------+
> + *      |  _   _            |     +------+
> + *      | | | | |           <---->| i2c1 |----------+
> + *      | |_| |_|           |     +------+          |           
> _________
> + *      |            -------|        |              |          
> |         |
> + *      |  CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> | 
> Panel  |
> + *      |            -------|                                  
> |_________|
> + *      |___________________|
> + *
> + */
> +
> +#if defined(__mips__)
> +#define LS2K1000_CONF_REG_BASE           0x1fe10000    /* mips64r2
> edition */
> +#else
> +#define LS2K1000_CONF_REG_BASE           0x1fe00000    /* LoongArch
> edition */
> +#endif
> +
> +/* The HDA, GPU, DDR share the single DDR PLL */
> +#define LS2K1000_DDR_PLL_REG             0x0490
> +/* The DC and GMAC share the same PLL */
> +#define LS2K1000_DC_PLL_REG              0x04A0
> +
> +#define LS2K1000_PIX0_PLL_REG            0x04B0
> +#define LS2K1000_PIX1_PLL_REG            0x04C0
> +
>  /* For LSDC_CRTCx_CFG_REG */
>  #define CFG_PIX_FMT_MASK                GENMASK(2, 0)
>