@@ -571,6 +571,10 @@ int etnaviv_drm_bind(struct device *dev, bool component)
if (ret < 0)
goto out_free_priv;
+ ret = etnaviv_register_irq_handler(dev, priv);
+ if (ret)
+ goto out_unbind;
+
load_gpu(drm);
ret = drm_dev_register(drm, 0);
@@ -623,7 +627,7 @@ static void etnaviv_master_unbind(struct device *dev)
return etnaviv_drm_unbind(dev, true);
}
-static const struct component_master_ops etnaviv_master_ops = {
+const struct component_master_ops etnaviv_master_ops = {
.bind = etnaviv_master_bind,
.unbind = etnaviv_master_unbind,
};
@@ -701,16 +705,36 @@ static struct platform_driver etnaviv_platform_driver = {
},
};
-static int etnaviv_create_platform_device(const char *name,
- struct platform_device **ppdev)
+int etnaviv_create_platform_device(struct device *parent,
+ const char *name, int id,
+ struct resource *pres,
+ void *data,
+ struct platform_device **ppdev)
{
struct platform_device *pdev;
int ret;
- pdev = platform_device_alloc(name, PLATFORM_DEVID_NONE);
+ pdev = platform_device_alloc(name, id);
if (!pdev)
return -ENOMEM;
+ pdev->dev.parent = parent;
+
+ if (pres) {
+ ret = platform_device_add_resources(pdev, pres, 1);
+ if (ret) {
+ platform_device_put(pdev);
+ return ret;
+ }
+ }
+
+ if (data) {
+ void *pdata = kmalloc(sizeof(void *), GFP_KERNEL);
+
+ *(void **)pdata = data;
+ pdev->dev.platform_data = pdata;
+ }
+
ret = platform_device_add(pdev);
if (ret) {
platform_device_put(pdev);
@@ -763,7 +787,10 @@ static int __init etnaviv_init(void)
if (np) {
of_node_put(np);
- ret = etnaviv_create_platform_device("etnaviv", &etnaviv_drm);
+ ret = etnaviv_create_platform_device(NULL, "etnaviv",
+ PLATFORM_DEVID_NONE,
+ NULL, NULL,
+ &etnaviv_drm);
if (ret)
goto unregister_platform_driver;
}
@@ -9,6 +9,7 @@
#include <linux/io.h>
#include <linux/list.h>
#include <linux/mm_types.h>
+#include <linux/platform_device.h>
#include <linux/sizes.h>
#include <linux/time64.h>
#include <linux/types.h>
@@ -98,8 +99,15 @@ bool etnaviv_cmd_validate_one(struct etnaviv_gpu *gpu,
u32 *stream, unsigned int size,
struct drm_etnaviv_gem_submit_reloc *relocs, unsigned int reloc_size);
+int etnaviv_create_platform_device(struct device *parent,
+ const char *name, int id,
+ struct resource *pres,
+ void *data,
+ struct platform_device **ppdev);
+
int etnaviv_drm_bind(struct device *dev, bool component);
void etnaviv_drm_unbind(struct device *dev, bool component);
+extern const struct component_master_ops etnaviv_master_ops;
#ifdef CONFIG_DEBUG_FS
void etnaviv_gem_describe_objects(struct etnaviv_drm_private *priv,
@@ -20,6 +20,7 @@
#include "etnaviv_gpu.h"
#include "etnaviv_gem.h"
#include "etnaviv_mmu.h"
+#include "etnaviv_pci_drv.h"
#include "etnaviv_perfmon.h"
#include "etnaviv_sched.h"
#include "common.xml.h"
@@ -28,6 +29,7 @@
#include "cmdstream.xml.h"
static const struct platform_device_id gpu_ids[] = {
+ { .name = "etnaviv-gpu,3d" },
{ .name = "etnaviv-gpu,2d" },
{ },
};
@@ -1871,6 +1873,33 @@ static const struct of_device_id etnaviv_gpu_match[] = {
};
MODULE_DEVICE_TABLE(of, etnaviv_gpu_match);
+/* dev is a pointer which point to the master device */
+int etnaviv_register_irq_handler(struct device *dev,
+ struct etnaviv_drm_private *priv)
+{
+ int ret;
+ int i;
+
+ if (dev_is_platform(dev)) {
+ for (i = 0; i < priv->num_gpus; i++) {
+ struct etnaviv_gpu *gpu = priv->gpu[i];
+
+ ret = devm_request_irq(dev, gpu->irq, irq_handler, 0,
+ dev_name(dev), priv);
+ if (ret) {
+ dev_err(dev, "failed to request IRQ handler: %d\n", ret);
+ return ret;
+ }
+
+ dev_info(dev, "GPU IRQ number: %d\n", gpu->irq);
+ }
+ } else if (IS_ENABLED(CONFIG_DRM_ETNAVIV_PCI_DRIVER)) {
+ ret = etnaviv_pci_register_irq_handler(dev, irq_handler, (void *)priv);
+ }
+
+ return ret;
+}
+
int etnaviv_gpu_driver_create(struct device *dev, void __iomem *mmio,
int irq, bool component, bool has_clk)
{
@@ -1886,16 +1915,7 @@ int etnaviv_gpu_driver_create(struct device *dev, void __iomem *mmio,
mutex_init(&gpu->lock);
mutex_init(&gpu->sched_lock);
- /* Get Interrupt: */
gpu->irq = irq;
- if (gpu->irq < 0)
- return gpu->irq;
-
- err = devm_request_irq(dev, irq, irq_handler, 0, dev_name(dev), gpu);
- if (err) {
- dev_err(dev, "failed to request IRQ%u: %d\n", gpu->irq, err);
- return err;
- }
/* Get Clocks: */
if (has_clk) {
@@ -1989,7 +2009,9 @@ static const struct dev_pm_ops etnaviv_gpu_pm_ops = {
static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ void **pp = (void **)dev->platform_data;
void __iomem *mmio;
+ bool has_clk;
int irq;
/* Map registers: */
@@ -1997,9 +2019,18 @@ static int etnaviv_gpu_platform_probe(struct platform_device *pdev)
if (IS_ERR(mmio))
return PTR_ERR(mmio);
- irq = platform_get_irq(pdev, 0);
+ if (pp && dev->parent && IS_ENABLED(CONFIG_DRM_ETNAVIV_PCI_DRIVER)) {
+ has_clk = etnaviv_pci_platform_has_clk(dev, *pp);
+ irq = etnaviv_pci_platform_get_irq_line(dev, *pp);
+ } else {
+ has_clk = true;
+ irq = platform_get_irq(pdev, 0);
+ }
+
+ if (irq < 0)
+ return irq;
- return etnaviv_gpu_driver_create(dev, mmio, irq, true, true);
+ return etnaviv_gpu_driver_create(dev, mmio, irq, true, has_clk);
}
static int etnaviv_gpu_platform_remove(struct platform_device *pdev)
@@ -215,6 +215,9 @@ void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);
int etnaviv_gpu_wait_idle(struct etnaviv_gpu *gpu, unsigned int timeout_ms);
void etnaviv_gpu_start_fe(struct etnaviv_gpu *gpu, u32 address, u16 prefetch);
+int etnaviv_register_irq_handler(struct device *dev,
+ struct etnaviv_drm_private *priv);
+
int etnaviv_gpu_bind(struct device *dev, struct device *master, void *data);
void etnaviv_gpu_unbind(struct device *dev, struct device *master, void *data);
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: GPL-2.0
+#include <linux/component.h>
#include <linux/pci.h>
#include "etnaviv_drv.h"
#include "etnaviv_gpu.h"
#include "etnaviv_pci_drv.h"
+int etnaviv_component = -1;
+MODULE_PARM_DESC(component, "Force to use component subsystem");
+module_param_named(component, etnaviv_component, int, 0644);
+
enum etnaviv_pci_gpu_chip_id {
GC_CORE_UNKNOWN = 0,
JM9100 = 1,
@@ -103,11 +108,58 @@ gc_core_plaform_data[GC_CORE_PCI_LAST] = {
},
};
+/* dev is a pointer which point to the sub gpu device */
+bool etnaviv_pci_platform_has_clk(struct device *dev, void *data)
+{
+ struct etnaviv_pci_gpu_platform_data *pdata = data;
+
+ if (pdata && pdata->no_clk)
+ return false;
+
+ return true;
+}
+
+/* dev is a pointer which point to the sub gpu device */
+int etnaviv_pci_platform_get_irq_line(struct device *dev, void *data)
+{
+ struct etnaviv_pci_gpu_platform_data *pdata = data;
+
+ if (!dev_is_pci(dev->parent))
+ return -ENODEV;
+
+ if (pdata && pdata->share_irq) {
+ struct pci_dev *parent;
+ int irq;
+
+ parent = to_pci_dev(dev->parent);
+ irq = parent->irq;
+ return irq;
+ }
+
+ return -ENODEV;
+}
+
+/* dev is a pointer which point to the master device */
+int etnaviv_pci_register_irq_handler(struct device *dev,
+ irqreturn_t (*fn)(int irq, void *data),
+ void *data)
+{
+ struct pci_dev *pdev;
+
+ if (!dev_is_pci(dev))
+ return -ENODEV;
+
+ pdev = to_pci_dev(dev);
+
+ return devm_request_irq(dev, pdev->irq, fn, 0, dev_name(dev), data);
+}
+
static int etnaviv_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
enum etnaviv_pci_gpu_chip_id chip_id = ent->driver_data;
struct device *dev = &pdev->dev;
+ bool use_component = false;
const struct etnaviv_pci_gpu_platform_data *pdata;
void __iomem *mmio;
int ret;
@@ -126,18 +178,65 @@ static int etnaviv_pci_probe(struct pci_dev *pdev,
pdata = &gc_core_plaform_data[chip_id];
- /* PCI bar 0 contain the MMIO registers */
- mmio = pcim_iomap(pdev, pdata->mmio_bar, 0);
- if (IS_ERR(mmio))
- return PTR_ERR(mmio);
+ if (etnaviv_component >= 1)
+ use_component = true;
+ else if (etnaviv_component == 0)
+ use_component = false;
+ else
+ use_component = pdata->num_core > 1 ? true : false;
- mmio += pdata->cores[0].offset;
+ if (use_component) {
+ unsigned int n_core = pdata->num_core;
+ struct component_match *matches = NULL;
+ unsigned int i;
- ret = etnaviv_gpu_driver_create(dev, mmio, pdev->irq, false, false);
- if (ret)
- return ret;
+ dev_info(dev, "%s has %u GPU cores\n", pdata->name, n_core);
+
+ /* Create a virtual platform device for sub-component core */
+ for (i = 0; i < n_core; i++) {
+ resource_size_t start, offset, size;
+ struct platform_device *sub_gpu_v;
+ struct resource res;
+
+ start = pci_resource_start(pdev, pdata->mmio_bar);
+ offset = pdata->cores[i].offset;
+ size = pdata->cores[i].size;
+
+ memset(&res, 0, sizeof(res));
+ res.flags = IORESOURCE_MEM;
+ res.name = "reg";
+ res.start = start + offset;
+ res.end = start + offset + size - 1;
+
+ ret = etnaviv_create_platform_device(dev,
+ pdata->cores[i].name,
+ pdata->cores[i].id,
+ &res,
+ (void *)pdata,
+ &sub_gpu_v);
+ if (ret)
+ return ret;
+
+ component_match_add(dev, &matches, component_compare_dev,
+ &sub_gpu_v->dev);
+ }
+
+ ret = component_master_add_with_match(dev, &etnaviv_master_ops, matches);
+ } else {
+ mmio = pcim_iomap(pdev, pdata->mmio_bar, 0);
+ if (IS_ERR(mmio))
+ return PTR_ERR(mmio);
+
+ mmio += pdata->cores[0].offset;
+
+ ret = etnaviv_gpu_driver_create(dev, mmio, pdev->irq, false, false);
+ if (ret)
+ return ret;
+
+ ret = etnaviv_drm_bind(dev, false);
+ }
- return etnaviv_drm_bind(dev, false);
+ return ret;
}
static void etnaviv_pci_remove(struct pci_dev *pdev)
@@ -3,16 +3,35 @@
#ifndef __ETNAVIV_PCI_DRV_H__
#define __ETNAVIV_PCI_DRV_H__
+#include <linux/irqreturn.h>
+
#ifdef CONFIG_DRM_ETNAVIV_PCI_DRIVER
int etnaviv_register_pci_driver(void);
void etnaviv_unregister_pci_driver(void);
+bool etnaviv_pci_platform_has_clk(struct device *dev, void *data);
+int etnaviv_pci_platform_get_irq_line(struct device *dev, void *data);
+int etnaviv_pci_register_irq_handler(struct device *dev,
+ irqreturn_t (*fn)(int irq, void *data),
+ void *data);
+
#else
static inline int etnaviv_register_pci_driver(void) { return 0; }
static inline void etnaviv_unregister_pci_driver(void) { }
+static inline bool
+etnaviv_pci_platform_has_clk(struct device *dev, void *data) { return true; }
+
+static inline int
+etnaviv_pci_platform_get_irq_line(struct device *dev, void *data) { return 0; }
+
+static inline int
+etnaviv_pci_register_irq_handler(struct device *dev,
+ irqreturn_t (*fn)(int irq, void *data),
+ void *data) { return 0; }
+
#endif
#endif