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 = ®_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)
>