mirror of
https://gitee.com/open-loongarch/linux-6.12.git
synced 2026-10-28 22:17:11 +08:00
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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 = ®_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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] */
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user