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)