| From 060530f1ea6740eb767085008d183f89ccdd289c Mon Sep 17 00:00:00 2001 |
| From: Rob Clark <robdclark@gmail.com> |
| Date: Mon, 3 Mar 2014 14:19:12 -0500 |
| Subject: drm/msm: use componentised device support |
| |
| From: Rob Clark <robdclark@gmail.com> |
| |
| commit 060530f1ea6740eb767085008d183f89ccdd289c upstream. |
| |
| |
| Signed-off-by: Rob Clark <robdclark@gmail.com> |
| [Guenter Roeck: backported to 3.14] |
| Signed-off-by: Guenter Roeck <linux@roeck-us.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| Please consider adding this patch to v3.14, to fix a build error |
| seen with arm:allmodconfig. |
| |
| Rob, it would be great if you can have a look to ensure that I did not miss |
| anything. |
| |
| Build results: |
| total: 125 pass: 125 fail: 0 |
| |
| Qemu test results: |
| total: 30 pass: 30 fail: 0 |
| |
| |
| drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 49 ++++++++++---- |
| drivers/gpu/drm/msm/hdmi/hdmi.c | 42 +++++++++--- |
| drivers/gpu/drm/msm/msm_drv.c | 112 ++++++++++++++++++++++++++++++++-- |
| drivers/gpu/drm/msm/msm_drv.h | 4 + |
| 4 files changed, 176 insertions(+), 31 deletions(-) |
| |
| --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c |
| +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c |
| @@ -35,8 +35,6 @@ |
| A3XX_INT0_CP_AHB_ERROR_HALT | \ |
| A3XX_INT0_UCHE_OOB_ACCESS) |
| |
| -static struct platform_device *a3xx_pdev; |
| - |
| static void a3xx_me_init(struct msm_gpu *gpu) |
| { |
| struct msm_ringbuffer *ring = gpu->rb; |
| @@ -311,7 +309,6 @@ static void a3xx_destroy(struct msm_gpu |
| ocmem_free(OCMEM_GRAPHICS, a3xx_gpu->ocmem_hdl); |
| #endif |
| |
| - put_device(&a3xx_gpu->pdev->dev); |
| kfree(a3xx_gpu); |
| } |
| |
| @@ -439,7 +436,8 @@ struct msm_gpu *a3xx_gpu_init(struct drm |
| struct a3xx_gpu *a3xx_gpu = NULL; |
| struct adreno_gpu *adreno_gpu; |
| struct msm_gpu *gpu; |
| - struct platform_device *pdev = a3xx_pdev; |
| + struct msm_drm_private *priv = dev->dev_private; |
| + struct platform_device *pdev = priv->gpu_pdev; |
| struct adreno_platform_config *config; |
| int ret; |
| |
| @@ -460,7 +458,6 @@ struct msm_gpu *a3xx_gpu_init(struct drm |
| adreno_gpu = &a3xx_gpu->base; |
| gpu = &adreno_gpu->base; |
| |
| - get_device(&pdev->dev); |
| a3xx_gpu->pdev = pdev; |
| |
| gpu->fast_rate = config->fast_rate; |
| @@ -522,17 +519,24 @@ fail: |
| # include <mach/kgsl.h> |
| #endif |
| |
| -static int a3xx_probe(struct platform_device *pdev) |
| +static void set_gpu_pdev(struct drm_device *dev, |
| + struct platform_device *pdev) |
| +{ |
| + struct msm_drm_private *priv = dev->dev_private; |
| + priv->gpu_pdev = pdev; |
| +} |
| + |
| +static int a3xx_bind(struct device *dev, struct device *master, void *data) |
| { |
| static struct adreno_platform_config config = {}; |
| #ifdef CONFIG_OF |
| - struct device_node *child, *node = pdev->dev.of_node; |
| + struct device_node *child, *node = dev->of_node; |
| u32 val; |
| int ret; |
| |
| ret = of_property_read_u32(node, "qcom,chipid", &val); |
| if (ret) { |
| - dev_err(&pdev->dev, "could not find chipid: %d\n", ret); |
| + dev_err(dev, "could not find chipid: %d\n", ret); |
| return ret; |
| } |
| |
| @@ -548,7 +552,7 @@ static int a3xx_probe(struct platform_de |
| for_each_child_of_node(child, pwrlvl) { |
| ret = of_property_read_u32(pwrlvl, "qcom,gpu-freq", &val); |
| if (ret) { |
| - dev_err(&pdev->dev, "could not find gpu-freq: %d\n", ret); |
| + dev_err(dev, "could not find gpu-freq: %d\n", ret); |
| return ret; |
| } |
| config.fast_rate = max(config.fast_rate, val); |
| @@ -558,12 +562,12 @@ static int a3xx_probe(struct platform_de |
| } |
| |
| if (!config.fast_rate) { |
| - dev_err(&pdev->dev, "could not find clk rates\n"); |
| + dev_err(dev, "could not find clk rates\n"); |
| return -ENXIO; |
| } |
| |
| #else |
| - struct kgsl_device_platform_data *pdata = pdev->dev.platform_data; |
| + struct kgsl_device_platform_data *pdata = dev->platform_data; |
| uint32_t version = socinfo_get_version(); |
| if (cpu_is_apq8064ab()) { |
| config.fast_rate = 450000000; |
| @@ -609,14 +613,30 @@ static int a3xx_probe(struct platform_de |
| config.bus_scale_table = pdata->bus_scale_table; |
| # endif |
| #endif |
| - pdev->dev.platform_data = &config; |
| - a3xx_pdev = pdev; |
| + dev->platform_data = &config; |
| + set_gpu_pdev(dev_get_drvdata(master), to_platform_device(dev)); |
| return 0; |
| } |
| |
| +static void a3xx_unbind(struct device *dev, struct device *master, |
| + void *data) |
| +{ |
| + set_gpu_pdev(dev_get_drvdata(master), NULL); |
| +} |
| + |
| +static const struct component_ops a3xx_ops = { |
| + .bind = a3xx_bind, |
| + .unbind = a3xx_unbind, |
| +}; |
| + |
| +static int a3xx_probe(struct platform_device *pdev) |
| +{ |
| + return component_add(&pdev->dev, &a3xx_ops); |
| +} |
| + |
| static int a3xx_remove(struct platform_device *pdev) |
| { |
| - a3xx_pdev = NULL; |
| + component_del(&pdev->dev, &a3xx_ops); |
| return 0; |
| } |
| |
| @@ -624,7 +644,6 @@ static const struct of_device_id dt_matc |
| { .compatible = "qcom,kgsl-3d0" }, |
| {} |
| }; |
| -MODULE_DEVICE_TABLE(of, dt_match); |
| |
| static struct platform_driver a3xx_driver = { |
| .probe = a3xx_probe, |
| --- a/drivers/gpu/drm/msm/hdmi/hdmi.c |
| +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c |
| @@ -17,8 +17,6 @@ |
| |
| #include "hdmi.h" |
| |
| -static struct platform_device *hdmi_pdev; |
| - |
| void hdmi_set_mode(struct hdmi *hdmi, bool power_on) |
| { |
| uint32_t ctrl = 0; |
| @@ -75,7 +73,7 @@ struct hdmi *hdmi_init(struct drm_device |
| { |
| struct hdmi *hdmi = NULL; |
| struct msm_drm_private *priv = dev->dev_private; |
| - struct platform_device *pdev = hdmi_pdev; |
| + struct platform_device *pdev = priv->hdmi_pdev; |
| struct hdmi_platform_config *config; |
| int i, ret; |
| |
| @@ -95,8 +93,6 @@ struct hdmi *hdmi_init(struct drm_device |
| |
| kref_init(&hdmi->refcount); |
| |
| - get_device(&pdev->dev); |
| - |
| hdmi->dev = dev; |
| hdmi->pdev = pdev; |
| hdmi->config = config; |
| @@ -249,17 +245,24 @@ fail: |
| |
| #include <linux/of_gpio.h> |
| |
| -static int hdmi_dev_probe(struct platform_device *pdev) |
| +static void set_hdmi_pdev(struct drm_device *dev, |
| + struct platform_device *pdev) |
| +{ |
| + struct msm_drm_private *priv = dev->dev_private; |
| + priv->hdmi_pdev = pdev; |
| +} |
| + |
| +static int hdmi_bind(struct device *dev, struct device *master, void *data) |
| { |
| static struct hdmi_platform_config config = {}; |
| #ifdef CONFIG_OF |
| - struct device_node *of_node = pdev->dev.of_node; |
| + struct device_node *of_node = dev->of_node; |
| |
| int get_gpio(const char *name) |
| { |
| int gpio = of_get_named_gpio(of_node, name, 0); |
| if (gpio < 0) { |
| - dev_err(&pdev->dev, "failed to get gpio: %s (%d)\n", |
| + dev_err(dev, "failed to get gpio: %s (%d)\n", |
| name, gpio); |
| gpio = -1; |
| } |
| @@ -336,14 +339,30 @@ static int hdmi_dev_probe(struct platfor |
| config.mux_sel_gpio = -1; |
| } |
| #endif |
| - pdev->dev.platform_data = &config; |
| - hdmi_pdev = pdev; |
| + dev->platform_data = &config; |
| + set_hdmi_pdev(dev_get_drvdata(master), to_platform_device(dev)); |
| return 0; |
| } |
| |
| +static void hdmi_unbind(struct device *dev, struct device *master, |
| + void *data) |
| +{ |
| + set_hdmi_pdev(dev_get_drvdata(master), NULL); |
| +} |
| + |
| +static const struct component_ops hdmi_ops = { |
| + .bind = hdmi_bind, |
| + .unbind = hdmi_unbind, |
| +}; |
| + |
| +static int hdmi_dev_probe(struct platform_device *pdev) |
| +{ |
| + return component_add(&pdev->dev, &hdmi_ops); |
| +} |
| + |
| static int hdmi_dev_remove(struct platform_device *pdev) |
| { |
| - hdmi_pdev = NULL; |
| + component_del(&pdev->dev, &hdmi_ops); |
| return 0; |
| } |
| |
| @@ -351,7 +370,6 @@ static const struct of_device_id dt_matc |
| { .compatible = "qcom,hdmi-tx" }, |
| {} |
| }; |
| -MODULE_DEVICE_TABLE(of, dt_match); |
| |
| static struct platform_driver hdmi_driver = { |
| .probe = hdmi_dev_probe, |
| --- a/drivers/gpu/drm/msm/msm_drv.c |
| +++ b/drivers/gpu/drm/msm/msm_drv.c |
| @@ -56,6 +56,10 @@ static char *vram; |
| MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU"); |
| module_param(vram, charp, 0); |
| |
| +/* |
| + * Util/helpers: |
| + */ |
| + |
| void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, |
| const char *dbgname) |
| { |
| @@ -143,6 +147,8 @@ static int msm_unload(struct drm_device |
| priv->vram.paddr, &attrs); |
| } |
| |
| + component_unbind_all(dev->dev, dev); |
| + |
| dev->dev_private = NULL; |
| |
| kfree(priv); |
| @@ -175,6 +181,7 @@ static int msm_load(struct drm_device *d |
| struct msm_kms *kms; |
| int ret; |
| |
| + |
| priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
| if (!priv) { |
| dev_err(dev->dev, "failed to allocate private data\n"); |
| @@ -226,6 +233,13 @@ static int msm_load(struct drm_device *d |
| (uint32_t)(priv->vram.paddr + size)); |
| } |
| |
| + platform_set_drvdata(pdev, dev); |
| + |
| + /* Bind all our sub-components: */ |
| + ret = component_bind_all(dev->dev, dev); |
| + if (ret) |
| + return ret; |
| + |
| switch (get_mdp_ver(pdev)) { |
| case 4: |
| kms = mdp4_kms_init(dev); |
| @@ -281,8 +295,6 @@ static int msm_load(struct drm_device *d |
| goto fail; |
| } |
| |
| - platform_set_drvdata(pdev, dev); |
| - |
| #ifdef CONFIG_DRM_MSM_FBDEV |
| priv->fbdev = msm_fbdev_init(dev); |
| #endif |
| @@ -819,18 +831,110 @@ static const struct dev_pm_ops msm_pm_op |
| }; |
| |
| /* |
| + * Componentized driver support: |
| + */ |
| + |
| +#ifdef CONFIG_OF |
| +/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx |
| + * (or probably any other).. so probably some room for some helpers |
| + */ |
| +static int compare_of(struct device *dev, void *data) |
| +{ |
| + return dev->of_node == data; |
| +} |
| + |
| +static int msm_drm_add_components(struct device *master, struct master *m) |
| +{ |
| + struct device_node *np = master->of_node; |
| + unsigned i; |
| + int ret; |
| + |
| + for (i = 0; ; i++) { |
| + struct device_node *node; |
| + |
| + node = of_parse_phandle(np, "connectors", i); |
| + if (!node) |
| + break; |
| + |
| + ret = component_master_add_child(m, compare_of, node); |
| + of_node_put(node); |
| + |
| + if (ret) |
| + return ret; |
| + } |
| + return 0; |
| +} |
| +#else |
| +static int compare_dev(struct device *dev, void *data) |
| +{ |
| + return dev == data; |
| +} |
| + |
| +static int msm_drm_add_components(struct device *master, struct master *m) |
| +{ |
| + /* For non-DT case, it kinda sucks. We don't actually have a way |
| + * to know whether or not we are waiting for certain devices (or if |
| + * they are simply not present). But for non-DT we only need to |
| + * care about apq8064/apq8060/etc (all mdp4/a3xx): |
| + */ |
| + static const char *devnames[] = { |
| + "hdmi_msm.0", "kgsl-3d0.0", |
| + }; |
| + int i; |
| + |
| + DBG("Adding components.."); |
| + |
| + for (i = 0; i < ARRAY_SIZE(devnames); i++) { |
| + struct device *dev; |
| + int ret; |
| + |
| + dev = bus_find_device_by_name(&platform_bus_type, |
| + NULL, devnames[i]); |
| + if (!dev) { |
| + dev_info(master, "still waiting for %s\n", devnames[i]); |
| + return -EPROBE_DEFER; |
| + } |
| + |
| + ret = component_master_add_child(m, compare_dev, dev); |
| + if (ret) { |
| + DBG("could not add child: %d", ret); |
| + return ret; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| +#endif |
| + |
| +static int msm_drm_bind(struct device *dev) |
| +{ |
| + return drm_platform_init(&msm_driver, to_platform_device(dev)); |
| +} |
| + |
| +static void msm_drm_unbind(struct device *dev) |
| +{ |
| + drm_put_dev(platform_get_drvdata(to_platform_device(dev))); |
| +} |
| + |
| +static const struct component_master_ops msm_drm_ops = { |
| + .add_components = msm_drm_add_components, |
| + .bind = msm_drm_bind, |
| + .unbind = msm_drm_unbind, |
| +}; |
| + |
| +/* |
| * Platform driver: |
| */ |
| |
| static int msm_pdev_probe(struct platform_device *pdev) |
| { |
| pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); |
| - return drm_platform_init(&msm_driver, pdev); |
| + return component_master_add(&pdev->dev, &msm_drm_ops); |
| } |
| |
| static int msm_pdev_remove(struct platform_device *pdev) |
| { |
| - drm_put_dev(platform_get_drvdata(pdev)); |
| + component_master_del(&pdev->dev, &msm_drm_ops); |
| |
| return 0; |
| } |
| --- a/drivers/gpu/drm/msm/msm_drv.h |
| +++ b/drivers/gpu/drm/msm/msm_drv.h |
| @@ -22,6 +22,7 @@ |
| #include <linux/clk.h> |
| #include <linux/cpufreq.h> |
| #include <linux/module.h> |
| +#include <linux/component.h> |
| #include <linux/platform_device.h> |
| #include <linux/pm.h> |
| #include <linux/pm_runtime.h> |
| @@ -69,6 +70,9 @@ struct msm_drm_private { |
| |
| struct msm_kms *kms; |
| |
| + /* subordinate devices, if present: */ |
| + struct platform_device *hdmi_pdev, *gpu_pdev; |
| + |
| /* when we have more than one 'msm_gpu' these need to be an array: */ |
| struct msm_gpu *gpu; |
| struct msm_file_private *lastctx; |