Update to 14fc3074cfe039

1. 添加带irq处理的gpio驱动
2. 龙芯2k系列添加mmc驱动支持
3. 添加看门狗驱动
4. dtb支持从command line中的board_name字段判断
5. 支持内存分布描述数据从固件传入的数据获取
6. 支持从bootcmdline获取ramdisk启动的地址和所用大小
7. 添加2k300的i2c总线驱动
8. 2k300使用新的spi 和 spi-io 驱动
9. CPU芯片温度传感器支持2k300的计算方式
10. 添加2k300的adc驱动
11. pwm: update default pwm freq to 50Mhz
12. loongson pwm 驱动支持dts使用clock-frequency形式声明频率
13. loongson pwm 确保disable时输出的电平为空闲电平
14. 2k300编译配置使能pwm leds功能
15. 2k300的gmac的phy-mode默认使用rgmii-id
16. 2k300先锋派设置YT8531的内部延迟
17. YT8531 phy 支持设置led的工作模式
18. LCD屏幕 支持cmdline的panel属性选择某一款板卡的dts配置,实现屏幕切换
19. 添加脚本用于快速选择板卡配置文件
20. 添加CAN驱动
21. 添加音频驱动
22. 优化OTG兼容性

Signed-off-by: maoxiaochuan <maoxiaochuan@loongson.cn>
This commit is contained in:
maoxiaochuan
2025-02-13 16:03:32 +08:00
parent f70d65c829
commit 7ffd18ab9d
107 changed files with 17571 additions and 43 deletions
+2
View File
@@ -7,6 +7,8 @@ config DRM_LOONGSON
select DRM_KMS_HELPER
select DRM_TTM
select DRM_TTM_HELPER
select DRM_BRIDGE_CONNECTOR
select DRM_DISPLAY_HELPER
select I2C
select I2C_ALGOBIT
help
+1
View File
@@ -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 \
@@ -25,6 +25,15 @@ static const struct lsdc_kms_funcs ls7a2000_kms_funcs = {
.crtc_init = ls7a2000_crtc_init,
};
static const struct lsdc_kms_funcs ls2k0300_kms_funcs = {
.create_i2c = lsdc_create_i2c_chan,
.irq_handler = ls7a2000_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,
@@ -35,7 +44,9 @@ static const struct loongson_gfx_desc ls7a1000_gfx = {
.hw_cursor_w = 32,
.hw_cursor_h = 32,
.pitch_align = 256,
.hw_vblank_enable = true,
.has_vblank_counter = false,
.has_dedicated_vram = true,
.funcs = &ls7a1000_kms_funcs,
},
.conf_reg_base = LS7A1000_CONF_REG_BASE,
@@ -43,6 +54,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 +65,7 @@ static const struct loongson_gfx_desc ls7a1000_gfx = {
.reg_size = 8,
},
},
.pixpll_funcs = &ls2k0300_pixpll_funcs,
.chip_id = CHIP_LS7A1000,
.model = "LS7A1000 bridge chipset",
};
@@ -67,7 +80,9 @@ static const struct loongson_gfx_desc ls7a2000_gfx = {
.hw_cursor_w = 64,
.hw_cursor_h = 64,
.pitch_align = 64,
.hw_vblank_enable = true,
.has_vblank_counter = true,
.has_dedicated_vram = true,
.funcs = &ls7a2000_kms_funcs,
},
.conf_reg_base = LS7A2000_CONF_REG_BASE,
@@ -75,6 +90,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,13 +101,51 @@ static const struct loongson_gfx_desc ls7a2000_gfx = {
.reg_size = 8,
},
},
.pixpll_funcs = &ls2k0300_pixpll_funcs,
.chip_id = CHIP_LS7A2000,
.model = "LS7A2000 bridge chipset",
};
static const struct loongson_gfx_desc ls2k0300_gfx = {
.dc = {
.num_of_crtc = 1,
.max_pixel_clk = 200000,
.max_width = 4096,
.max_height = 4096,
.num_of_hw_cursor = 1,
.hw_cursor_w = 32,
.hw_cursor_h = 32,
.pitch_align = 1, // 不需要考虑对齐,对小屏幕不友好
.hw_vblank_enable = false, // 强制不使用 hw_vblank
.has_vblank_counter = false,
.has_dedicated_vram = false,
.funcs = &ls2k0300_kms_funcs,
},
.conf_reg_base = LS2K0300_CFG_REG_BASE,
.gfxpll = {
.reg_offset = LS2K0300_DDR_PLL_REG,
.reg_size = 16 + 16,
},
.gfxpll_funcs = &ls2k1000_gfx_pll_funcs,
.pixpll = {
[0] = {
.reg_offset = LS2K0300_PIX_PLL0_REG,
.reg_size = 16,
},
[1] = {
.reg_offset = LS2K0300_PIX_PLL1_REG,
.reg_size = 16,
},
},
.pixpll_funcs = &ls2k0300_pixpll_funcs,
.chip_id = CHIP_LS2K0300,
.model = "LS2K300 SoC",
};
static const struct lsdc_desc *__chip_id_desc_table[] = {
[CHIP_LS7A1000] = &ls7a1000_gfx.dc,
[CHIP_LS7A2000] = &ls7a2000_gfx.dc,
[CHIP_LS2K0300] = &ls2k0300_gfx.dc,
[CHIP_LS_LAST] = NULL,
};
@@ -4,6 +4,7 @@
*/
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <video/nomodeset.h>
@@ -16,12 +17,14 @@ module_param_named(modeset, loongson_modeset, int, 0400);
int loongson_vblank = 1;
MODULE_PARM_DESC(vblank, "Disable/Enable hw vblank support");
module_param_named(vblank, loongson_vblank, int, 0400);
extern struct platform_driver lsdc_platform_driver;
static int __init loongson_module_init(void)
{
if (!loongson_modeset || video_firmware_drivers_only())
return -ENODEV;
platform_driver_register(&lsdc_platform_driver);
return pci_register_driver(&lsdc_pci_driver);
}
module_init(loongson_module_init);
+345 -17
View File
@@ -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>
@@ -16,6 +17,12 @@
#include <drm/drm_modeset_helper.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_of.h>
#include <drm/drm_bridge_connector.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_reserved_mem.h>
#include "loongson_module.h"
#include "lsdc_drv.h"
@@ -57,6 +64,9 @@ static const struct drm_mode_config_funcs lsdc_mode_config_funcs = {
/* Display related */
static int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc);
static int lsdc_common_probe(struct lsdc_device *ldev);
static int lsdc_modeset_init(struct lsdc_device *ldev,
unsigned int num_crtc,
const struct lsdc_kms_funcs *funcs,
@@ -78,18 +88,31 @@ static int lsdc_modeset_init(struct lsdc_device *ldev,
return ret;
}
for (i = 0; i < num_crtc; i++) {
struct i2c_adapter *ddc = NULL;
dispipe = &ldev->dispipe[i];
if (dispipe->li2c)
ddc = &dispipe->li2c->adapter;
ret = funcs->output_init(ddev, dispipe, ddc, i);
if (ldev->has_ports_node) {
drm_info(ddev, "Has OF graph support\n");
ret = lsdc_attach_output(ldev, num_crtc);
if (ret)
return ret;
}
ldev->num_output++;
/*
* 之前的处理是 OF扫描到 dc之后,就按照ACPI的做法处理
* 这样的话就不会扫到edid(2k300 没i2c去读)
*/
if (!ldev->num_output) {
for (i = 0; i < num_crtc; i++) {
struct i2c_adapter *ddc = NULL;
dispipe = &ldev->dispipe[i];
if (dispipe->li2c)
ddc = &dispipe->li2c->adapter;
ret = funcs->output_init(ddev, dispipe, ddc, i);
if (ret)
return ret;
ldev->num_output++;
}
}
for (i = 0; i < num_crtc; i++) {
@@ -155,8 +178,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,27 +209,78 @@ static int lsdc_get_dedicated_vram(struct lsdc_device *ldev,
return (size > SZ_1M) ? 0 : -ENODEV;
}
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,
lsdc_create_device(struct device *dev,
const struct lsdc_desc *descp,
const struct drm_driver *driver)
const struct drm_driver *driver,
struct pci_dev *pdev,
struct platform_device *pfdev)
{
struct lsdc_device *ldev;
struct drm_device *ddev;
int ret;
ldev = devm_drm_dev_alloc(&pdev->dev, driver, struct lsdc_device, base);
ldev = devm_drm_dev_alloc(dev, driver, struct lsdc_device, base);
if (IS_ERR(ldev))
return ldev;
ldev->dc = pdev;
ldev->descp = descp;
// 先注册了 dev, lsdc_common_probe里面需要扫描 OF
ldev->dev = dev;
ddev = &ldev->base;
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) {
dma_addr_t daddr = 0;
void *vaddr;
ldev->vram_size = 0x900000;
vaddr = dma_alloc_wc(dev, ldev->vram_size, &daddr,
GFP_KERNEL | __GFP_NOWARN);
ret = !vaddr;
ldev->vram_base = daddr;
}
}
if (ret) {
drm_err(ddev, "Init VRAM failed: %d\n", ret);
return ERR_PTR(ret);
@@ -230,12 +303,20 @@ lsdc_create_device(struct pci_dev *pdev,
lsdc_gem_init(ddev);
/* Bar 0 of the DC device contains the MMIO register's base address */
ldev->reg_base = pcim_iomap(pdev, 0, 0);
if (pdev)
ldev->reg_base = pcim_iomap(pdev, 0, 0);
else {
struct resource *res;
res = platform_get_resource(pfdev, IORESOURCE_MEM, 0);
ldev->reg_base = devm_ioremap_resource(dev, res);
}
if (!ldev->reg_base)
return ERR_PTR(-ENODEV);
spin_lock_init(&ldev->reglock);
lsdc_common_probe(ldev);
ret = lsdc_mode_config_init(ddev, descp);
if (ret)
return ERR_PTR(ret);
@@ -281,7 +362,11 @@ static int lsdc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_info(&pdev->dev, "Found %s, revision: %u",
to_loongson_gfx(descp)->model, pdev->revision);
ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
if (!descp->hw_vblank_enable)
loongson_vblank = 0;
// ldev = lsdc_create_device(pdev, descp, &lsdc_drm_driver);
ldev = lsdc_create_device(&pdev->dev, descp, &lsdc_drm_driver, pdev, NULL);
if (IS_ERR(ldev))
return PTR_ERR(ldev);
@@ -460,3 +545,246 @@ MODULE_DEVICE_TABLE(pci, lsdc_pciid_list);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
static const struct of_device_id lsdc_dt_ids[] = {
{ .compatible = "loongson,ls2k0300-dc", .data = (void *)CHIP_LS2K0300, },
{}
};
static int lsdc_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
const struct lsdc_desc *descp = NULL;
const struct of_device_id *of_id;
struct lsdc_device *ldev;
struct drm_device *ddev;
int ret;
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "Failed to set dma mask\n");
return ret;
}
of_id = of_match_device(lsdc_dt_ids, dev);
if (of_id && of_id->data)
descp = lsdc_device_probe(NULL, (enum loongson_chip_id)of_id->data);
if (!descp) {
dev_err(dev, "unknown dc chip core\n");
return -ENOENT;
}
if (!descp->hw_vblank_enable)
loongson_vblank = 0;
ldev = lsdc_create_device(&pdev->dev, descp, &lsdc_drm_driver, NULL, pdev);
if (IS_ERR(ldev))
return PTR_ERR(ldev);
ddev = &ldev->base;
platform_set_drvdata(pdev, ddev);
drm_kms_helper_poll_init(ddev);
if (loongson_vblank) {
int irq;
ret = drm_vblank_init(ddev, descp->num_of_crtc);
if (ret)
return ret;
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq,
descp->funcs->irq_handler,
IRQF_SHARED /* | IRQF_TRIGGER_RISING */,
dev_name(&pdev->dev), ddev);
if (ret) {
drm_err(ddev, "Failed to register interrupt: %d\n", ret);
return ret;
}
drm_info(ddev, "registered irq: %u\n", irq);
}
ret = drm_dev_register(ddev, 0);
if (ret)
return ret;
drm_fbdev_ttm_setup(ddev, 32);
return 0;
}
static void lsdc_platform_remove(struct platform_device *pdev)
{
return ;
}
struct platform_driver lsdc_platform_driver = {
.probe = lsdc_platform_probe,
.remove = lsdc_platform_remove,
.driver = {
.name = "lsdc",
.pm = &lsdc_pm_ops,
.of_match_table = of_match_ptr(lsdc_dt_ids),
},
};
MODULE_DEVICE_TABLE(of, lsdc_dt_ids);
/*
* 从 5.10 移植而来
* 这一套就包括了对 encoder connector (bridge)初始化
* 最终作用于 lsdc_modeset_init 函数对完整链路的扫描(替代了原来的类ACPI的处理)
* 暂时应该不支持 i2c 对 encoder connector 的沟通
* 以后遇到实际情况再修改
*/
static void lsdc_encoder_reset(struct drm_encoder *encoder)
{
return;
}
static const struct drm_encoder_funcs lsdc_encoder_funcs = {
.reset = lsdc_encoder_reset,
.destroy = drm_encoder_cleanup,
};
static int lsdc_attach_bridges(struct lsdc_device *ldev,
struct device_node *ports,
unsigned int i)
{
struct lsdc_display_pipe * const dispipe = &ldev->dispipe[i];
struct drm_device *ddev = &ldev->base;
struct drm_bridge *bridge = NULL;
struct drm_panel *panel = NULL;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct lsdc_output *output;
int ret;
ret = drm_of_find_panel_or_bridge(ports, i, 0, &panel, &bridge);
if (panel) {
bridge = devm_drm_panel_bridge_add_typed(ddev->dev, panel, DRM_MODE_CONNECTOR_DPI);
drm_info(ddev, "output-%u is a DPI panel\n", i);
}
if (!bridge)
return ret;
output = devm_kzalloc(ddev->dev, sizeof(*output), GFP_KERNEL);
if (!output)
return -ENOMEM;
encoder = &output->encoder;
ret = drm_encoder_init(ddev, encoder, &lsdc_encoder_funcs,
DRM_MODE_ENCODER_DPI, "encoder-%u", i);
if (ret) {
drm_err(ddev, "Failed to init encoder: %d\n", ret);
return ret;
}
encoder->possible_crtcs = BIT(i);
ret = drm_bridge_attach(encoder, bridge, NULL, 0);
//ret = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
{
connector = drm_bridge_connector_init(ddev, encoder);
if (IS_ERR(connector)) {
drm_err(ddev, "Unable to init connector\n");
return PTR_ERR(connector);
}
drm_connector_attach_encoder(connector, encoder);
}
drm_info(ddev, "bridge-%u attached to %s\n", i, encoder->name);
dispipe->output = output[0];
return 0;
}
static int lsdc_attach_output(struct lsdc_device *ldev, uint32_t num_crtc)
{
struct drm_device *ddev = &ldev->base;
struct device_node *ports;
struct lsdc_display_pipe *disp;
unsigned int i;
int ret;
ldev->num_output = 0;
ports = of_get_child_by_name(ldev->dev->of_node, "ports");
for (i = 0; i < num_crtc; i++) {
struct drm_bridge *b;
struct drm_panel *p;
disp = &ldev->dispipe[i];
disp->available = false;
ret = drm_of_find_panel_or_bridge(ports, i, 0, &p, &b);
if (ret) {
if (ret == -ENODEV) {
drm_dbg(ddev, "No active panel or bridge for port%u\n", i);
disp->available = false;
continue;
}
if (ret == -EPROBE_DEFER)
drm_dbg(ddev, "Bridge for port%d is defer probed\n", i);
goto RET;
}
disp->available = true;
ldev->num_output++;
}
if (ldev->num_output == 0) {
drm_err(ddev, "No valid output, abort\n");
ret = -ENODEV;
goto RET;
}
for (i = 0; i < num_crtc; i++) {
disp = &ldev->dispipe[i];
if (disp->available) {
ret = lsdc_attach_bridges(ldev, ports, i);
if (ret)
goto RET;
} else {
drm_info(ddev, "output-%u is not available\n", i);
}
}
drm_info(ddev, "number of outputs: %u\n", ldev->num_output);
RET:
of_node_put(ports);
return ret;
}
static void lsdc_of_probe(struct lsdc_device *ldev, struct device_node *np)
{
struct device_node *ports;
if (!np) {
ldev->has_ports_node = false;
dev_info(ldev->dev, "don't has DT support\n");
return;
}
ports = of_get_child_by_name(np, "ports");
ldev->has_ports_node = ports ? true : false;
of_node_put(ports);
}
static int lsdc_common_probe(struct lsdc_device *ldev)
{
lsdc_of_probe(ldev, ldev->dev->of_node);
return 0;
}
+12
View File
@@ -41,6 +41,7 @@
enum loongson_chip_id {
CHIP_LS7A1000 = 0,
CHIP_LS7A2000 = 1,
CHIP_LS2K0300 = 2,
CHIP_LS_LAST,
};
@@ -60,7 +61,9 @@ struct lsdc_desc {
u32 hw_cursor_w;
u32 hw_cursor_h;
u32 pitch_align; /* CRTC DMA alignment constraint */
bool hw_vblank_enable;
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 +81,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;
enum loongson_chip_id chip_id;
char model[64];
@@ -191,6 +196,8 @@ struct lsdc_display_pipe {
struct lsdc_output output;
struct lsdc_i2c *li2c;
unsigned int index;
bool available;
};
static inline struct lsdc_display_pipe *
@@ -263,6 +270,7 @@ struct lsdc_gem {
};
struct lsdc_device {
struct device *dev;
struct drm_device base;
struct ttm_device bdev;
@@ -295,6 +303,10 @@ struct lsdc_device {
/* @num_output: count the number of active display pipe */
unsigned int num_output;
/* @has_ports_node: true if there are OF graph in the DT */
bool has_ports_node;
};
static inline struct lsdc_device *tdev_to_ldev(struct ttm_device *bdev)
+9 -2
View File
@@ -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)) {
+5
View File
@@ -49,4 +49,9 @@ struct loongson_gfxpll {
int loongson_gfxpll_create(struct drm_device *ddev,
struct loongson_gfxpll **ppout);
extern const struct loongson_gfxpll_funcs ls2k0300_gfx_pll_funcs;
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
@@ -0,0 +1,140 @@
// 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,
};
+101 -3
View File
@@ -41,6 +41,26 @@ union lsdc_pixpll_reg_bitmap {
u64 d;
};
/* u32 */
union ls2k0300_pixpll_reg_bitmap {
struct ls2k0300_pixpll_bitmap {
unsigned sel_out : 1;
unsigned reserved_1 : 2;
unsigned sw_adj_en : 1; /* allow software adjust */
unsigned bypass : 1; /* bypass L1 PLL */
unsigned powerdown : 1; /* write 1 to powerdown the PLL */
unsigned reserved_2 : 1;
unsigned locked : 1; /* 7 Is L1 PLL locked, read only */
unsigned div_ref : 7; /* 8:14 ref clock divider */
unsigned loopc : 9; /* 15:23 Clock Multiplier */
unsigned div_out : 7; /* 24:30 output clock divider */
unsigned reserved_4 : 1; /* 31 */
} bitmap;
u32 w[4];
u64 d[2];
};
struct clk_to_pixpll_parms_lookup_t {
unsigned int clock; /* kHz */
@@ -452,11 +472,11 @@ 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,
@@ -464,6 +484,84 @@ static const struct lsdc_pixpll_funcs __pixpll_default_funcs = {
.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,
.get_rate = lsdc_pixpll_get_freq,
.print = lsdc_pixpll_print,
};
static int ls2k0300_pixpll_param_update(struct lsdc_pixpll * const this,
struct lsdc_pixpll_parms const *pin)
{
void __iomem *reg = this->mmio;
unsigned int counter = 0;
unsigned long val;
bool locked;
/* set sel_pll_out0 0 */
val = readl(reg);
val &= ~(1UL << 0);
writel(val, reg);
/* bypass */
val |= (1UL << 4);
/* allow software setting the PLL */
val |= (1UL << 3);
writel(val, reg);
/* pll powerdown */
val = readl(reg);
val |= (1UL << 5);
writel(val, reg);
val = (pin->div_out << 24) |
(pin->loopc << 15) |
(pin->div_ref << 8);
writel(val, reg);
/* unbypass */
val &= ~(1UL << 4);
/* power up */
val &= ~(1UL << 5);
writel(val, reg);
/* wait pll setup and locked */
do {
val = readl(reg);
locked = val & 0x80;
counter++;
} while (locked == false);
DRM_DEBUG_DRIVER("%u loop waited\n", counter);
/* select PIX0 */
writel((val | 1), reg);
return 0;
}
static unsigned int
ls2k0300_pixpll_get_clock_rate(struct lsdc_pixpll * const this)
{
struct lsdc_pixpll_parms *ppar = this->priv;
union ls2k0300_pixpll_reg_bitmap reg_bitmap;
struct ls2k0300_pixpll_bitmap *obj = &reg_bitmap.bitmap;
reg_bitmap.w[0] = readl(this->mmio);
return ppar->ref_clock / obj->div_ref * obj->loopc / obj->div_out;
}
const struct lsdc_pixpll_funcs ls2k0300_pixpll_funcs = {
.setup = lsdc_pixel_pll_setup,
.compute = lsdc_pixel_pll_compute,
.update = ls2k0300_pixpll_param_update,
.get_rate = ls2k0300_pixpll_get_clock_rate,
.print = lsdc_pixpll_print,
};
/* pixel pll initialization */
int lsdc_pixpll_init(struct lsdc_pixpll * const this,
@@ -477,7 +575,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);
}
+5
View File
@@ -83,4 +83,9 @@ int lsdc_pixpll_init(struct lsdc_pixpll * const this,
struct drm_device *ddev,
unsigned int index);
extern const struct lsdc_pixpll_funcs ls2k0300_pixpll_funcs;
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
+6
View File
@@ -42,6 +42,12 @@
/* For LSDC_CRTCx_CFG_REG */
#define CFG_PIX_FMT_MASK GENMASK(2, 0)
/* LS2K0300 */
#define LS2K0300_DDR_PLL_REG 0x0408
#define LS2K0300_PIX_PLL0_REG 0x0410
#define LS2K0300_PIX_PLL1_REG 0x0414
#define LS2K0300_CFG_REG_BASE 0x16000000
enum lsdc_pixel_format {
LSDC_PF_NONE = 0,
LSDC_PF_XRGB444 = 1, /* [12 bits] */
+62
View File
@@ -123,8 +123,62 @@ static const struct drm_panel_funcs panel_lvds_funcs = {
.get_orientation = panel_lvds_get_orientation,
};
static char cmdline_panel_buf[128];
extern char* saved_command_line;
static int get_panel_by_cmdline(void)
{
char* cmdline_part;
cmdline_part = strstr(saved_command_line, "panel=");
if (cmdline_part) {
memset(cmdline_panel_buf, 0, 128);
sscanf(cmdline_part, "panel=%s", cmdline_panel_buf);
cmdline_panel_buf[127] = 0;
}
if (!strcmp(cmdline_panel_buf, "default"))
return 2;
return 0;
}
static int cmpare_panel_name_cmdline(struct device_node *panel)
{
const char* label = NULL;
of_property_read_string(panel, "label", &label);
if (!label)
return -EINVAL;
return strcmp(label, cmdline_panel_buf);
}
static struct device_node* panel_lvds_cmdline_match_dt(void)
{
struct device_node *panel_set_node;
struct device_node *child;
panel_set_node = of_find_node_by_name(NULL, "panel_param_set");
if (!panel_set_node)
return NULL;
child = NULL;
do {
child = of_get_next_child(panel_set_node, child);
if (!child)
break;
if (!cmpare_panel_name_cmdline(child)) {
pr_info("cmdline panel property(%s) select ofnode is %s\n", cmdline_panel_buf, child->full_name);
return child;
}
} while(child);
return NULL;
}
static int panel_lvds_parse_dt(struct panel_lvds *lvds)
{
struct device_node *panel_by_cmdline = NULL;
struct device_node *np = lvds->dev->of_node;
int ret;
@@ -134,6 +188,14 @@ static int panel_lvds_parse_dt(struct panel_lvds *lvds)
return ret;
}
cmdline_panel_buf[0] = 0;
ret = get_panel_by_cmdline();
if (!ret)
panel_by_cmdline = panel_lvds_cmdline_match_dt();
if (panel_by_cmdline)
np = panel_by_cmdline;
ret = of_get_drm_panel_display_mode(np, &lvds->dmode, &lvds->bus_flags);
if (ret < 0) {
dev_err(lvds->dev, "%pOF: problems parsing panel-timing (%d)\n",