2026-01-29 22:25:33 +08:00

308 lines
8.2 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Phytium display engine DRM driver
*
* Copyright (C) 2021-2023, Phytium Technology Co., Ltd.
*/
#include <linux/of_device.h>
#include <linux/acpi.h>
#include <drm/drm_drv.h>
#include <linux/dma-mapping.h>
#include "phytium_display_drv.h"
#include "phytium_platform.h"
#include "phytium_dp.h"
#include "phytium_gem.h"
#include "pe220x_dc.h"
#include "pe220x_dp.h"
int phytium_platform_carveout_mem_init(struct platform_device *pdev,
struct phytium_display_private *priv)
{
struct resource *res;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (res) {
priv->pool_size = resource_size(res);
priv->pool_phys_addr = res->start;
}
if ((priv->pool_phys_addr != 0) && (priv->pool_size != 0)) {
priv->pool_virt_addr = ioremap_cache(priv->pool_phys_addr, priv->pool_size);
if (priv->pool_virt_addr == NULL) {
DRM_ERROR("failed to remap carveout mem(0x%llx)\n", priv->pool_phys_addr);
ret = -EINVAL;
goto failed_ioremap;
}
ret = phytium_memory_pool_init(&pdev->dev, priv);
if (ret)
goto failed_init_memory_pool;
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = priv->pool_size;
priv->support_memory_type = MEMORY_TYPE_SYSTEM_CARVEOUT;
priv->vram_hw_init = NULL;
} else {
DRM_DEBUG_KMS("not support carveout memory\n");
priv->mem_state[PHYTIUM_MEM_SYSTEM_CARVEOUT_TOTAL] = 0;
priv->support_memory_type = MEMORY_TYPE_SYSTEM_UNIFIED;
priv->vram_hw_init = NULL;
}
return 0;
failed_init_memory_pool:
iounmap(priv->pool_virt_addr);
failed_ioremap:
return ret;
}
void phytium_platform_carveout_mem_fini(struct platform_device *pdev,
struct phytium_display_private *priv)
{
if (priv->support_memory_type == MEMORY_TYPE_SYSTEM_CARVEOUT) {
phytium_memory_pool_fini(&pdev->dev, priv);
iounmap(priv->pool_virt_addr);
}
}
static struct phytium_display_private *
phytium_platform_private_init(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct device_node *node;
struct fwnode_handle *np;
struct phytium_display_private *priv = NULL;
struct phytium_platform_private *platform_priv = NULL;
struct phytium_device_info *phytium_info = NULL;
int i = 0, ret = 0;
struct resource *res;
platform_priv = devm_kzalloc(&pdev->dev, sizeof(*platform_priv), GFP_KERNEL);
if (!platform_priv) {
DRM_ERROR("no memory to allocate for phytium_platform_private\n");
goto exit;
}
memset(platform_priv, 0, sizeof(*platform_priv));
priv = &platform_priv->base;
phytium_display_private_init(priv, dev);
if (pdev->dev.of_node) {
phytium_info = (struct phytium_device_info *)of_device_get_match_data(&pdev->dev);
if (!phytium_info) {
DRM_ERROR("failed to get dts id data(phytium_info)\n");
goto failed;
}
memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info));
node = pdev->dev.of_node;
ret = of_property_read_u8(node, "pipe_mask", &priv->info.pipe_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing pipe_mask property from dts\n");
goto failed;
}
ret = of_property_read_u8(node, "edp_mask", &priv->info.edp_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing edp_mask property from dts\n");
goto failed;
}
} else if (has_acpi_companion(&pdev->dev)) {
phytium_info = (struct phytium_device_info *)acpi_device_get_match_data(&pdev->dev);
if (!phytium_info) {
DRM_ERROR("failed to get acpi id data(phytium_info)\n");
goto failed;
}
memcpy(&(priv->info), phytium_info, sizeof(struct phytium_device_info));
np = dev_fwnode(&(pdev->dev));
ret = fwnode_property_read_u8(np, "pipe_mask", &priv->info.pipe_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing pipe_mask property from acpi\n");
goto failed;
}
ret = fwnode_property_read_u8(np, "edp_mask", &priv->info.edp_mask);
if (ret < 0) {
dev_err(&pdev->dev, "missing edp_mask property from acpi\n");
goto failed;
}
}
priv->info.num_pipes = 0;
for_each_pipe_masked(priv, i)
priv->info.num_pipes++;
if (priv->info.num_pipes == 0) {
DRM_ERROR("num_pipes is zero, so exit init\n");
goto failed;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->regs = devm_ioremap_resource(&pdev->dev, res);
if (priv->regs == NULL) {
DRM_ERROR("ioremap fail, addr:0x%llx, size:0x%llx\n", res->start, res->end);
goto failed;
}
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
dev_err(&pdev->dev, "failed to get irq\n");
goto failed;
}
if (IS_PE220X(priv)) {
priv->dc_hw_clear_msi_irq = NULL;
priv->dc_hw_fb_format_check = pe220x_dc_hw_fb_format_check;
}
return priv;
failed:
devm_kfree(&pdev->dev, platform_priv);
exit:
return NULL;
}
static void phytium_platform_private_fini(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
struct phytium_platform_private *platform_priv = to_platform_priv(priv);
devm_kfree(&pdev->dev, platform_priv);
}
static int phytium_platform_probe(struct platform_device *pdev)
{
struct phytium_display_private *priv = NULL;
struct drm_device *dev = NULL;
int ret = 0;
dev = drm_dev_alloc(&phytium_display_drm_driver, &pdev->dev);
if (IS_ERR(dev)) {
DRM_ERROR("failed to allocate drm_device\n");
return PTR_ERR(dev);
}
dev_set_drvdata(&pdev->dev, dev);
dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
priv = phytium_platform_private_init(pdev);
if (priv)
dev->dev_private = priv;
else
goto failed_platform_private_init;
ret = phytium_platform_carveout_mem_init(pdev, priv);
if (ret) {
DRM_ERROR("failed to init system carveout memory\n");
goto failed_carveout_mem_init;
}
ret = drm_dev_register(dev, 0);
if (ret) {
DRM_ERROR("failed to register drm dev\n");
goto failed_register_drm;
}
phytium_dp_hpd_irq_setup(dev, true);
return 0;
failed_register_drm:
phytium_platform_carveout_mem_fini(pdev, priv);
failed_carveout_mem_init:
phytium_platform_private_fini(pdev);
failed_platform_private_init:
dev_set_drvdata(&pdev->dev, NULL);
drm_dev_put(dev);
return -1;
}
static int phytium_platform_remove(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
phytium_dp_hpd_irq_setup(dev, false);
cancel_work_sync(&priv->hotplug_work);
drm_dev_unregister(dev);
phytium_platform_private_fini(pdev);
dev_set_drvdata(&pdev->dev, NULL);
drm_dev_put(dev);
return 0;
}
static void phytium_platform_shutdown(struct platform_device *pdev)
{
struct drm_device *dev = dev_get_drvdata(&pdev->dev);
struct phytium_display_private *priv = dev->dev_private;
priv->display_shutdown(dev);
}
static int phytium_platform_pm_suspend(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct phytium_display_private *priv = drm_dev->dev_private;
return priv->display_pm_suspend(drm_dev);
}
static int phytium_platform_pm_resume(struct device *dev)
{
struct drm_device *drm_dev = dev_get_drvdata(dev);
struct phytium_display_private *priv = drm_dev->dev_private;
return priv->display_pm_resume(drm_dev);
}
static const struct dev_pm_ops phytium_platform_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(phytium_platform_pm_suspend, phytium_platform_pm_resume)
};
static const struct phytium_device_info pe220x_info = {
.platform_mask = BIT(PHYTIUM_PLATFORM_PE220X),
.total_pipes = 2,
.crtc_clock_max = PE220X_DC_PIX_CLOCK_MAX,
.hdisplay_max = PE220X_DC_HDISPLAY_MAX,
.vdisplay_max = PE220X_DC_VDISPLAY_MAX,
.address_mask = PE220X_DC_ADDRESS_MASK,
.backlight_max = PE220X_DP_BACKLIGHT_MAX,
};
static const struct of_device_id display_of_match[] = {
{
.compatible = "phytium,dc",
.data = &pe220x_info,
},
{ }
};
#ifdef CONFIG_ACPI
static const struct acpi_device_id display_acpi_ids[] = {
{
.id = "PHYT0015",
.driver_data = (kernel_ulong_t)&pe220x_info,
},
{},
};
MODULE_DEVICE_TABLE(acpi, display_acpi_ids);
#else
#define display_acpi_ids NULL
#endif
struct platform_driver phytium_platform_driver = {
.driver = {
.name = "phytium_display_platform",
.of_match_table = of_match_ptr(display_of_match),
.acpi_match_table = ACPI_PTR(display_acpi_ids),
},
.probe = phytium_platform_probe,
.remove = phytium_platform_remove,
.shutdown = phytium_platform_shutdown,
.driver.pm = &phytium_platform_pm_ops,
};