summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/exynos/exynos_drm_drv.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 13:18:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-26 13:18:51 -0700
commit099bfbfc7fbbe22356c02f0caf709ac32e1126ea (patch)
treec2dfe2f9445255d866e9203cff9e9f865ef93513 /drivers/gpu/drm/exynos/exynos_drm_drv.c
parent22165fa79814e71e7a5974b3c37a5028ed16c8f9 (diff)
parentc5fd936e992dd2829167d2adc63e151675ca6898 (diff)
downloadlinux-099bfbfc7fbbe22356c02f0caf709ac32e1126ea.tar.bz2
Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm updates from Dave Airlie: "This is the main drm pull request for v4.2. I've one other new driver from freescale on my radar, it's been posted and reviewed, I'd just like to get someone to give it a last look, so maybe I'll send it or maybe I'll leave it. There is no major nouveau changes in here, Ben was working on something big, and we agreed it was a bit late, there wasn't anything else he considered urgent to merge. There might be another msm pull for some bits that are waiting on arm-soc, I'll see how we time it. This touches some "of" stuff, acks are in place except for the fixes to the build in various configs,t hat I just applied. Summary: New drivers: - virtio-gpu: KMS only pieces of driver for virtio-gpu in qemu. This is just the first part of this driver, enough to run unaccelerated userspace on. As qemu merges more we'll start adding the 3D features for the virgl 3d work. - amdgpu: a new driver from AMD to driver their newer GPUs. (VI+) It contains a new cleaner userspace API, and is a clean break from radeon moving forward, that AMD are going to concentrate on. It also contains a set of register headers auto generated from AMD internal database. core: - atomic modesetting API completed, enabled by default now. - Add support for mode_id blob to atomic ioctl to complete interface. - bunch of Displayport MST fixes - lots of misc fixes. panel: - new simple panels - fix some long-standing build issues with bridge drivers radeon: - VCE1 support - add a GPU reset counter for userspace - lots of fixes. amdkfd: - H/W debugger support module - static user-mode queues - support killing all the waves when a process terminates - use standard DECLARE_BITMAP i915: - Add Broxton support - S3, rotation support for Skylake - RPS booting tuning - CPT modeset sequence fixes - ns2501 dither support - enable cmd parser on haswell - cdclk handling fixes - gen8 dynamic pte allocation - lots of atomic conversion work exynos: - Add atomic modesetting support - Add iommu support - Consolidate drm driver initialization - and MIC, DECON and MIPI-DSI support for exynos5433 omapdrm: - atomic modesetting support (fixes lots of things in rewrite) tegra: - DP aux transaction fixes - iommu support fix msm: - adreno a306 support - various dsi bits - various 64-bit fixes - NV12MT support rcar-du: - atomic and misc fixes sti: - fix HDMI timing complaince tilcdc: - use drm component API to access tda998x driver - fix module unloading qxl: - stability fixes" * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (872 commits) drm/nouveau: Pause between setting gpu to D3hot and cutting the power drm/dp/mst: close deadlock in connector destruction. drm: Always enable atomic API drm/vgem: Set unique to "vgem" of: fix a build error to of_graph_get_endpoint_by_regs function drm/dp/mst: take lock around looking up the branch device on hpd irq drm/dp/mst: make sure mst_primary mstb is valid in work function of: add EXPORT_SYMBOL for of_graph_get_endpoint_by_regs ARM: dts: rename the clock of MIPI DSI 'pll_clk' to 'sclk_mipi' drm/atomic: Don't set crtc_state->enable manually drm/exynos: dsi: do not set TE GPIO direction by input drm/exynos: dsi: add support for MIC driver as a bridge drm/exynos: dsi: add support for Exynos5433 drm/exynos: dsi: make use of array for clock access drm/exynos: dsi: make use of driver data for static values drm/exynos: dsi: add macros for register access drm/exynos: dsi: rename pll_clk to sclk_clk drm/exynos: mic: add MIC driver of: add helper for getting endpoint node of specific identifiers drm/exynos: add Exynos5433 decon driver ...
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_drm_drv.c')
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c429
1 files changed, 163 insertions, 266 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 8ac465208eae..63a68c60a353 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -38,19 +38,6 @@
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-static struct platform_device *exynos_drm_pdev;
-
-static DEFINE_MUTEX(drm_component_lock);
-static LIST_HEAD(drm_component_list);
-
-struct component_dev {
- struct list_head list;
- struct device *crtc_dev;
- struct device *conn_dev;
- enum exynos_drm_output_type out_type;
- unsigned int dev_type_flag;
-};
-
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
@@ -98,6 +85,8 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_cleanup_vblank;
+ drm_mode_config_reset(dev);
+
/*
* enable drm irq mode.
* - with irq_enabled = true, we can use the vblank feature.
@@ -348,190 +337,29 @@ static const struct dev_pm_ops exynos_drm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(exynos_drm_sys_suspend, exynos_drm_sys_resume)
};
-int exynos_drm_component_add(struct device *dev,
- enum exynos_drm_device_type dev_type,
- enum exynos_drm_output_type out_type)
-{
- struct component_dev *cdev;
-
- if (dev_type != EXYNOS_DEVICE_TYPE_CRTC &&
- dev_type != EXYNOS_DEVICE_TYPE_CONNECTOR) {
- DRM_ERROR("invalid device type.\n");
- return -EINVAL;
- }
-
- mutex_lock(&drm_component_lock);
-
- /*
- * Make sure to check if there is a component which has two device
- * objects, for connector and for encoder/connector.
- * It should make sure that crtc and encoder/connector drivers are
- * ready before exynos drm core binds them.
- */
- list_for_each_entry(cdev, &drm_component_list, list) {
- if (cdev->out_type == out_type) {
- /*
- * If crtc and encoder/connector device objects are
- * added already just return.
- */
- if (cdev->dev_type_flag == (EXYNOS_DEVICE_TYPE_CRTC |
- EXYNOS_DEVICE_TYPE_CONNECTOR)) {
- mutex_unlock(&drm_component_lock);
- return 0;
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
- cdev->crtc_dev = dev;
- cdev->dev_type_flag |= dev_type;
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
- cdev->conn_dev = dev;
- cdev->dev_type_flag |= dev_type;
- }
-
- mutex_unlock(&drm_component_lock);
- return 0;
- }
- }
-
- mutex_unlock(&drm_component_lock);
-
- cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
- if (!cdev)
- return -ENOMEM;
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC)
- cdev->crtc_dev = dev;
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR)
- cdev->conn_dev = dev;
-
- cdev->out_type = out_type;
- cdev->dev_type_flag = dev_type;
-
- mutex_lock(&drm_component_lock);
- list_add_tail(&cdev->list, &drm_component_list);
- mutex_unlock(&drm_component_lock);
-
- return 0;
-}
-
-void exynos_drm_component_del(struct device *dev,
- enum exynos_drm_device_type dev_type)
-{
- struct component_dev *cdev, *next;
-
- mutex_lock(&drm_component_lock);
-
- list_for_each_entry_safe(cdev, next, &drm_component_list, list) {
- if (dev_type == EXYNOS_DEVICE_TYPE_CRTC) {
- if (cdev->crtc_dev == dev) {
- cdev->crtc_dev = NULL;
- cdev->dev_type_flag &= ~dev_type;
- }
- }
-
- if (dev_type == EXYNOS_DEVICE_TYPE_CONNECTOR) {
- if (cdev->conn_dev == dev) {
- cdev->conn_dev = NULL;
- cdev->dev_type_flag &= ~dev_type;
- }
- }
-
- /*
- * Release cdev object only in case that both of crtc and
- * encoder/connector device objects are NULL.
- */
- if (!cdev->crtc_dev && !cdev->conn_dev) {
- list_del(&cdev->list);
- kfree(cdev);
- }
- }
-
- mutex_unlock(&drm_component_lock);
-}
-
-static int compare_dev(struct device *dev, void *data)
-{
- return dev == (struct device *)data;
-}
-
-static struct component_match *exynos_drm_match_add(struct device *dev)
-{
- struct component_match *match = NULL;
- struct component_dev *cdev;
- unsigned int attach_cnt = 0;
-
- mutex_lock(&drm_component_lock);
-
- /* Do not retry to probe if there is no any kms driver regitered. */
- if (list_empty(&drm_component_list)) {
- mutex_unlock(&drm_component_lock);
- return ERR_PTR(-ENODEV);
- }
-
- list_for_each_entry(cdev, &drm_component_list, list) {
- /*
- * Add components to master only in case that crtc and
- * encoder/connector device objects exist.
- */
- if (!cdev->crtc_dev || !cdev->conn_dev)
- continue;
-
- attach_cnt++;
-
- mutex_unlock(&drm_component_lock);
-
- /*
- * fimd and dpi modules have same device object so add
- * only crtc device object in this case.
- */
- if (cdev->crtc_dev == cdev->conn_dev) {
- component_match_add(dev, &match, compare_dev,
- cdev->crtc_dev);
- goto out_lock;
- }
-
- /*
- * Do not chage below call order.
- * crtc device first should be added to master because
- * connector/encoder need pipe number of crtc when they
- * are created.
- */
- component_match_add(dev, &match, compare_dev, cdev->crtc_dev);
- component_match_add(dev, &match, compare_dev, cdev->conn_dev);
-
-out_lock:
- mutex_lock(&drm_component_lock);
- }
-
- mutex_unlock(&drm_component_lock);
-
- return attach_cnt ? match : ERR_PTR(-EPROBE_DEFER);
-}
-
-static int exynos_drm_bind(struct device *dev)
-{
- return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
-}
-
-static void exynos_drm_unbind(struct device *dev)
-{
- drm_put_dev(dev_get_drvdata(dev));
-}
-
-static const struct component_master_ops exynos_drm_ops = {
- .bind = exynos_drm_bind,
- .unbind = exynos_drm_unbind,
-};
+/* forward declaration */
+static struct platform_driver exynos_drm_platform_driver;
+/*
+ * Connector drivers should not be placed before associated crtc drivers,
+ * because connector requires pipe number of its crtc during initialization.
+ */
static struct platform_driver *const exynos_drm_kms_drivers[] = {
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ &vidi_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS_FIMD
&fimd_driver,
#endif
+#ifdef CONFIG_DRM_EXYNOS5433_DECON
+ &exynos5433_decon_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS7_DECON
&decon_driver,
#endif
+#ifdef CONFIG_DRM_EXYNOS_MIC
+ &mic_driver,
+#endif
#ifdef CONFIG_DRM_EXYNOS_DP
&dp_driver,
#endif
@@ -560,6 +388,59 @@ static struct platform_driver *const exynos_drm_non_kms_drivers[] = {
#ifdef CONFIG_DRM_EXYNOS_IPP
&ipp_driver,
#endif
+ &exynos_drm_platform_driver,
+};
+
+static struct platform_driver *const exynos_drm_drv_with_simple_dev[] = {
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ &vidi_driver,
+#endif
+#ifdef CONFIG_DRM_EXYNOS_IPP
+ &ipp_driver,
+#endif
+ &exynos_drm_platform_driver,
+};
+#define PDEV_COUNT ARRAY_SIZE(exynos_drm_drv_with_simple_dev)
+
+static int compare_dev(struct device *dev, void *data)
+{
+ return dev == (struct device *)data;
+}
+
+static struct component_match *exynos_drm_match_add(struct device *dev)
+{
+ struct component_match *match = NULL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
+ struct device_driver *drv = &exynos_drm_kms_drivers[i]->driver;
+ struct device *p = NULL, *d;
+
+ while ((d = bus_find_device(&platform_bus_type, p, drv,
+ (void *)platform_bus_type.match))) {
+ put_device(p);
+ component_match_add(dev, &match, compare_dev, d);
+ p = d;
+ }
+ put_device(p);
+ }
+
+ return match ?: ERR_PTR(-ENODEV);
+}
+
+static int exynos_drm_bind(struct device *dev)
+{
+ return drm_platform_init(&exynos_drm_driver, to_platform_device(dev));
+}
+
+static void exynos_drm_unbind(struct device *dev)
+{
+ drm_put_dev(dev_get_drvdata(dev));
+}
+
+static const struct component_master_ops exynos_drm_ops = {
+ .bind = exynos_drm_bind,
+ .unbind = exynos_drm_unbind,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
@@ -570,9 +451,8 @@ static int exynos_drm_platform_probe(struct platform_device *pdev)
exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls);
match = exynos_drm_match_add(&pdev->dev);
- if (IS_ERR(match)) {
+ if (IS_ERR(match))
return PTR_ERR(match);
- }
return component_master_add_with_match(&pdev->dev, &exynos_drm_ops,
match);
@@ -584,13 +464,6 @@ static int exynos_drm_platform_remove(struct platform_device *pdev)
return 0;
}
-static const char * const strings[] = {
- "samsung,exynos3",
- "samsung,exynos4",
- "samsung,exynos5",
- "samsung,exynos7",
-};
-
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = exynos_drm_platform_remove,
@@ -600,101 +473,125 @@ static struct platform_driver exynos_drm_platform_driver = {
},
};
-static int exynos_drm_init(void)
+static struct platform_device *exynos_drm_pdevs[PDEV_COUNT];
+
+static void exynos_drm_unregister_devices(void)
{
- bool is_exynos = false;
- int ret, i, j;
+ int i = PDEV_COUNT;
- /*
- * Register device object only in case of Exynos SoC.
- *
- * Below codes resolves temporarily infinite loop issue incurred
- * by Exynos drm driver when using multi-platform kernel.
- * So these codes will be replaced with more generic way later.
- */
- for (i = 0; i < ARRAY_SIZE(strings); i++) {
- if (of_machine_is_compatible(strings[i])) {
- is_exynos = true;
- break;
- }
+ while (--i >= 0) {
+ platform_device_unregister(exynos_drm_pdevs[i]);
+ exynos_drm_pdevs[i] = NULL;
}
+}
- if (!is_exynos)
- return -ENODEV;
+static int exynos_drm_register_devices(void)
+{
+ int i;
- exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1,
- NULL, 0);
- if (IS_ERR(exynos_drm_pdev))
- return PTR_ERR(exynos_drm_pdev);
+ for (i = 0; i < PDEV_COUNT; ++i) {
+ struct platform_driver *d = exynos_drm_drv_with_simple_dev[i];
+ struct platform_device *pdev =
+ platform_device_register_simple(d->driver.name, -1,
+ NULL, 0);
- ret = exynos_drm_probe_vidi();
- if (ret < 0)
- goto err_unregister_pd;
+ if (!IS_ERR(pdev)) {
+ exynos_drm_pdevs[i] = pdev;
+ continue;
+ }
+ while (--i >= 0) {
+ platform_device_unregister(exynos_drm_pdevs[i]);
+ exynos_drm_pdevs[i] = NULL;
+ }
- for (i = 0; i < ARRAY_SIZE(exynos_drm_kms_drivers); ++i) {
- ret = platform_driver_register(exynos_drm_kms_drivers[i]);
- if (ret < 0)
- goto err_unregister_kms_drivers;
+ return PTR_ERR(pdev);
}
- for (j = 0; j < ARRAY_SIZE(exynos_drm_non_kms_drivers); ++j) {
- ret = platform_driver_register(exynos_drm_non_kms_drivers[j]);
- if (ret < 0)
- goto err_unregister_non_kms_drivers;
- }
+ return 0;
+}
-#ifdef CONFIG_DRM_EXYNOS_IPP
- ret = exynos_platform_device_ipp_register();
- if (ret < 0)
- goto err_unregister_non_kms_drivers;
-#endif
+static void exynos_drm_unregister_drivers(struct platform_driver * const *drv,
+ int count)
+{
+ while (--count >= 0)
+ platform_driver_unregister(drv[count]);
+}
- ret = platform_driver_register(&exynos_drm_platform_driver);
- if (ret)
- goto err_unregister_resources;
+static int exynos_drm_register_drivers(struct platform_driver * const *drv,
+ int count)
+{
+ int i, ret;
- return 0;
+ for (i = 0; i < count; ++i) {
+ ret = platform_driver_register(drv[i]);
+ if (!ret)
+ continue;
-err_unregister_resources:
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-#endif
+ while (--i >= 0)
+ platform_driver_unregister(drv[i]);
-err_unregister_non_kms_drivers:
- while (--j >= 0)
- platform_driver_unregister(exynos_drm_non_kms_drivers[j]);
+ return ret;
+ }
-err_unregister_kms_drivers:
- while (--i >= 0)
- platform_driver_unregister(exynos_drm_kms_drivers[i]);
+ return 0;
+}
- exynos_drm_remove_vidi();
+static inline int exynos_drm_register_kms_drivers(void)
+{
+ return exynos_drm_register_drivers(exynos_drm_kms_drivers,
+ ARRAY_SIZE(exynos_drm_kms_drivers));
+}
-err_unregister_pd:
- platform_device_unregister(exynos_drm_pdev);
+static inline int exynos_drm_register_non_kms_drivers(void)
+{
+ return exynos_drm_register_drivers(exynos_drm_non_kms_drivers,
+ ARRAY_SIZE(exynos_drm_non_kms_drivers));
+}
- return ret;
+static inline void exynos_drm_unregister_kms_drivers(void)
+{
+ exynos_drm_unregister_drivers(exynos_drm_kms_drivers,
+ ARRAY_SIZE(exynos_drm_kms_drivers));
}
-static void exynos_drm_exit(void)
+static inline void exynos_drm_unregister_non_kms_drivers(void)
{
- int i;
+ exynos_drm_unregister_drivers(exynos_drm_non_kms_drivers,
+ ARRAY_SIZE(exynos_drm_non_kms_drivers));
+}
-#ifdef CONFIG_DRM_EXYNOS_IPP
- exynos_platform_device_ipp_unregister();
-#endif
+static int exynos_drm_init(void)
+{
+ int ret;
- for (i = ARRAY_SIZE(exynos_drm_non_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_non_kms_drivers[i]);
+ ret = exynos_drm_register_devices();
+ if (ret)
+ return ret;
- for (i = ARRAY_SIZE(exynos_drm_kms_drivers) - 1; i >= 0; --i)
- platform_driver_unregister(exynos_drm_kms_drivers[i]);
+ ret = exynos_drm_register_kms_drivers();
+ if (ret)
+ goto err_unregister_pdevs;
- platform_driver_unregister(&exynos_drm_platform_driver);
+ ret = exynos_drm_register_non_kms_drivers();
+ if (ret)
+ goto err_unregister_kms_drivers;
+
+ return 0;
+
+err_unregister_kms_drivers:
+ exynos_drm_unregister_kms_drivers();
- exynos_drm_remove_vidi();
+err_unregister_pdevs:
+ exynos_drm_unregister_devices();
- platform_device_unregister(exynos_drm_pdev);
+ return ret;
+}
+
+static void exynos_drm_exit(void)
+{
+ exynos_drm_unregister_non_kms_drivers();
+ exynos_drm_unregister_kms_drivers();
+ exynos_drm_unregister_devices();
}
module_init(exynos_drm_init);