From c422f31d5654a4787a226bb684bd601d4db9558d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 10 Dec 2014 12:30:02 +0100 Subject: drm/tegra: Remove dummy ->load_lut() implementation The ->load_lut() callback is optional, therefore a dummy implementation is not needed. Signed-off-by: Thierry Reding Signed-off-by: Daniel Vetter --- drivers/gpu/drm/tegra/dc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index b957908aec73..d9076a10cb75 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1095,10 +1095,6 @@ static void tegra_crtc_commit(struct drm_crtc *crtc) tegra_dc_commit(dc); } -static void tegra_crtc_load_lut(struct drm_crtc *crtc) -{ -} - static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .disable = tegra_crtc_disable, .mode_fixup = tegra_crtc_mode_fixup, @@ -1106,7 +1102,6 @@ static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .mode_set_base = tegra_crtc_mode_set_base, .prepare = tegra_crtc_prepare, .commit = tegra_crtc_commit, - .load_lut = tegra_crtc_load_lut, }; static irqreturn_t tegra_dc_irq(int irq, void *data) -- cgit v1.2.3 From f4c5cf88fbd50e4779042268947b2e2f90c20484 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 18 Dec 2014 15:29:14 +0100 Subject: gpu: host1x: Provide a proper struct bus_type Previously the struct bus_type exported by the host1x infrastructure was only a very basic skeleton. Turn that implementation into a more full- fledged bus to support proper probe ordering and power management. Note that the bus infrastructure needs to be available before any of the drivers can be registered. This is automatically ensured if all drivers are built as loadable modules (via symbol dependencies). If all drivers are built-in there are no such guarantees and the link order determines the initcall ordering. Adjust drivers/gpu/Makefile to make sure that the host1x bus infrastructure is initialized prior to any of its users (only drm/tegra currently). v2: Fix building host1x and tegra-drm as modules Reported-by: Dave Airlie Reviewed-by: Sean Paul Reviewed-by: Mark Zhang Signed-off-by: Thierry Reding --- drivers/gpu/Makefile | 5 +- drivers/gpu/drm/tegra/drm.c | 4 +- drivers/gpu/host1x/bus.c | 111 ++++++++++++++++++++++++++++++-------------- drivers/gpu/host1x/bus.h | 4 +- drivers/gpu/host1x/dev.c | 6 +-- include/linux/host1x.h | 18 +++++-- 6 files changed, 103 insertions(+), 45 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index 70da9eb52a42..e9ed439a5b65 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -1,3 +1,6 @@ -obj-y += drm/ vga/ +# drm/tegra depends on host1x, so if both drivers are built-in care must be +# taken to initialize them in the correct order. Link order is the only way +# to ensure this currently. obj-$(CONFIG_TEGRA_HOST1X) += host1x/ +obj-y += drm/ vga/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d4f827593dfa..a0c18ae79029 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -912,7 +912,9 @@ static const struct of_device_id host1x_drm_subdevs[] = { }; static struct host1x_driver host1x_drm_driver = { - .name = "drm", + .driver = { + .name = "drm", + }, .probe = host1x_drm_probe, .remove = host1x_drm_remove, .subdevs = host1x_drm_subdevs, diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index 0b52f0ea8871..4a99c6416e6a 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -72,13 +72,14 @@ static void host1x_subdev_del(struct host1x_subdev *subdev) /** * host1x_device_parse_dt() - scan device tree and add matching subdevices */ -static int host1x_device_parse_dt(struct host1x_device *device) +static int host1x_device_parse_dt(struct host1x_device *device, + struct host1x_driver *driver) { struct device_node *np; int err; for_each_child_of_node(device->dev.parent->of_node, np) { - if (of_match_node(device->driver->subdevs, np) && + if (of_match_node(driver->subdevs, np) && of_device_is_available(np)) { err = host1x_subdev_add(device, np); if (err < 0) @@ -109,17 +110,12 @@ static void host1x_subdev_register(struct host1x_device *device, mutex_unlock(&device->clients_lock); mutex_unlock(&device->subdevs_lock); - /* - * When all subdevices have been registered, the composite device is - * ready to be probed. - */ if (list_empty(&device->subdevs)) { - err = device->driver->probe(device); + err = device_add(&device->dev); if (err < 0) - dev_err(&device->dev, "probe failed for %ps: %d\n", - device->driver, err); + dev_err(&device->dev, "failed to add: %d\n", err); else - device->bound = true; + device->registered = true; } } @@ -127,18 +123,16 @@ static void __host1x_subdev_unregister(struct host1x_device *device, struct host1x_subdev *subdev) { struct host1x_client *client = subdev->client; - int err; /* * If all subdevices have been activated, we're about to remove the * first active subdevice, so unload the driver first. */ - if (list_empty(&device->subdevs) && device->bound) { - err = device->driver->remove(device); - if (err < 0) - dev_err(&device->dev, "remove failed: %d\n", err); - - device->bound = false; + if (list_empty(&device->subdevs)) { + if (device->registered) { + device->registered = false; + device_del(&device->dev); + } } /* @@ -265,20 +259,60 @@ static int host1x_del_client(struct host1x *host1x, return -ENODEV; } -static struct bus_type host1x_bus_type = { - .name = "host1x", -}; +static int host1x_device_match(struct device *dev, struct device_driver *drv) +{ + return strcmp(dev_name(dev), drv->name) == 0; +} -int host1x_bus_init(void) +static int host1x_device_probe(struct device *dev) { - return bus_register(&host1x_bus_type); + struct host1x_driver *driver = to_host1x_driver(dev->driver); + struct host1x_device *device = to_host1x_device(dev); + + if (driver->probe) + return driver->probe(device); + + return 0; } -void host1x_bus_exit(void) +static int host1x_device_remove(struct device *dev) { - bus_unregister(&host1x_bus_type); + struct host1x_driver *driver = to_host1x_driver(dev->driver); + struct host1x_device *device = to_host1x_device(dev); + + if (driver->remove) + return driver->remove(device); + + return 0; } +static void host1x_device_shutdown(struct device *dev) +{ + struct host1x_driver *driver = to_host1x_driver(dev->driver); + struct host1x_device *device = to_host1x_device(dev); + + if (driver->shutdown) + driver->shutdown(device); +} + +static const struct dev_pm_ops host1x_device_pm_ops = { + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, +}; + +struct bus_type host1x_bus_type = { + .name = "host1x", + .match = host1x_device_match, + .probe = host1x_device_probe, + .remove = host1x_device_remove, + .shutdown = host1x_device_shutdown, + .pm = &host1x_device_pm_ops, +}; + static void __host1x_device_del(struct host1x_device *device) { struct host1x_subdev *subdev, *sd; @@ -347,6 +381,8 @@ static int host1x_device_add(struct host1x *host1x, if (!device) return -ENOMEM; + device_initialize(&device->dev); + mutex_init(&device->subdevs_lock); INIT_LIST_HEAD(&device->subdevs); INIT_LIST_HEAD(&device->active); @@ -357,18 +393,14 @@ static int host1x_device_add(struct host1x *host1x, device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask; device->dev.dma_mask = &device->dev.coherent_dma_mask; + dev_set_name(&device->dev, "%s", driver->driver.name); device->dev.release = host1x_device_release; - dev_set_name(&device->dev, "%s", driver->name); device->dev.bus = &host1x_bus_type; device->dev.parent = host1x->dev; - err = device_register(&device->dev); - if (err < 0) - return err; - - err = host1x_device_parse_dt(device); + err = host1x_device_parse_dt(device, driver); if (err < 0) { - device_unregister(&device->dev); + kfree(device); return err; } @@ -399,7 +431,12 @@ static int host1x_device_add(struct host1x *host1x, static void host1x_device_del(struct host1x *host1x, struct host1x_device *device) { - device_unregister(&device->dev); + if (device->registered) { + device->registered = false; + device_del(&device->dev); + } + + put_device(&device->dev); } static void host1x_attach_driver(struct host1x *host1x, @@ -474,7 +511,8 @@ int host1x_unregister(struct host1x *host1x) return 0; } -int host1x_driver_register(struct host1x_driver *driver) +int host1x_driver_register_full(struct host1x_driver *driver, + struct module *owner) { struct host1x *host1x; @@ -491,9 +529,12 @@ int host1x_driver_register(struct host1x_driver *driver) mutex_unlock(&devices_lock); - return 0; + driver->driver.bus = &host1x_bus_type; + driver->driver.owner = owner; + + return driver_register(&driver->driver); } -EXPORT_SYMBOL(host1x_driver_register); +EXPORT_SYMBOL(host1x_driver_register_full); void host1x_driver_unregister(struct host1x_driver *driver) { diff --git a/drivers/gpu/host1x/bus.h b/drivers/gpu/host1x/bus.h index 4099e99212c8..88fb1c4aac68 100644 --- a/drivers/gpu/host1x/bus.h +++ b/drivers/gpu/host1x/bus.h @@ -18,10 +18,10 @@ #ifndef HOST1X_BUS_H #define HOST1X_BUS_H +struct bus_type; struct host1x; -int host1x_bus_init(void); -void host1x_bus_exit(void); +extern struct bus_type host1x_bus_type; int host1x_register(struct host1x *host1x); int host1x_unregister(struct host1x *host1x); diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 2529908d304b..53d3d1d45b48 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -216,7 +216,7 @@ static int __init tegra_host1x_init(void) { int err; - err = host1x_bus_init(); + err = bus_register(&host1x_bus_type); if (err < 0) return err; @@ -233,7 +233,7 @@ static int __init tegra_host1x_init(void) unregister_host1x: platform_driver_unregister(&tegra_host1x_driver); unregister_bus: - host1x_bus_exit(); + bus_unregister(&host1x_bus_type); return err; } module_init(tegra_host1x_init); @@ -242,7 +242,7 @@ static void __exit tegra_host1x_exit(void) { platform_driver_unregister(&tegra_mipi_driver); platform_driver_unregister(&tegra_host1x_driver); - host1x_bus_exit(); + bus_unregister(&host1x_bus_type); } module_exit(tegra_host1x_exit); diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 7890b553d12e..464f33814a94 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -250,17 +250,29 @@ void host1x_job_unpin(struct host1x_job *job); struct host1x_device; struct host1x_driver { + struct device_driver driver; + const struct of_device_id *subdevs; struct list_head list; - const char *name; int (*probe)(struct host1x_device *device); int (*remove)(struct host1x_device *device); + void (*shutdown)(struct host1x_device *device); }; -int host1x_driver_register(struct host1x_driver *driver); +static inline struct host1x_driver * +to_host1x_driver(struct device_driver *driver) +{ + return container_of(driver, struct host1x_driver, driver); +} + +int host1x_driver_register_full(struct host1x_driver *driver, + struct module *owner); void host1x_driver_unregister(struct host1x_driver *driver); +#define host1x_driver_register(driver) \ + host1x_driver_register_full(driver, THIS_MODULE) + struct host1x_device { struct host1x_driver *driver; struct list_head list; @@ -273,7 +285,7 @@ struct host1x_device { struct mutex clients_lock; struct list_head clients; - bool bound; + bool registered; }; static inline struct host1x_device *to_host1x_device(struct device *dev) -- cgit v1.2.3 From 8c8cb58ed6429da3cd41c1b44770c7a4b1b33933 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 16:46:37 +0100 Subject: drm/tegra: gem: Use iommu_map_sg() The iommu_map_sg() function is now available in the IOMMU API, so drop the open-coded variant. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 8777b7f75791..1ccde09d01c8 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -92,36 +92,6 @@ static const struct host1x_bo_ops tegra_bo_ops = { .kunmap = tegra_bo_kunmap, }; -/* - * A generic iommu_map_sg() function is being reviewed and will hopefully be - * merged soon. At that point this function can be dropped in favour of the - * one provided by the IOMMU API. - */ -static ssize_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova, - struct scatterlist *sg, unsigned int nents, - int prot) -{ - struct scatterlist *s; - size_t offset = 0; - unsigned int i; - int err; - - for_each_sg(sg, s, nents, i) { - phys_addr_t phys = page_to_phys(sg_page(s)); - size_t length = s->offset + s->length; - - err = iommu_map(domain, iova + offset, phys, length, prot); - if (err < 0) { - iommu_unmap(domain, iova, offset); - return err; - } - - offset += length; - } - - return offset; -} - static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) { int prot = IOMMU_READ | IOMMU_WRITE; @@ -144,8 +114,8 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo) bo->paddr = bo->mm->start; - err = __iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, - bo->sgt->nents, prot); + err = iommu_map_sg(tegra->domain, bo->paddr, bo->sgt->sgl, + bo->sgt->nents, prot); if (err < 0) { dev_err(tegra->drm->dev, "failed to map buffer: %zd\n", err); goto remove; -- cgit v1.2.3 From aa942f6ac43ab7373a025fe54780c8a852f33348 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 10 Dec 2014 11:41:05 +0100 Subject: drm/tegra: Remove redundant zeroing out of memory The DRM core now zeroes out the memory associated with CRTC, encoder and connector objects upon cleanup, so there's no need to explicitly do that in drivers anymore. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 6 ------ drivers/gpu/drm/tegra/output.c | 12 ------------ 2 files changed, 18 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index ae26cc054fff..3a8a7c227051 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -895,15 +895,9 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, return 0; } -static void drm_crtc_clear(struct drm_crtc *crtc) -{ - memset(crtc, 0, sizeof(*crtc)); -} - static void tegra_dc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); - drm_crtc_clear(crtc); } static const struct drm_crtc_funcs tegra_crtc_funcs = { diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 6a5c7b81fbc5..0e4042ce904f 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -98,16 +98,10 @@ tegra_connector_detect(struct drm_connector *connector, bool force) return status; } -static void drm_connector_clear(struct drm_connector *connector) -{ - memset(connector, 0, sizeof(*connector)); -} - static void tegra_connector_destroy(struct drm_connector *connector) { drm_connector_unregister(connector); drm_connector_cleanup(connector); - drm_connector_clear(connector); } static const struct drm_connector_funcs connector_funcs = { @@ -117,15 +111,9 @@ static const struct drm_connector_funcs connector_funcs = { .destroy = tegra_connector_destroy, }; -static void drm_encoder_clear(struct drm_encoder *encoder) -{ - memset(encoder, 0, sizeof(*encoder)); -} - static void tegra_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); - drm_encoder_clear(encoder); } static const struct drm_encoder_funcs encoder_funcs = { -- cgit v1.2.3 From 518e6227afe076a7facd9a9b5ce1d5ded98732d5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 18:04:08 +0100 Subject: drm/tegra: plane: Use proper possible_crtcs mask The possible_crtcs mask needs to be a mask of CRTC indices. There is no guarantee that the DRM indices match the hardware pipe number, so the mask must be computed from the CRTC index. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 3a8a7c227051..5bab53e0f990 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -435,6 +435,19 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = { static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, struct tegra_dc *dc) { + /* + * Ideally this would use drm_crtc_mask(), but that would require the + * CRTC to already be in the mode_config's list of CRTCs. However, it + * will only be added to that list in the drm_crtc_init_with_planes() + * (in tegra_dc_init()), which in turn requires registration of these + * planes. So we have ourselves a nice little chicken and egg problem + * here. + * + * We work around this by manually creating the mask from the number + * of CRTCs that have been registered, and should therefore always be + * the same as drm_crtc_index() after registration. + */ + unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc; struct tegra_plane *plane; unsigned int num_formats; const u32 *formats; @@ -447,7 +460,7 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, num_formats = ARRAY_SIZE(tegra_primary_plane_formats); formats = tegra_primary_plane_formats; - err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe, + err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, &tegra_primary_plane_funcs, formats, num_formats, DRM_PLANE_TYPE_PRIMARY); if (err < 0) { -- cgit v1.2.3 From bf19b885f956eb0fec7dd7c02400b679dca7312e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Dec 2014 14:00:35 +0300 Subject: drm/tegra: Check for NULL pointer instead of IS_ERR() iommu_domain_alloc() returns NULL on error, it never returns error pointers. Signed-off-by: Dan Carpenter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index a0c18ae79029..219f314cc162 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -36,8 +36,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (iommu_present(&platform_bus_type)) { tegra->domain = iommu_domain_alloc(&platform_bus_type); - if (IS_ERR(tegra->domain)) { - err = PTR_ERR(tegra->domain); + if (!tegra->domain) { + err = -ENOMEM; goto free; } -- cgit v1.2.3 From 42d0659ba76956665c704d2a7cee9fa0f54a7acf Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 15:45:39 +0100 Subject: drm/tegra: dc: Initialize border color Tegra114 and earlier support specifying the color of the border (i.e. the active area of the screen that is not covered by any of the overlay windows). By default this is set to a light blue, so set it to black to comply with the requirements set by atomic modesetting. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 5bab53e0f990..dbb2d00268b2 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -21,6 +21,7 @@ #include struct tegra_dc_soc_info { + bool supports_border_color; bool supports_interlacing; bool supports_cursor; bool supports_block_linear; @@ -1036,6 +1037,9 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, /* program display mode */ tegra_dc_set_timings(dc, mode); + if (dc->soc->supports_border_color) + tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR); + /* interlacing isn't supported yet, so disable it */ if (dc->soc->supports_interlacing) { value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL); @@ -1578,6 +1582,7 @@ static const struct host1x_client_ops dc_client_ops = { }; static const struct tegra_dc_soc_info tegra20_dc_soc_info = { + .supports_border_color = true, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1586,6 +1591,7 @@ static const struct tegra_dc_soc_info tegra20_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra30_dc_soc_info = { + .supports_border_color = true, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1594,6 +1600,7 @@ static const struct tegra_dc_soc_info tegra30_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra114_dc_soc_info = { + .supports_border_color = true, .supports_interlacing = false, .supports_cursor = false, .supports_block_linear = false, @@ -1602,6 +1609,7 @@ static const struct tegra_dc_soc_info tegra114_dc_soc_info = { }; static const struct tegra_dc_soc_info tegra124_dc_soc_info = { + .supports_border_color = false, .supports_interlacing = true, .supports_cursor = true, .supports_block_linear = true, -- cgit v1.2.3 From d700ba7a6657d7c2be76b56996f54f02783456a6 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 15:50:04 +0100 Subject: drm/tegra: dc: Describe register copies Most of the display controller's registers are double-buffered, a few of them are triple-buffered. The ASSEMBLY shadow copy is latched intto the ACTIVE copy for double-buffered registers. For triple-buffered registers the ASSEMBLY copy is first latched into the ARM copy. Latching into the ACTIVE copy happens immediately if the controller is inactive. Otherwise the latching happens on the next frame boundary. The latching of the ASSEMBLY into the ARM copy happens immediately. Latching is controlled by a set of *_ACT_REQ and *_UPDATE bits in the DC_CMD_STATE_CONTROL register. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index dbb2d00268b2..a56fccc428d5 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -53,6 +53,18 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc) tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); } +/* + * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the + * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. + * Latching happens mmediately if the display controller is in STOP mode or + * on the next frame boundary otherwise. + * + * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The + * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits + * are written. When the *_ACT_REQ bits are written, the ARM copy is latched + * into the ACTIVE copy, either immediately if the display controller is in + * STOP mode, or at the next frame boundary otherwise. + */ static void tegra_dc_commit(struct tegra_dc *dc) { tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); -- cgit v1.2.3 From fb35c6b60e6db1e8ae0d7d358b76f3f511bf2707 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 15:55:28 +0100 Subject: drm/tegra: dc: Return planar flag for non-YUV modes This prevents the compiler from warning about using a variable that is possibly uninitialized. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index a56fccc428d5..c2ab1a2da6a3 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -134,6 +134,9 @@ static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) return true; } + if (planar) + *planar = false; + return false; } -- cgit v1.2.3 From 4ee8cee0c5d149939664b25d8fea63ad76493e30 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:25:14 +0100 Subject: drm/tegra: hdmi: Registers are 32-bit Use a sized unsigned 32-bit data type (u32) to store register contents. The HDMI registers are 32 bits wide irrespective of the architecture's data width. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index ffe26547328d..0f122eae7c64 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -31,7 +31,7 @@ struct tegra_hdmi_config { unsigned int num_tmds; unsigned long fuse_override_offset; - unsigned long fuse_override_value; + u32 fuse_override_value; bool has_sor_io_peak_current; }; @@ -85,16 +85,16 @@ enum { HDA, }; -static inline unsigned long tegra_hdmi_readl(struct tegra_hdmi *hdmi, - unsigned long reg) +static inline u32 tegra_hdmi_readl(struct tegra_hdmi *hdmi, + unsigned long offset) { - return readl(hdmi->regs + (reg << 2)); + return readl(hdmi->regs + (offset << 2)); } -static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, unsigned long val, - unsigned long reg) +static inline void tegra_hdmi_writel(struct tegra_hdmi *hdmi, u32 value, + unsigned long offset) { - writel(val, hdmi->regs + (reg << 2)); + writel(value, hdmi->regs + (offset << 2)); } struct tegra_hdmi_audio_config { @@ -455,8 +455,8 @@ static void tegra_hdmi_setup_audio_fs_tables(struct tegra_hdmi *hdmi) for (i = 0; i < ARRAY_SIZE(freqs); i++) { unsigned int f = freqs[i]; unsigned int eight_half; - unsigned long value; unsigned int delta; + u32 value; if (f > 96000) delta = 2; @@ -477,7 +477,7 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) struct device_node *node = hdmi->dev->of_node; const struct tegra_hdmi_audio_config *config; unsigned int offset = 0; - unsigned long value; + u32 value; switch (hdmi->audio_source) { case HDA: @@ -571,9 +571,9 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk) return 0; } -static inline unsigned long tegra_hdmi_subpack(const u8 *ptr, size_t size) +static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) { - unsigned long value = 0; + u32 value = 0; size_t i; for (i = size; i > 0; i--) @@ -587,8 +587,8 @@ static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data, { const u8 *ptr = data; unsigned long offset; - unsigned long value; size_t i, j; + u32 value; switch (ptr[0]) { case HDMI_INFOFRAME_TYPE_AVI: @@ -707,9 +707,9 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi) static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) { struct hdmi_vendor_infoframe frame; - unsigned long value; u8 buffer[10]; ssize_t err; + u32 value; if (!hdmi->stereo) { value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); @@ -738,7 +738,7 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi) static void tegra_hdmi_setup_tmds(struct tegra_hdmi *hdmi, const struct tmds_config *tmds) { - unsigned long value; + u32 value; tegra_hdmi_writel(hdmi, tmds->pll0, HDMI_NV_PDISP_SOR_PLL0); tegra_hdmi_writel(hdmi, tmds->pll1, HDMI_NV_PDISP_SOR_PLL1); @@ -776,8 +776,8 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) struct tegra_hdmi *hdmi = to_hdmi(output); struct device_node *node = hdmi->dev->of_node; unsigned int pulse_start, div82, pclk; - unsigned long value; int retries = 1000; + u32 value; int err; if (hdmi->enabled) @@ -1011,7 +1011,7 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct tegra_hdmi *hdmi = to_hdmi(output); - unsigned long value; + u32 value; if (!hdmi->enabled) return 0; @@ -1117,8 +1117,8 @@ static int tegra_hdmi_show_regs(struct seq_file *s, void *data) return err; #define DUMP_REG(name) \ - seq_printf(s, "%-56s %#05x %08lx\n", #name, name, \ - tegra_hdmi_readl(hdmi, name)) + seq_printf(s, "%-56s %#05x %08x\n", #name, name, \ + tegra_hdmi_readl(hdmi, name)) DUMP_REG(HDMI_CTXSW); DUMP_REG(HDMI_NV_PDISP_SOR_STATE0); -- cgit v1.2.3 From 9c0b4ca1123e5b05daeabaa1879ff1fcebd5f9e1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 12:27:59 +0100 Subject: drm/tegra: dsi: Registers are 32-bit Use a sized unsigned 32-bit data type (u32) to store register contents. The DSI registers are 32 bits wide irrespective of the architecture's data width. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 33f67fd601c6..e967ae1d6ca6 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -77,13 +77,12 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output) return container_of(output, struct tegra_dsi, output); } -static inline unsigned long tegra_dsi_readl(struct tegra_dsi *dsi, - unsigned long reg) +static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg) { return readl(dsi->regs + (reg << 2)); } -static inline void tegra_dsi_writel(struct tegra_dsi *dsi, unsigned long value, +static inline void tegra_dsi_writel(struct tegra_dsi *dsi, u32 value, unsigned long reg) { writel(value, dsi->regs + (reg << 2)); @@ -95,7 +94,7 @@ static int tegra_dsi_show_regs(struct seq_file *s, void *data) struct tegra_dsi *dsi = node->info_ent->data; #define DUMP_REG(name) \ - seq_printf(s, "%-32s %#05x %08lx\n", #name, name, \ + seq_printf(s, "%-32s %#05x %08x\n", #name, name, \ tegra_dsi_readl(dsi, name)) DUMP_REG(DSI_INCR_SYNCPT); @@ -341,7 +340,8 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) { struct mipi_dphy_timing timing; - unsigned long value, period; + unsigned long period; + u32 value; long rate; int err; @@ -728,7 +728,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); struct tegra_dsi *dsi = to_dsi(output); - unsigned long value; + u32 value; int err; if (!dsi->enabled) @@ -888,7 +888,7 @@ static const struct tegra_output_ops dsi_ops = { static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) { - unsigned long value; + u32 value; value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); -- cgit v1.2.3 From 92f0e073ed213a1af673a9ee414339feb9738809 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 16:29:40 +0100 Subject: drm/tegra: dsi: Soft-reset controller on ->disable This reset is necessary to properly clean up the internal state of the controller. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index e967ae1d6ca6..60b802205546 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -724,6 +724,30 @@ static void tegra_dsi_disable(struct tegra_dsi *dsi) usleep_range(5000, 10000); } +static void tegra_dsi_soft_reset(struct tegra_dsi *dsi) +{ + u32 value; + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value &= ~DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + usleep_range(300, 1000); + + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + value |= DSI_POWER_CONTROL_ENABLE; + tegra_dsi_writel(dsi, value, DSI_POWER_CONTROL); + + usleep_range(300, 1000); + + value = tegra_dsi_readl(dsi, DSI_TRIGGER); + if (value) + tegra_dsi_writel(dsi, 0, DSI_TRIGGER); + + if (dsi->slave) + tegra_dsi_soft_reset(dsi->slave); +} + static int tegra_output_dsi_disable(struct tegra_output *output) { struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); @@ -762,6 +786,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output) if (err < 0) dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); + tegra_dsi_soft_reset(dsi); tegra_dsi_disable(dsi); dsi->enabled = false; -- cgit v1.2.3 From 201106d83ee4dc54342e4d8d31a202bf6711a25e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 16:31:48 +0100 Subject: drm/tegra: dsi: Reset across ->exit()/->init() This allows a DRM driver unload/reload cycle to completely reset the DSI controller and may help in situations where it's broken. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 60b802205546..748727bc175b 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -948,6 +948,14 @@ static int tegra_dsi_init(struct host1x_client *client) struct tegra_dsi *dsi = host1x_client_to_dsi(client); int err; + reset_control_deassert(dsi->rst); + + err = tegra_dsi_pad_calibrate(dsi); + if (err < 0) { + dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); + goto reset; + } + /* Gangsters must not register their own outputs. */ if (!dsi->master) { dsi->output.type = TEGRA_OUTPUT_DSI; @@ -968,6 +976,10 @@ static int tegra_dsi_init(struct host1x_client *client) } return 0; + +reset: + reset_control_assert(dsi->rst); + return err; } static int tegra_dsi_exit(struct host1x_client *client) @@ -997,6 +1009,8 @@ static int tegra_dsi_exit(struct host1x_client *client) } } + reset_control_assert(dsi->rst); + return 0; } @@ -1423,13 +1437,6 @@ static int tegra_dsi_probe(struct platform_device *pdev) if (IS_ERR(dsi->rst)) return PTR_ERR(dsi->rst); - err = reset_control_deassert(dsi->rst); - if (err < 0) { - dev_err(&pdev->dev, "failed to bring DSI out of reset: %d\n", - err); - return err; - } - dsi->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(dsi->clk)) { dev_err(&pdev->dev, "cannot get DSI clock\n"); @@ -1495,12 +1502,6 @@ static int tegra_dsi_probe(struct platform_device *pdev) goto disable_vdd; } - err = tegra_dsi_pad_calibrate(dsi); - if (err < 0) { - dev_err(dsi->dev, "MIPI calibration failed: %d\n", err); - goto mipi_free; - } - dsi->host.ops = &tegra_dsi_host_ops; dsi->host.dev = &pdev->dev; -- cgit v1.2.3 From 2dafd63682d0e2be20b913f370d9f29eb0d483f7 Mon Sep 17 00:00:00 2001 From: David Ung Date: Fri, 5 Dec 2014 15:30:05 -0800 Subject: drm/tegra: dsi: Adjust D-PHY timing Compliance testing shows that HS Trail is off by -12%. Increase the HS Trail time to make this test pass. Signed-off-by: David Ung [treding@nvidia.com: update specification references, add comment] Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/mipi-phy.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/mipi-phy.c b/drivers/gpu/drm/tegra/mipi-phy.c index 486d19d589c8..ba2ae6511957 100644 --- a/drivers/gpu/drm/tegra/mipi-phy.c +++ b/drivers/gpu/drm/tegra/mipi-phy.c @@ -12,9 +12,9 @@ #include "mipi-phy.h" /* - * Default D-PHY timings based on MIPI D-PHY specification. Derived from - * the valid ranges specified in Section 5.9 of the D-PHY specification - * with minor adjustments. + * Default D-PHY timings based on MIPI D-PHY specification. Derived from the + * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY + * specification (v1.2) with minor adjustments. */ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, unsigned long period) @@ -34,7 +34,20 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, timing->hszero = 145 + 5 * period; timing->hssettle = 85 + 6 * period; timing->hsskip = 40; - timing->hstrail = max(8 * period, 60 + 4 * period); + + /* + * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40) + * contains this formula as: + * + * T_HS-TRAIL = max(n * 8 * period, 60 + n * 4 * period) + * + * where n = 1 for forward-direction HS mode and n = 4 for reverse- + * direction HS mode. There's only one setting and this function does + * not parameterize on anything other that period, so this code will + * assumes that reverse-direction HS mode is supported and uses n = 4. + */ + timing->hstrail = max(4 * 8 * period, 60 + 4 * 4 * period); + timing->init = 100000; timing->lpx = 60; timing->taget = 5 * timing->lpx; @@ -46,8 +59,8 @@ int mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing, } /* - * Validate D-PHY timing according to MIPI Alliance Specification for D-PHY, - * Section 5.9 "Global Operation Timing Parameters". + * Validate D-PHY timing according to MIPI D-PHY specification (v1.2, Section + * Section 6.9 "Global Operation Timing Parameters"). */ int mipi_dphy_timing_validate(struct mipi_dphy_timing *timing, unsigned long period) -- cgit v1.2.3 From 8643bc6d8e3701734a86d2ae2da32bbfaf1d4535 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Jan 2015 14:01:26 +0300 Subject: drm/tegra: dc: Fix bad irqsave/restore in tegra_dc_finish_page_flip() We can't save two values to the IRQ flags at the same time so the IRQs are not enabled at the end. This kind of bug is easy to miss in testing if the function is normally called with IRQs disabled so we wouldn't enable IRQs anyway. Signed-off-by: Dan Carpenter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index c2ab1a2da6a3..aca886e03b28 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -867,7 +867,7 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) bo = tegra_fb_get_plane(crtc->primary->fb, 0); - spin_lock_irqsave(&dc->lock, flags); + spin_lock(&dc->lock); /* check if new start address has been latched */ tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -875,7 +875,7 @@ static void tegra_dc_finish_page_flip(struct tegra_dc *dc) base = tegra_dc_readl(dc, DC_WINBUF_START_ADDR); tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); - spin_unlock_irqrestore(&dc->lock, flags); + spin_unlock(&dc->lock); if (base == bo->paddr + crtc->primary->fb->offsets[0]) { drm_crtc_send_vblank_event(crtc, dc->event); -- cgit v1.2.3 From 2cb207e413a9ace502edbce51c604e2088b0acb2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 16 Jan 2015 13:43:42 +0300 Subject: drm/tegra: gem: oops in error handling kfree(ERR_PTR(-ENOMEM)) will not work very well. Signed-off-by: Dan Carpenter Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/gem.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 1ccde09d01c8..cfb481943b6b 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -214,10 +214,8 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) for_each_sg(sgt->sgl, s, sgt->nents, i) sg_dma_address(s) = sg_phys(s); - if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) { - sgt = ERR_PTR(-ENOMEM); + if (dma_map_sg(drm->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE) == 0) goto release_sgt; - } bo->sgt = sgt; @@ -226,6 +224,7 @@ static int tegra_bo_get_pages(struct drm_device *drm, struct tegra_bo *bo) release_sgt: sg_free_table(sgt); kfree(sgt); + sgt = ERR_PTR(-ENOMEM); put_pages: drm_gem_put_pages(&bo->gem, bo->pages, false, false); return PTR_ERR(sgt); -- cgit v1.2.3 From 62b9e06321a254c3039966cff831487498e831a5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 21 Nov 2014 17:33:33 +0100 Subject: drm/tegra: Use tegra_commit_dc() in output drivers All output drivers have open-coded variants of this function, so export it to remove some code duplication. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 2 +- drivers/gpu/drm/tegra/drm.h | 1 + drivers/gpu/drm/tegra/dsi.c | 6 ++---- drivers/gpu/drm/tegra/hdmi.c | 6 ++---- drivers/gpu/drm/tegra/rgb.c | 8 +++----- drivers/gpu/drm/tegra/sor.c | 6 ++---- 6 files changed, 11 insertions(+), 18 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index aca886e03b28..dab7ea261e74 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -65,7 +65,7 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc) * into the ACTIVE copy, either immediately if the display controller is in * STOP mode, or at the next frame boundary otherwise. */ -static void tegra_dc_commit(struct tegra_dc *dc) +void tegra_dc_commit(struct tegra_dc *dc) { tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 3a3b2e7b5b3f..5a0e96debcb1 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -177,6 +177,7 @@ struct tegra_dc_window { void tegra_dc_enable_vblank(struct tegra_dc *dc); void tegra_dc_disable_vblank(struct tegra_dc *dc); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); +void tegra_dc_commit(struct tegra_dc *dc); struct tegra_output_ops { int (*enable)(struct tegra_output *output); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 748727bc175b..0ca8ca3775fd 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -658,8 +658,7 @@ static int tegra_output_dsi_enable(struct tegra_output *output) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); /* enable DSI controller */ tegra_dsi_enable(dsi); @@ -778,8 +777,7 @@ static int tegra_output_dsi_disable(struct tegra_output *output) value &= ~DSI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); } err = tegra_dsi_wait_idle(dsi, 100); diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 0f122eae7c64..f118b914293e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -997,8 +997,7 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); /* TODO: add HDCP support */ @@ -1042,8 +1041,7 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) value &= ~HDMI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); } clk_disable_unprepare(hdmi->clk); diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index d6af9be48f42..3b851abbccae 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -123,8 +123,7 @@ static int tegra_output_rgb_enable(struct tegra_output *output) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(rgb->dc); rgb->enabled = true; @@ -148,11 +147,10 @@ static int tegra_output_rgb_disable(struct tegra_output *output) value &= ~DISP_CTRL_MODE_MASK; tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND); - tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(rgb->dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); - tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); + tegra_dc_commit(rgb->dc); + rgb->enabled = false; return 0; diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 7829e81f065d..6a341822abe9 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -267,8 +267,7 @@ static int tegra_sor_wakeup(struct tegra_sor *sor) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); timeout = jiffies + msecs_to_jiffies(250); @@ -1080,8 +1079,7 @@ static int tegra_output_sor_disable(struct tegra_output *output) value &= ~SOR_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); + tegra_dc_commit(dc); } err = tegra_sor_power_down(sor); -- cgit v1.2.3 From 36904adf217ab0755cc2ef3fa186e01fd07a2aca Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 21 Nov 2014 17:35:54 +0100 Subject: drm/tegra: Stop CRTC at CRTC disable time Previously output drivers would all stop the display controller in their disable path. However with the transition to atomic modesetting the display controller needs to be kept running until all planes have been disabled so that software can properly determine (using VBLANK counts) when it is safe to remove the framebuffers associated with the planes. Moving this code into the display controller's disable path also gets rid of the duplication of this into all output drivers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 6 ++++++ drivers/gpu/drm/tegra/dsi.c | 4 ---- drivers/gpu/drm/tegra/hdmi.c | 4 ---- drivers/gpu/drm/tegra/rgb.c | 4 ---- drivers/gpu/drm/tegra/sor.c | 4 ---- 5 files changed, 6 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index dab7ea261e74..915bbdc350ac 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -940,6 +940,7 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) struct tegra_dc *dc = to_tegra_dc(crtc); struct drm_device *drm = crtc->dev; struct drm_plane *plane; + u32 value; drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { if (plane->crtc == crtc) { @@ -953,6 +954,11 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) } } + /* stop the display controller */ + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + drm_crtc_vblank_off(crtc); tegra_dc_commit(dc); } diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 0ca8ca3775fd..4c0dfe92402a 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -769,10 +769,6 @@ static int tegra_output_dsi_disable(struct tegra_output *output) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~DSI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index f118b914293e..d4c635148cc7 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1033,10 +1033,6 @@ static int tegra_output_hdmi_disable(struct tegra_output *output) tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); */ - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~HDMI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 3b851abbccae..39b8d5fe04b2 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -143,10 +143,6 @@ static int tegra_output_rgb_disable(struct tegra_output *output) PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND); - tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); tegra_dc_commit(rgb->dc); diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 6a341822abe9..1fe801ee8eb0 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1071,10 +1071,6 @@ static int tegra_output_sor_disable(struct tegra_output *output) tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); */ - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~SOR_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); -- cgit v1.2.3 From 86df256f301fc5f215160d8c8e7093f605d40a14 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:03:53 +0100 Subject: drm/tegra: dc: Wait for idle when disabled When disabling the display controller, stop it and wait for it to become idle. Doing so ensures that no further accesses to the framebuffer occur and the buffers can be safely unmapped or freed. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 70 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 915bbdc350ac..f8ad5ed6e0e7 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -53,6 +53,26 @@ static void tegra_dc_cursor_commit(struct tegra_dc *dc) tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); } +/* + * Reads the active copy of a register. This takes the dc->lock spinlock to + * prevent races with the VBLANK processing which also needs access to the + * active copy of some registers. + */ +static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&dc->lock, flags); + + tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS); + value = tegra_dc_readl(dc, offset); + tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS); + + spin_unlock_irqrestore(&dc->lock, flags); + return value; +} + /* * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy. @@ -935,12 +955,47 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .destroy = tegra_dc_destroy, }; +static void tegra_dc_stop(struct tegra_dc *dc) +{ + u32 value; + + /* stop the display controller */ + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + tegra_dc_commit(dc); +} + +static bool tegra_dc_idle(struct tegra_dc *dc) +{ + u32 value; + + value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND); + + return (value & DISP_CTRL_MODE_MASK) == 0; +} + +static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) +{ + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_before(jiffies, timeout)) { + if (tegra_dc_idle(dc)) + return 0; + + usleep_range(1000, 2000); + } + + dev_dbg(dc->dev, "timeout waiting for DC to become idle\n"); + return -ETIMEDOUT; +} + static void tegra_crtc_disable(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); struct drm_device *drm = crtc->dev; struct drm_plane *plane; - u32 value; drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { if (plane->crtc == crtc) { @@ -954,10 +1009,15 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) } } - /* stop the display controller */ - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + if (!tegra_dc_idle(dc)) { + tegra_dc_stop(dc); + + /* + * Ignore the return value, there isn't anything useful to do + * in case this fails. + */ + tegra_dc_wait_idle(dc, 100); + } drm_crtc_vblank_off(crtc); tegra_dc_commit(dc); -- cgit v1.2.3 From f99142149095b3172c255fc792e8ebb84f38f182 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 26 Nov 2014 13:03:57 +0100 Subject: drm/tegra: Move tegra_drm_mode_funcs to the core This structure will be extended using non-framebuffer related callbacks in subsequent patches, so it should move to a more central location. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 15 +++++++++++++++ drivers/gpu/drm/tegra/drm.h | 4 ++++ drivers/gpu/drm/tegra/fb.c | 25 ++++--------------------- 3 files changed, 23 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 219f314cc162..e29d9146b7c7 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -24,6 +24,13 @@ struct tegra_drm_file { struct list_head contexts; }; +static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { + .fb_create = tegra_fb_create, +#ifdef CONFIG_DRM_TEGRA_FBDEV + .output_poll_changed = tegra_fb_output_poll_changed, +#endif +}; + static int tegra_drm_load(struct drm_device *drm, unsigned long flags) { struct host1x_device *device = to_host1x_device(drm->dev); @@ -52,6 +59,14 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm_mode_config_init(drm); + drm->mode_config.min_width = 0; + drm->mode_config.min_height = 0; + + drm->mode_config.max_width = 4096; + drm->mode_config.max_height = 4096; + + drm->mode_config.funcs = &tegra_drm_mode_funcs; + err = tegra_drm_fb_prepare(drm); if (err < 0) goto config; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 5a0e96debcb1..286a970a4432 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -292,12 +292,16 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer, bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer); int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer, struct tegra_bo_tiling *tiling); +struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, + struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd); int tegra_drm_fb_prepare(struct drm_device *drm); void tegra_drm_fb_free(struct drm_device *drm); int tegra_drm_fb_init(struct drm_device *drm); void tegra_drm_fb_exit(struct drm_device *drm); #ifdef CONFIG_DRM_TEGRA_FBDEV void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); +void tegra_fb_output_poll_changed(struct drm_device *drm); #endif extern struct platform_driver tegra_dc_driver; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index e9c715d89261..397fb34d5d5b 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -129,9 +129,9 @@ static struct tegra_fb *tegra_fb_alloc(struct drm_device *drm, return fb; } -static struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, - struct drm_file *file, - struct drm_mode_fb_cmd2 *cmd) +struct drm_framebuffer *tegra_fb_create(struct drm_device *drm, + struct drm_file *file, + struct drm_mode_fb_cmd2 *cmd) { unsigned int hsub, vsub, i; struct tegra_bo *planes[4]; @@ -377,7 +377,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev) drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev->base); } -static void tegra_fb_output_poll_changed(struct drm_device *drm) +void tegra_fb_output_poll_changed(struct drm_device *drm) { struct tegra_drm *tegra = drm->dev_private; @@ -386,28 +386,11 @@ static void tegra_fb_output_poll_changed(struct drm_device *drm) } #endif -static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { - .fb_create = tegra_fb_create, -#ifdef CONFIG_DRM_TEGRA_FBDEV - .output_poll_changed = tegra_fb_output_poll_changed, -#endif -}; - int tegra_drm_fb_prepare(struct drm_device *drm) { #ifdef CONFIG_DRM_TEGRA_FBDEV struct tegra_drm *tegra = drm->dev_private; -#endif - drm->mode_config.min_width = 0; - drm->mode_config.min_height = 0; - - drm->mode_config.max_width = 4096; - drm->mode_config.max_height = 4096; - - drm->mode_config.funcs = &tegra_drm_mode_funcs; - -#ifdef CONFIG_DRM_TEGRA_FBDEV tegra->fbdev = tegra_fbdev_create(drm); if (IS_ERR(tegra->fbdev)) return PTR_ERR(tegra->fbdev); -- cgit v1.2.3 From 50a246aa13b80c8310dc5a82be639a60868f667e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 17 Nov 2014 15:14:48 +0100 Subject: drm/tegra: dc: No longer disable planes at CRTC disable The DRM core should take care of disabling all unneeded planes, so there is no need to do this explicitly. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index f8ad5ed6e0e7..d51173056a9e 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -994,20 +994,6 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) static void tegra_crtc_disable(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); - struct drm_device *drm = crtc->dev; - struct drm_plane *plane; - - drm_for_each_legacy_plane(plane, &drm->mode_config.plane_list) { - if (plane->crtc == crtc) { - tegra_window_plane_disable(plane); - plane->crtc = NULL; - - if (plane->fb) { - drm_framebuffer_unreference(plane->fb); - plane->fb = NULL; - } - } - } if (!tegra_dc_idle(dc)) { tegra_dc_stop(dc); -- cgit v1.2.3 From 132085d84fca65c46b56dc0f8233910906a15e8d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Nov 2014 15:38:40 +0100 Subject: drm/tegra: Convert output midlayer to helpers The output layer was initially designed to help reduce the amount of code duplicated in output drivers. An unfortunate side-effect of that was that it turned into a midlayer and it became difficult to make the output drivers work without bending over backwards to fit into the midlayer. This commit starts to convert the midlayer into a helper library by exporting most of the common functions so that they can be used by the output drivers directly. Doing so will allow output drivers to reuse common code paths but more easily override them where necessary. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.h | 9 +++++++++ drivers/gpu/drm/tegra/output.c | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 286a970a4432..bf749ac4a344 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -272,6 +272,15 @@ int tegra_output_remove(struct tegra_output *output); int tegra_output_init(struct drm_device *drm, struct tegra_output *output); int tegra_output_exit(struct tegra_output *output); +int tegra_output_connector_get_modes(struct drm_connector *connector); +struct drm_encoder * +tegra_output_connector_best_encoder(struct drm_connector *connector); +enum drm_connector_status +tegra_output_connector_detect(struct drm_connector *connector, bool force); +void tegra_output_connector_destroy(struct drm_connector *connector); + +void tegra_output_encoder_destroy(struct drm_encoder *encoder); + /* from dpaux.c */ struct tegra_dpaux; struct drm_dp_link; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 0e4042ce904f..54f8392a7c3c 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -12,7 +12,7 @@ #include #include "drm.h" -static int tegra_connector_get_modes(struct drm_connector *connector) +int tegra_output_connector_get_modes(struct drm_connector *connector) { struct tegra_output *output = connector_to_output(connector); struct edid *edid = NULL; @@ -57,8 +57,8 @@ static int tegra_connector_mode_valid(struct drm_connector *connector, return status; } -static struct drm_encoder * -tegra_connector_best_encoder(struct drm_connector *connector) +struct drm_encoder * +tegra_output_connector_best_encoder(struct drm_connector *connector) { struct tegra_output *output = connector_to_output(connector); @@ -66,13 +66,13 @@ tegra_connector_best_encoder(struct drm_connector *connector) } static const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = tegra_connector_get_modes, + .get_modes = tegra_output_connector_get_modes, .mode_valid = tegra_connector_mode_valid, - .best_encoder = tegra_connector_best_encoder, + .best_encoder = tegra_output_connector_best_encoder, }; -static enum drm_connector_status -tegra_connector_detect(struct drm_connector *connector, bool force) +enum drm_connector_status +tegra_output_connector_detect(struct drm_connector *connector, bool force) { struct tegra_output *output = connector_to_output(connector); enum drm_connector_status status = connector_status_unknown; @@ -98,7 +98,7 @@ tegra_connector_detect(struct drm_connector *connector, bool force) return status; } -static void tegra_connector_destroy(struct drm_connector *connector) +void tegra_output_connector_destroy(struct drm_connector *connector) { drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -106,18 +106,18 @@ static void tegra_connector_destroy(struct drm_connector *connector) static const struct drm_connector_funcs connector_funcs = { .dpms = drm_helper_connector_dpms, - .detect = tegra_connector_detect, + .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = tegra_connector_destroy, + .destroy = tegra_output_connector_destroy, }; -static void tegra_encoder_destroy(struct drm_encoder *encoder) +void tegra_output_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); } static const struct drm_encoder_funcs encoder_funcs = { - .destroy = tegra_encoder_destroy, + .destroy = tegra_output_encoder_destroy, }; static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode) -- cgit v1.2.3 From d5bae6f33ee98cf4c6939c4b8db2fc76c1eed720 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Dec 2014 15:12:28 +0100 Subject: drm/tegra: output: Make ->setup_clock() optional In order to transition output drivers to using the struct tegra_output as a helper rather than midlayer, make this callback optional. Instead drivers should implement the equivalent as part of ->mode_fixup(). For the conversion to atomic modesetting a new callback ->atomic_check() should be implemented that updates the display controller's state with the corresponding parent clock, rate and shift clock divider. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 10 +++++++++- drivers/gpu/drm/tegra/drm.h | 10 ---------- 2 files changed, 9 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index d51173056a9e..e35e10758556 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1066,11 +1066,19 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc, if (!output) return -ENODEV; + /* + * The ->setup_clock() callback is optional, but if encoders don't + * implement it they most likely need to do the equivalent within the + * ->mode_fixup() callback. + */ + if (!output->ops || !output->ops->setup_clock) + return 0; + /* * This assumes that the parent clock is pll_d_out0 or pll_d2_out * respectively, each of which divides the base pll_d by 2. */ - err = tegra_output_setup_clock(output, dc->clk, pclk, &div); + err = output->ops->setup_clock(output, dc->clk, pclk, &div); if (err < 0) { dev_err(dc->dev, "failed to setup clock: %ld\n", err); return err; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index bf749ac4a344..d7433976a40b 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -240,16 +240,6 @@ static inline int tegra_output_disable(struct tegra_output *output) return output ? -ENOSYS : -EINVAL; } -static inline int tegra_output_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *div) -{ - if (output && output->ops && output->ops->setup_clock) - return output->ops->setup_clock(output, clk, pclk, div); - - return output ? -ENOSYS : -EINVAL; -} - static inline int tegra_output_check_mode(struct tegra_output *output, struct drm_display_mode *mode, enum drm_mode_status *status) -- cgit v1.2.3 From c5a107d3279734c3599136696b6790add9e8e798 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Dec 2014 15:15:06 +0100 Subject: drm/tegra: Add tegra_dc_setup_clock() helper This is a small helper that performs the basic steps required by all output drivers to prepare the display controller for use with a given encoder. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 20 ++++++++++++++++++++ drivers/gpu/drm/tegra/drm.h | 2 ++ 2 files changed, 22 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index e35e10758556..90909883e739 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1092,6 +1092,26 @@ static int tegra_crtc_setup_clk(struct drm_crtc *crtc, return 0; } +int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, + unsigned long pclk, unsigned int div) +{ + u32 value; + int err; + + err = clk_set_parent(dc->clk, parent); + if (err < 0) { + dev_err(dc->dev, "failed to set parent clock: %d\n", err); + return err; + } + + DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div); + + value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; + tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); + + return 0; +} + static int tegra_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted, diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index d7433976a40b..e1374ec2b76e 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -178,6 +178,8 @@ void tegra_dc_enable_vblank(struct tegra_dc *dc); void tegra_dc_disable_vblank(struct tegra_dc *dc); void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); void tegra_dc_commit(struct tegra_dc *dc); +int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, + unsigned long pclk, unsigned int div); struct tegra_output_ops { int (*enable)(struct tegra_output *output); -- cgit v1.2.3 From 3b0e58554873d1034beef737f15c7aa46492ff98 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 16 Dec 2014 18:30:16 +0100 Subject: drm/tegra: rgb: Demidlayer Implement encoder and connector within the RGB driver itself using the Tegra output helpers rather than using the Tegra output as midlayer. By doing so one level of indirection is removed and output drivers become more flexible while keeping the majority of the advantages provided by the common output helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 24 ++++ drivers/gpu/drm/tegra/drm.h | 1 - drivers/gpu/drm/tegra/output.c | 5 - drivers/gpu/drm/tegra/rgb.c | 243 +++++++++++++++++++++++------------------ 4 files changed, 161 insertions(+), 112 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 90909883e739..286cc8ce0c66 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -994,6 +994,7 @@ static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout) static void tegra_crtc_disable(struct drm_crtc *crtc) { struct tegra_dc *dc = to_tegra_dc(crtc); + u32 value; if (!tegra_dc_idle(dc)) { tegra_dc_stop(dc); @@ -1005,6 +1006,29 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) tegra_dc_wait_idle(dc, 100); } + /* + * This should really be part of the RGB encoder driver, but clearing + * these bits has the side-effect of stopping the display controller. + * When that happens no VBLANK interrupts will be raised. At the same + * time the encoder is disabled before the display controller, so the + * above code is always going to timeout waiting for the controller + * to go idle. + * + * Given the close coupling between the RGB encoder and the display + * controller doing it here is still kind of okay. None of the other + * encoder drivers require these bits to be cleared. + * + * XXX: Perhaps given that the display controller is switched off at + * this point anyway maybe clearing these bits isn't even useful for + * the RGB encoder? + */ + if (dc->rgb) { + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + } + drm_crtc_vblank_off(crtc); tegra_dc_commit(dc); } diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index e1374ec2b76e..dbc1f83327ea 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -193,7 +193,6 @@ struct tegra_output_ops { }; enum tegra_output_type { - TEGRA_OUTPUT_RGB, TEGRA_OUTPUT_HDMI, TEGRA_OUTPUT_DSI, TEGRA_OUTPUT_EDP, diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 54f8392a7c3c..f01ddfaa4c99 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -274,11 +274,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) int connector, encoder; switch (output->type) { - case TEGRA_OUTPUT_RGB: - connector = DRM_MODE_CONNECTOR_LVDS; - encoder = DRM_MODE_ENCODER_LVDS; - break; - case TEGRA_OUTPUT_HDMI: connector = DRM_MODE_CONNECTOR_HDMIA; encoder = DRM_MODE_ENCODER_TMDS; diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 39b8d5fe04b2..30d7ae02ace8 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -9,6 +9,8 @@ #include +#include + #include "drm.h" #include "dc.h" @@ -85,13 +87,99 @@ static void tegra_dc_write_regs(struct tegra_dc *dc, tegra_dc_writel(dc, table[i].value, table[i].offset); } -static int tegra_output_rgb_enable(struct tegra_output *output) +static void tegra_rgb_connector_dpms(struct drm_connector *connector, + int mode) +{ +} + +static const struct drm_connector_funcs tegra_rgb_connector_funcs = { + .dpms = tegra_rgb_connector_dpms, + .detect = tegra_output_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_output_connector_destroy, +}; + +static enum drm_mode_status +tegra_rgb_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* + * FIXME: For now, always assume that the mode is okay. There are + * unresolved issues with clk_round_rate(), which doesn't always + * reliably report whether a frequency can be set or not. + */ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs tegra_rgb_connector_helper_funcs = { + .get_modes = tegra_output_connector_get_modes, + .mode_valid = tegra_rgb_connector_mode_valid, + .best_encoder = tegra_output_connector_best_encoder, +}; + +static const struct drm_encoder_funcs tegra_rgb_encoder_funcs = { + .destroy = tegra_output_encoder_destroy, +}; + +static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) { + struct tegra_output *output = encoder_to_output(encoder); + unsigned long pclk = mode->clock * 1000; struct tegra_rgb *rgb = to_rgb(output); - unsigned long value; + unsigned int div; + int err; - if (rgb->enabled) - return 0; + /* + * We may not want to change the frequency of the parent clock, since + * it may be a parent for other peripherals. This is due to the fact + * that on Tegra20 there's only a single clock dedicated to display + * (pll_d_out0), whereas later generations have a second one that can + * be used to independently drive a second output (pll_d2_out0). + * + * As a way to support multiple outputs on Tegra20 as well, pll_p is + * typically used as the parent clock for the display controllers. + * But this comes at a cost: pll_p is the parent of several other + * peripherals, so its frequency shouldn't change out of the blue. + * + * The best we can do at this point is to use the shift clock divider + * and hope that the desired frequency can be matched (or at least + * matched sufficiently close that the panel will still work). + */ + div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2; + + err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div); + if (err < 0) { + dev_err(output->dev, "failed to setup DC clock: %d\n", err); + return false; + } + + return true; +} + +static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void tegra_rgb_encoder_commit(struct drm_encoder *encoder) +{ +} + +static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_rgb *rgb = to_rgb(output); + u32 value; + + if (output->panel) + drm_panel_prepare(output->panel); tegra_dc_write_regs(rgb->dc, rgb_enable, ARRAY_SIZE(rgb_enable)); @@ -125,88 +213,31 @@ static int tegra_output_rgb_enable(struct tegra_output *output) tegra_dc_commit(rgb->dc); - rgb->enabled = true; - - return 0; + if (output->panel) + drm_panel_enable(output->panel); } -static int tegra_output_rgb_disable(struct tegra_output *output) +static void tegra_rgb_encoder_disable(struct drm_encoder *encoder) { + struct tegra_output *output = encoder_to_output(encoder); struct tegra_rgb *rgb = to_rgb(output); - unsigned long value; - if (!rgb->enabled) - return 0; - - value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + if (output->panel) + drm_panel_disable(output->panel); tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); - tegra_dc_commit(rgb->dc); - - rgb->enabled = false; - - return 0; + if (output->panel) + drm_panel_unprepare(output->panel); } -static int tegra_output_rgb_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *div) -{ - struct tegra_rgb *rgb = to_rgb(output); - int err; - - err = clk_set_parent(clk, rgb->clk_parent); - if (err < 0) { - dev_err(output->dev, "failed to set parent: %d\n", err); - return err; - } - - /* - * We may not want to change the frequency of the parent clock, since - * it may be a parent for other peripherals. This is due to the fact - * that on Tegra20 there's only a single clock dedicated to display - * (pll_d_out0), whereas later generations have a second one that can - * be used to independently drive a second output (pll_d2_out0). - * - * As a way to support multiple outputs on Tegra20 as well, pll_p is - * typically used as the parent clock for the display controllers. - * But this comes at a cost: pll_p is the parent of several other - * peripherals, so its frequency shouldn't change out of the blue. - * - * The best we can do at this point is to use the shift clock divider - * and hope that the desired frequency can be matched (or at least - * matched sufficiently close that the panel will still work). - */ - - *div = ((clk_get_rate(clk) * 2) / pclk) - 2; - - return 0; -} - -static int tegra_output_rgb_check_mode(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status) -{ - /* - * FIXME: For now, always assume that the mode is okay. There are - * unresolved issues with clk_round_rate(), which doesn't always - * reliably report whether a frequency can be set or not. - */ - - *status = MODE_OK; - - return 0; -} - -static const struct tegra_output_ops rgb_ops = { - .enable = tegra_output_rgb_enable, - .disable = tegra_output_rgb_disable, - .setup_clock = tegra_output_rgb_setup_clock, - .check_mode = tegra_output_rgb_check_mode, +static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = { + .dpms = tegra_rgb_encoder_dpms, + .mode_fixup = tegra_rgb_encoder_mode_fixup, + .prepare = tegra_rgb_encoder_prepare, + .commit = tegra_rgb_encoder_commit, + .mode_set = tegra_rgb_encoder_mode_set, + .disable = tegra_rgb_encoder_disable, }; int tegra_dc_rgb_probe(struct tegra_dc *dc) @@ -265,55 +296,55 @@ int tegra_dc_rgb_remove(struct tegra_dc *dc) if (err < 0) return err; + dc->rgb = NULL; + return 0; } int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) { - struct tegra_rgb *rgb = to_rgb(dc->rgb); + struct tegra_output *output = dc->rgb; int err; if (!dc->rgb) return -ENODEV; - rgb->output.type = TEGRA_OUTPUT_RGB; - rgb->output.ops = &rgb_ops; - - err = tegra_output_init(dc->base.dev, &rgb->output); - if (err < 0) { - dev_err(dc->dev, "output setup failed: %d\n", err); - return err; + drm_connector_init(drm, &output->connector, &tegra_rgb_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(&output->connector, + &tegra_rgb_connector_helper_funcs); + output->connector.dpms = DRM_MODE_DPMS_OFF; + + if (output->panel) { + err = drm_panel_attach(output->panel, &output->connector); + if (err < 0) + dev_err(output->dev, "failed to attach panel: %d\n", + err); } + drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(&output->encoder, + &tegra_rgb_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&output->connector, + &output->encoder); + drm_connector_register(&output->connector); + /* - * By default, outputs can be associated with each display controller. - * RGB outputs are an exception, so we make sure they can be attached - * to only their parent display controller. + * Other outputs can be attached to either display controller. The RGB + * outputs are an exception and work only with their parent display + * controller. */ - rgb->output.encoder.possible_crtcs = drm_crtc_mask(&dc->base); + output->encoder.possible_crtcs = drm_crtc_mask(&dc->base); return 0; } int tegra_dc_rgb_exit(struct tegra_dc *dc) { - if (dc->rgb) { - int err; - - err = tegra_output_disable(dc->rgb); - if (err < 0) { - dev_err(dc->dev, "output failed to disable: %d\n", err); - return err; - } - - err = tegra_output_exit(dc->rgb); - if (err < 0) { - dev_err(dc->dev, "output cleanup failed: %d\n", err); - return err; - } - - dc->rgb = NULL; - } + if (!dc->rgb) + return 0; - return 0; + return tegra_output_exit(dc->rgb); } -- cgit v1.2.3 From 596827196f5fad8ac77201058978128f6c06f234 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Nov 2014 16:50:59 +0100 Subject: drm/tegra: hdmi: Demidlayer Implement encoder and connector within the HDMI driver itself using the Tegra output helpers rather than using the Tegra output as midlayer. By doing so one level of indirection is removed and output drivers become more flexible while keeping the majority of the advantages provided by the common output helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.h | 1 - drivers/gpu/drm/tegra/hdmi.c | 280 +++++++++++++++++++++-------------------- drivers/gpu/drm/tegra/output.c | 5 - 3 files changed, 147 insertions(+), 139 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index dbc1f83327ea..95b6aebfaf00 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -193,7 +193,6 @@ struct tegra_output_ops { }; enum tegra_output_type { - TEGRA_OUTPUT_HDMI, TEGRA_OUTPUT_DSI, TEGRA_OUTPUT_EDP, }; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index d4c635148cc7..056bb2c1c426 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -9,10 +9,14 @@ #include #include +#include #include #include #include +#include +#include + #include "hdmi.h" #include "drm.h" #include "dc.h" @@ -40,7 +44,6 @@ struct tegra_hdmi { struct host1x_client client; struct tegra_output output; struct device *dev; - bool enabled; struct regulator *hdmi; struct regulator *pll; @@ -768,53 +771,107 @@ static bool tegra_output_is_hdmi(struct tegra_output *output) return drm_detect_hdmi_monitor(edid); } -static int tegra_output_hdmi_enable(struct tegra_output *output) +static void tegra_hdmi_connector_dpms(struct drm_connector *connector, + int mode) { - unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct drm_display_mode *mode = &dc->base.mode; +} + +static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { + .dpms = tegra_hdmi_connector_dpms, + .detect = tegra_output_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_output_connector_destroy, +}; + +static enum drm_mode_status +tegra_hdmi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct tegra_output *output = connector_to_output(connector); struct tegra_hdmi *hdmi = to_hdmi(output); - struct device_node *node = hdmi->dev->of_node; - unsigned int pulse_start, div82, pclk; - int retries = 1000; - u32 value; - int err; + unsigned long pclk = mode->clock * 1000; + enum drm_mode_status status = MODE_OK; + struct clk *parent; + long err; - if (hdmi->enabled) - return 0; + parent = clk_get_parent(hdmi->clk_parent); - hdmi->dvi = !tegra_output_is_hdmi(output); + err = clk_round_rate(parent, pclk * 4); + if (err <= 0) + status = MODE_NOCLOCK; - pclk = mode->clock * 1000; - h_sync_width = mode->hsync_end - mode->hsync_start; - h_back_porch = mode->htotal - mode->hsync_end; - h_front_porch = mode->hsync_start - mode->hdisplay; + return status; +} - err = regulator_enable(hdmi->pll); +static const struct drm_connector_helper_funcs +tegra_hdmi_connector_helper_funcs = { + .get_modes = tegra_output_connector_get_modes, + .mode_valid = tegra_hdmi_connector_mode_valid, + .best_encoder = tegra_output_connector_best_encoder, +}; + +static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = { + .destroy = tegra_output_encoder_destroy, +}; + +static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + struct tegra_hdmi *hdmi = to_hdmi(output); + unsigned long pclk = mode->clock * 1000; + int err; + + err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0); if (err < 0) { - dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err); - return err; + dev_err(output->dev, "failed to setup DC clock: %d\n", err); + return false; } - err = regulator_enable(hdmi->vdd); + err = clk_set_rate(hdmi->clk_parent, pclk); if (err < 0) { - dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err); - return err; + dev_err(output->dev, "failed to set clock rate to %lu Hz\n", + pclk); + return false; } - err = clk_set_rate(hdmi->clk, pclk); - if (err < 0) - return err; + return true; +} - err = clk_prepare_enable(hdmi->clk); - if (err < 0) { - dev_err(hdmi->dev, "failed to enable clock: %d\n", err); - return err; - } +static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder) +{ +} - reset_control_assert(hdmi->rst); - usleep_range(1000, 2000); - reset_control_deassert(hdmi->rst); +static void tegra_hdmi_encoder_commit(struct drm_encoder *encoder) +{ +} + +static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + unsigned int h_sync_width, h_front_porch, h_back_porch, i, rekey; + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + struct device_node *node = output->dev->of_node; + struct tegra_hdmi *hdmi = to_hdmi(output); + unsigned int pulse_start, div82, pclk; + int retries = 1000; + u32 value; + int err; + + hdmi->dvi = !tegra_output_is_hdmi(output); + + pclk = mode->clock * 1000; + h_sync_width = mode->hsync_end - mode->hsync_start; + h_back_porch = mode->htotal - mode->hsync_end; + h_front_porch = mode->hsync_start - mode->hdisplay; /* power up sequence */ value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_SOR_PLL0); @@ -1000,104 +1057,33 @@ static int tegra_output_hdmi_enable(struct tegra_output *output) tegra_dc_commit(dc); /* TODO: add HDCP support */ - - hdmi->enabled = true; - - return 0; } -static int tegra_output_hdmi_disable(struct tegra_output *output) +static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct tegra_hdmi *hdmi = to_hdmi(output); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); u32 value; - if (!hdmi->enabled) - return 0; - /* * The following accesses registers of the display controller, so make * sure it's only executed when the output is attached to one. */ if (dc) { - /* - * XXX: We can't do this here because it causes HDMI to go - * into an erroneous state with the result that HDMI won't - * properly work once disabled. See also a similar symptom - * for the SOR output. - */ - /* - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - */ - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~HDMI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); tegra_dc_commit(dc); } - - clk_disable_unprepare(hdmi->clk); - reset_control_assert(hdmi->rst); - regulator_disable(hdmi->vdd); - regulator_disable(hdmi->pll); - - hdmi->enabled = false; - - return 0; -} - -static int tegra_output_hdmi_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *div) -{ - struct tegra_hdmi *hdmi = to_hdmi(output); - int err; - - err = clk_set_parent(clk, hdmi->clk_parent); - if (err < 0) { - dev_err(output->dev, "failed to set parent: %d\n", err); - return err; - } - - err = clk_set_rate(hdmi->clk_parent, pclk); - if (err < 0) - dev_err(output->dev, "failed to set clock rate to %lu Hz\n", - pclk); - - *div = 0; - - return 0; -} - -static int tegra_output_hdmi_check_mode(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status) -{ - struct tegra_hdmi *hdmi = to_hdmi(output); - unsigned long pclk = mode->clock * 1000; - struct clk *parent; - long err; - - parent = clk_get_parent(hdmi->clk_parent); - - err = clk_round_rate(parent, pclk * 4); - if (err <= 0) - *status = MODE_NOCLOCK; - else - *status = MODE_OK; - - return 0; } -static const struct tegra_output_ops hdmi_ops = { - .enable = tegra_output_hdmi_enable, - .disable = tegra_output_hdmi_disable, - .setup_clock = tegra_output_hdmi_setup_clock, - .check_mode = tegra_output_hdmi_check_mode, +static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { + .dpms = tegra_hdmi_encoder_dpms, + .mode_fixup = tegra_hdmi_encoder_mode_fixup, + .prepare = tegra_hdmi_encoder_prepare, + .commit = tegra_hdmi_encoder_commit, + .mode_set = tegra_hdmi_encoder_mode_set, + .disable = tegra_hdmi_encoder_disable, }; static int tegra_hdmi_show_regs(struct seq_file *s, void *data) @@ -1345,15 +1331,28 @@ static int tegra_hdmi_init(struct host1x_client *client) struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; - hdmi->output.type = TEGRA_OUTPUT_HDMI; hdmi->output.dev = client->dev; - hdmi->output.ops = &hdmi_ops; - err = tegra_output_init(drm, &hdmi->output); - if (err < 0) { - dev_err(client->dev, "output setup failed: %d\n", err); - return err; - } + drm_connector_init(drm, &hdmi->output.connector, + &tegra_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + drm_connector_helper_add(&hdmi->output.connector, + &tegra_hdmi_connector_helper_funcs); + hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF; + + drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(&hdmi->output.encoder, + &tegra_hdmi_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&hdmi->output.connector, + &hdmi->output.encoder); + drm_connector_register(&hdmi->output.connector); + + hdmi->output.encoder.possible_crtcs = 0x3; + + if (gpio_is_valid(hdmi->output.hpd_gpio)) + enable_irq(hdmi->output.hpd_irq); if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_hdmi_debugfs_init(hdmi, drm->primary); @@ -1368,6 +1367,26 @@ static int tegra_hdmi_init(struct host1x_client *client) return err; } + err = regulator_enable(hdmi->pll); + if (err < 0) { + dev_err(hdmi->dev, "failed to enable PLL regulator: %d\n", err); + return err; + } + + err = regulator_enable(hdmi->vdd); + if (err < 0) { + dev_err(hdmi->dev, "failed to enable VDD regulator: %d\n", err); + return err; + } + + err = clk_prepare_enable(hdmi->clk); + if (err < 0) { + dev_err(hdmi->dev, "failed to enable clock: %d\n", err); + return err; + } + + reset_control_deassert(hdmi->rst); + return 0; } @@ -1376,6 +1395,13 @@ static int tegra_hdmi_exit(struct host1x_client *client) struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); int err; + tegra_output_exit(&hdmi->output); + + clk_disable_unprepare(hdmi->clk); + reset_control_assert(hdmi->rst); + + regulator_disable(hdmi->vdd); + regulator_disable(hdmi->pll); regulator_disable(hdmi->hdmi); if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -1385,18 +1411,6 @@ static int tegra_hdmi_exit(struct host1x_client *client) err); } - err = tegra_output_disable(&hdmi->output); - if (err < 0) { - dev_err(client->dev, "output failed to disable: %d\n", err); - return err; - } - - err = tegra_output_exit(&hdmi->output); - if (err < 0) { - dev_err(client->dev, "output cleanup failed: %d\n", err); - return err; - } - return 0; } diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index f01ddfaa4c99..c7ebe8ea3868 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -274,11 +274,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) int connector, encoder; switch (output->type) { - case TEGRA_OUTPUT_HDMI: - connector = DRM_MODE_CONNECTOR_HDMIA; - encoder = DRM_MODE_ENCODER_TMDS; - break; - case TEGRA_OUTPUT_DSI: connector = DRM_MODE_CONNECTOR_DSI; encoder = DRM_MODE_ENCODER_DSI; -- cgit v1.2.3 From 5b901e78b24539f4a1e194e8058f26ab38623c37 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 2 Dec 2014 17:30:23 +0100 Subject: drm/tegra: dsi: Demidlayer Implement encoder and connector within the DSI driver itself using the Tegra output helpers rather than using the Tegra output as midlayer. By doing so one level of indirection is removed and output drivers become more flexible while keeping the majority of the advantages provided by the common output helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.h | 1 - drivers/gpu/drm/tegra/dsi.c | 358 ++++++++++++++++++++++------------------- drivers/gpu/drm/tegra/output.c | 5 - 3 files changed, 195 insertions(+), 169 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 95b6aebfaf00..ae3daa436ee6 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -193,7 +193,6 @@ struct tegra_output_ops { }; enum tegra_output_type { - TEGRA_OUTPUT_DSI, TEGRA_OUTPUT_EDP, }; diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 4c0dfe92402a..0b476e1c005a 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -51,7 +51,6 @@ struct tegra_dsi { struct mipi_dsi_host host; struct regulator *vdd; - bool enabled; unsigned int video_fifo_depth; unsigned int host_fifo_depth; @@ -628,46 +627,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, return 0; } -static int tegra_output_dsi_enable(struct tegra_output *output) -{ - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - const struct drm_display_mode *mode = &dc->base.mode; - struct tegra_dsi *dsi = to_dsi(output); - u32 value; - int err; - - if (dsi->enabled) - return 0; - - err = tegra_dsi_configure(dsi, dc->pipe, mode); - if (err < 0) - return err; - - /* enable display controller */ - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= DSI_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - tegra_dc_commit(dc); - - /* enable DSI controller */ - tegra_dsi_enable(dsi); - - dsi->enabled = true; - - return 0; -} - static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) { u32 value; @@ -704,6 +663,29 @@ static void tegra_dsi_ganged_disable(struct tegra_dsi *dsi) tegra_dsi_writel(dsi, 0, DSI_GANGED_MODE_CONTROL); } +static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, + unsigned int vrefresh) +{ + unsigned int timeout; + u32 value; + + /* one frame high-speed transmission timeout */ + timeout = (bclk / vrefresh) / 512; + value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); + + /* 2 ms peripheral timeout for panel */ + timeout = 2 * bclk / 512 * 1000; + value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); + tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); + + value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); + tegra_dsi_writel(dsi, value, DSI_TO_TALLY); + + if (dsi->slave) + tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); +} + static void tegra_dsi_disable(struct tegra_dsi *dsi) { u32 value; @@ -747,82 +729,51 @@ static void tegra_dsi_soft_reset(struct tegra_dsi *dsi) tegra_dsi_soft_reset(dsi->slave); } -static int tegra_output_dsi_disable(struct tegra_output *output) +static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct tegra_dsi *dsi = to_dsi(output); - u32 value; - int err; - - if (!dsi->enabled) - return 0; - - tegra_dsi_video_disable(dsi); - - /* - * The following accesses registers of the display controller, so make - * sure it's only executed when the output is attached to one. - */ - if (dc) { - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value &= ~DSI_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - - tegra_dc_commit(dc); - } - - err = tegra_dsi_wait_idle(dsi, 100); - if (err < 0) - dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); - - tegra_dsi_soft_reset(dsi); - tegra_dsi_disable(dsi); - - dsi->enabled = false; - - return 0; } -static void tegra_dsi_set_timeout(struct tegra_dsi *dsi, unsigned long bclk, - unsigned int vrefresh) -{ - unsigned int timeout; - u32 value; +static const struct drm_connector_funcs tegra_dsi_connector_funcs = { + .dpms = tegra_dsi_connector_dpms, + .detect = tegra_output_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_output_connector_destroy, +}; - /* one frame high-speed transmission timeout */ - timeout = (bclk / vrefresh) / 512; - value = DSI_TIMEOUT_LRX(0x2000) | DSI_TIMEOUT_HTX(timeout); - tegra_dsi_writel(dsi, value, DSI_TIMEOUT_0); +static enum drm_mode_status +tegra_dsi_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} - /* 2 ms peripheral timeout for panel */ - timeout = 2 * bclk / 512 * 1000; - value = DSI_TIMEOUT_PR(timeout) | DSI_TIMEOUT_TA(0x2000); - tegra_dsi_writel(dsi, value, DSI_TIMEOUT_1); +static const struct drm_connector_helper_funcs tegra_dsi_connector_helper_funcs = { + .get_modes = tegra_output_connector_get_modes, + .mode_valid = tegra_dsi_connector_mode_valid, + .best_encoder = tegra_output_connector_best_encoder, +}; - value = DSI_TALLY_TA(0) | DSI_TALLY_LRX(0) | DSI_TALLY_HTX(0); - tegra_dsi_writel(dsi, value, DSI_TO_TALLY); +static const struct drm_encoder_funcs tegra_dsi_encoder_funcs = { + .destroy = tegra_output_encoder_destroy, +}; - if (dsi->slave) - tegra_dsi_set_timeout(dsi->slave, bclk, vrefresh); +static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode) +{ } -static int tegra_output_dsi_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *divp) +static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct drm_display_mode *mode = &dc->base.mode; + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + unsigned int mul, div, scdiv, vrefresh, lanes; struct tegra_dsi *dsi = to_dsi(output); - unsigned int mul, div, vrefresh, lanes; - unsigned long bclk, plld; + unsigned long pclk, bclk, plld; int err; lanes = tegra_dsi_get_lanes(dsi); + pclk = mode->clock * 1000; err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); if (err < 0) @@ -847,19 +798,6 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, */ plld /= 2; - err = clk_set_parent(clk, dsi->clk_parent); - if (err < 0) { - dev_err(dsi->dev, "failed to set parent clock: %d\n", err); - return err; - } - - err = clk_set_rate(dsi->clk_parent, plld); - if (err < 0) { - dev_err(dsi->dev, "failed to set base clock rate to %lu Hz\n", - plld); - return err; - } - /* * Derive pixel clock from bit clock using the shift clock divider. * Note that this is only half of what we would expect, but we need @@ -870,39 +808,132 @@ static int tegra_output_dsi_setup_clock(struct tegra_output *output, * not working properly otherwise. Perhaps the PLLs cannot generate * frequencies sufficiently high. */ - *divp = ((8 * mul) / (div * lanes)) - 2; + scdiv = ((8 * mul) / (div * lanes)) - 2; + + err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv); + if (err < 0) { + dev_err(output->dev, "failed to setup DC clock: %d\n", err); + return false; + } + + err = clk_set_rate(dsi->clk_parent, plld); + if (err < 0) { + dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n", + plld); + return false; + } - /* - * XXX: Move the below somewhere else so that we don't need to have - * access to the vrefresh in this function? - */ tegra_dsi_set_timeout(dsi, bclk, vrefresh); err = tegra_dsi_set_phy_timing(dsi); - if (err < 0) - return err; + if (err < 0) { + dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err); + return false; + } - return 0; + return true; +} + +static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void tegra_dsi_encoder_commit(struct drm_encoder *encoder) +{ } -static int tegra_output_dsi_check_mode(struct tegra_output *output, +static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - enum drm_mode_status *status) + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + struct tegra_dsi *dsi = to_dsi(output); + u32 value; + int err; + + + err = tegra_dsi_configure(dsi, dc->pipe, mode); + if (err < 0) { + dev_err(dsi->dev, "failed to configure DSI: %d\n", err); + return; + } + + if (output->panel) + drm_panel_prepare(output->panel); + + /* enable display controller */ + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value |= DSI_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + value |= DISP_CTRL_MODE_C_DISPLAY; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + + tegra_dc_commit(dc); + + /* enable DSI controller */ + tegra_dsi_enable(dsi); + + if (output->panel) + drm_panel_enable(output->panel); + + return; +} + +static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) { + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + struct tegra_dsi *dsi = to_dsi(output); + u32 value; + int err; + + if (output->panel) + drm_panel_disable(output->panel); + + tegra_dsi_video_disable(dsi); + /* - * FIXME: For now, always assume that the mode is okay. + * The following accesses registers of the display controller, so make + * sure it's only executed when the output is attached to one. */ + if (dc) { + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value &= ~DSI_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - *status = MODE_OK; + tegra_dc_commit(dc); + } - return 0; + err = tegra_dsi_wait_idle(dsi, 100); + if (err < 0) + dev_dbg(dsi->dev, "failed to idle DSI: %d\n", err); + + tegra_dsi_soft_reset(dsi); + + if (output->panel) + drm_panel_unprepare(output->panel); + + tegra_dsi_disable(dsi); + + return; } -static const struct tegra_output_ops dsi_ops = { - .enable = tegra_output_dsi_enable, - .disable = tegra_output_dsi_disable, - .setup_clock = tegra_output_dsi_setup_clock, - .check_mode = tegra_output_dsi_check_mode, +static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { + .dpms = tegra_dsi_encoder_dpms, + .mode_fixup = tegra_dsi_encoder_mode_fixup, + .prepare = tegra_dsi_encoder_prepare, + .commit = tegra_dsi_encoder_commit, + .mode_set = tegra_dsi_encoder_mode_set, + .disable = tegra_dsi_encoder_disable, }; static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) @@ -952,15 +983,30 @@ static int tegra_dsi_init(struct host1x_client *client) /* Gangsters must not register their own outputs. */ if (!dsi->master) { - dsi->output.type = TEGRA_OUTPUT_DSI; dsi->output.dev = client->dev; - dsi->output.ops = &dsi_ops; - err = tegra_output_init(drm, &dsi->output); - if (err < 0) { - dev_err(client->dev, "output setup failed: %d\n", err); - return err; - } + drm_connector_init(drm, &dsi->output.connector, + &tegra_dsi_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + drm_connector_helper_add(&dsi->output.connector, + &tegra_dsi_connector_helper_funcs); + dsi->output.connector.dpms = DRM_MODE_DPMS_OFF; + + if (dsi->output.panel) + drm_panel_attach(dsi->output.panel, + &dsi->output.connector); + + drm_encoder_init(drm, &dsi->output.encoder, + &tegra_dsi_encoder_funcs, + DRM_MODE_ENCODER_DSI); + drm_encoder_helper_add(&dsi->output.encoder, + &tegra_dsi_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&dsi->output.connector, + &dsi->output.encoder); + drm_connector_register(&dsi->output.connector); + + dsi->output.encoder.possible_crtcs = 0x3; } if (IS_ENABLED(CONFIG_DEBUG_FS)) { @@ -981,28 +1027,14 @@ static int tegra_dsi_exit(struct host1x_client *client) struct tegra_dsi *dsi = host1x_client_to_dsi(client); int err; + tegra_output_exit(&dsi->output); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_dsi_debugfs_exit(dsi); if (err < 0) dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err); } - if (!dsi->master) { - err = tegra_output_disable(&dsi->output); - if (err < 0) { - dev_err(client->dev, "output failed to disable: %d\n", - err); - return err; - } - - err = tegra_output_exit(&dsi->output); - if (err < 0) { - dev_err(client->dev, "output cleanup failed: %d\n", - err); - return err; - } - } - reset_control_assert(dsi->rst); return 0; @@ -1547,6 +1579,12 @@ static int tegra_dsi_remove(struct platform_device *pdev) return err; } + err = tegra_output_remove(&dsi->output); + if (err < 0) { + dev_err(&pdev->dev, "failed to remove output: %d\n", err); + return err; + } + mipi_dsi_host_unregister(&dsi->host); tegra_mipi_free(dsi->mipi); @@ -1555,12 +1593,6 @@ static int tegra_dsi_remove(struct platform_device *pdev) clk_disable_unprepare(dsi->clk); reset_control_assert(dsi->rst); - err = tegra_output_remove(&dsi->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to remove output: %d\n", err); - return err; - } - return 0; } diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index c7ebe8ea3868..81de885226ff 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -274,11 +274,6 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) int connector, encoder; switch (output->type) { - case TEGRA_OUTPUT_DSI: - connector = DRM_MODE_CONNECTOR_DSI; - encoder = DRM_MODE_ENCODER_DSI; - break; - case TEGRA_OUTPUT_EDP: connector = DRM_MODE_CONNECTOR_eDP; encoder = DRM_MODE_ENCODER_TMDS; -- cgit v1.2.3 From 6fad8f66d7b5f2194f30c62ac40248e15a5b2af1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Nov 2014 15:41:34 +0100 Subject: drm/tegra: sor: Demidlayer Implement encoder and connector within the eDP driver itself using the Tegra output helpers rather than using the Tegra output as midlayer. By doing so one level of indirection is removed and output drivers become more flexible while keeping the majority of the advantages provided by the common output helpers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.h | 5 - drivers/gpu/drm/tegra/output.c | 15 +- drivers/gpu/drm/tegra/sor.c | 810 ++++++++++++++++++++++------------------- 3 files changed, 436 insertions(+), 394 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index ae3daa436ee6..c74d5db47537 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -192,16 +192,11 @@ struct tegra_output_ops { enum drm_connector_status (*detect)(struct tegra_output *output); }; -enum tegra_output_type { - TEGRA_OUTPUT_EDP, -}; - struct tegra_output { struct device_node *of_node; struct device *dev; const struct tegra_output_ops *ops; - enum tegra_output_type type; struct drm_panel *panel; struct i2c_adapter *ddc; diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 81de885226ff..57313e3ac238 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -271,19 +271,8 @@ int tegra_output_remove(struct tegra_output *output) int tegra_output_init(struct drm_device *drm, struct tegra_output *output) { - int connector, encoder; - - switch (output->type) { - case TEGRA_OUTPUT_EDP: - connector = DRM_MODE_CONNECTOR_eDP; - encoder = DRM_MODE_ENCODER_TMDS; - break; - - default: - connector = DRM_MODE_CONNECTOR_Unknown; - encoder = DRM_MODE_ENCODER_NONE; - break; - } + int connector = DRM_MODE_CONNECTOR_Unknown; + int encoder = DRM_MODE_ENCODER_NONE; drm_connector_init(drm, &output->connector, &connector_funcs, connector); diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 1fe801ee8eb0..be1ad42c69be 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include +#include #include "dc.h" #include "drm.h" @@ -481,10 +483,342 @@ static int tegra_sor_calc_config(struct tegra_sor *sor, return 0; } -static int tegra_output_sor_enable(struct tegra_output *output) +static int tegra_sor_detach(struct tegra_sor *sor) +{ + unsigned long value, timeout; + + /* switch to safe mode */ + value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); + value &= ~SOR_SUPER_STATE_MODE_NORMAL; + tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); + tegra_sor_super_update(sor); + + timeout = jiffies + msecs_to_jiffies(250); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_PWR); + if (value & SOR_PWR_MODE_SAFE) + break; + } + + if ((value & SOR_PWR_MODE_SAFE) == 0) + return -ETIMEDOUT; + + /* go to sleep */ + value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); + value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; + tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); + tegra_sor_super_update(sor); + + /* detach */ + value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); + value &= ~SOR_SUPER_STATE_ATTACHED; + tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); + tegra_sor_super_update(sor); + + timeout = jiffies + msecs_to_jiffies(250); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_TEST); + if ((value & SOR_TEST_ATTACHED) == 0) + break; + + usleep_range(25, 100); + } + + if ((value & SOR_TEST_ATTACHED) != 0) + return -ETIMEDOUT; + + return 0; +} + +static int tegra_sor_power_down(struct tegra_sor *sor) +{ + unsigned long value, timeout; + int err; + + value = tegra_sor_readl(sor, SOR_PWR); + value &= ~SOR_PWR_NORMAL_STATE_PU; + value |= SOR_PWR_TRIGGER; + tegra_sor_writel(sor, value, SOR_PWR); + + timeout = jiffies + msecs_to_jiffies(250); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_PWR); + if ((value & SOR_PWR_TRIGGER) == 0) + return 0; + + usleep_range(25, 100); + } + + if ((value & SOR_PWR_TRIGGER) != 0) + return -ETIMEDOUT; + + err = clk_set_parent(sor->clk, sor->clk_safe); + if (err < 0) + dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); + + value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); + value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | + SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); + tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); + + /* stop lane sequencer */ + value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | + SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; + tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); + + timeout = jiffies + msecs_to_jiffies(250); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); + if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) + break; + + usleep_range(25, 100); + } + + if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) + return -ETIMEDOUT; + + value = tegra_sor_readl(sor, SOR_PLL_2); + value |= SOR_PLL_2_PORT_POWERDOWN; + tegra_sor_writel(sor, value, SOR_PLL_2); + + usleep_range(20, 100); + + value = tegra_sor_readl(sor, SOR_PLL_0); + value |= SOR_PLL_0_POWER_OFF; + value |= SOR_PLL_0_VCOPD; + tegra_sor_writel(sor, value, SOR_PLL_0); + + value = tegra_sor_readl(sor, SOR_PLL_2); + value |= SOR_PLL_2_SEQ_PLLCAPPD; + value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; + tegra_sor_writel(sor, value, SOR_PLL_2); + + usleep_range(20, 100); + + return 0; +} + +static int tegra_sor_crc_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + + return 0; +} + +static int tegra_sor_crc_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) +{ + u32 value; + + timeout = jiffies + msecs_to_jiffies(timeout); + + while (time_before(jiffies, timeout)) { + value = tegra_sor_readl(sor, SOR_CRC_A); + if (value & SOR_CRC_A_VALID) + return 0; + + usleep_range(100, 200); + } + + return -ETIMEDOUT; +} + +static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, + size_t size, loff_t *ppos) +{ + struct tegra_sor *sor = file->private_data; + ssize_t num, err; + char buf[10]; + u32 value; + + mutex_lock(&sor->lock); + + if (!sor->enabled) { + err = -EAGAIN; + goto unlock; + } + + value = tegra_sor_readl(sor, SOR_STATE_1); + value &= ~SOR_STATE_ASY_CRC_MODE_MASK; + tegra_sor_writel(sor, value, SOR_STATE_1); + + value = tegra_sor_readl(sor, SOR_CRC_CNTRL); + value |= SOR_CRC_CNTRL_ENABLE; + tegra_sor_writel(sor, value, SOR_CRC_CNTRL); + + value = tegra_sor_readl(sor, SOR_TEST); + value &= ~SOR_TEST_CRC_POST_SERIALIZE; + tegra_sor_writel(sor, value, SOR_TEST); + + err = tegra_sor_crc_wait(sor, 100); + if (err < 0) + goto unlock; + + tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); + value = tegra_sor_readl(sor, SOR_CRC_B); + + num = scnprintf(buf, sizeof(buf), "%08x\n", value); + + err = simple_read_from_buffer(buffer, size, ppos, buf, num); + +unlock: + mutex_unlock(&sor->lock); + return err; +} + +static const struct file_operations tegra_sor_crc_fops = { + .owner = THIS_MODULE, + .open = tegra_sor_crc_open, + .read = tegra_sor_crc_read, + .release = tegra_sor_crc_release, +}; + +static int tegra_sor_debugfs_init(struct tegra_sor *sor, + struct drm_minor *minor) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); - struct drm_display_mode *mode = &dc->base.mode; + struct dentry *entry; + int err = 0; + + sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); + if (!sor->debugfs) + return -ENOMEM; + + entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, + &tegra_sor_crc_fops); + if (!entry) { + dev_err(sor->dev, + "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", + minor->debugfs_root->d_name.name); + err = -ENOMEM; + goto remove; + } + + return err; + +remove: + debugfs_remove(sor->debugfs); + sor->debugfs = NULL; + return err; +} + +static int tegra_sor_debugfs_exit(struct tegra_sor *sor) +{ + debugfs_remove_recursive(sor->debugfs); + sor->debugfs = NULL; + + return 0; +} + +static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode) +{ +} + +static enum drm_connector_status +tegra_sor_connector_detect(struct drm_connector *connector, bool force) +{ + struct tegra_output *output = connector_to_output(connector); + struct tegra_sor *sor = to_sor(output); + + if (sor->dpaux) + return tegra_dpaux_detect(sor->dpaux); + + return connector_status_unknown; +} + +static const struct drm_connector_funcs tegra_sor_connector_funcs = { + .dpms = tegra_sor_connector_dpms, + .detect = tegra_sor_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tegra_output_connector_destroy, +}; + +static int tegra_sor_connector_get_modes(struct drm_connector *connector) +{ + struct tegra_output *output = connector_to_output(connector); + struct tegra_sor *sor = to_sor(output); + int err; + + if (sor->dpaux) + tegra_dpaux_enable(sor->dpaux); + + err = tegra_output_connector_get_modes(connector); + + if (sor->dpaux) + tegra_dpaux_disable(sor->dpaux); + + return err; +} + +static enum drm_mode_status +tegra_sor_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static const struct drm_connector_helper_funcs tegra_sor_connector_helper_funcs = { + .get_modes = tegra_sor_connector_get_modes, + .mode_valid = tegra_sor_connector_mode_valid, + .best_encoder = tegra_output_connector_best_encoder, +}; + +static const struct drm_encoder_funcs tegra_sor_encoder_funcs = { + .destroy = tegra_output_encoder_destroy, +}; + +static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); + unsigned long pclk = mode->clock * 1000; + struct tegra_sor *sor = to_sor(output); + int err; + + err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0); + if (err < 0) { + dev_err(output->dev, "failed to setup DC clock: %d\n", err); + return false; + } + + err = clk_set_rate(sor->clk_parent, pclk); + if (err < 0) { + dev_err(output->dev, "failed to set clock rate to %lu Hz\n", + pclk); + return false; + } + + return true; +} + +static void tegra_sor_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void tegra_sor_encoder_commit(struct drm_encoder *encoder) +{ +} + +static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); unsigned int vbe, vse, hbe, hse, vbs, hbs, i; struct tegra_sor *sor = to_sor(output); struct tegra_sor_config config; @@ -504,6 +838,9 @@ static int tegra_output_sor_enable(struct tegra_output *output) reset_control_deassert(sor->rst); + if (output->panel) + drm_panel_prepare(output->panel); + /* FIXME: properly convert to struct drm_dp_aux */ aux = (struct drm_dp_aux *)sor->dpaux; @@ -873,175 +1210,61 @@ static int tegra_output_sor_enable(struct tegra_output *output) value = ((vbs & 0x7fff) << 16) | (hbs & 0x7fff); tegra_sor_writel(sor, value, SOR_HEAD_STATE_4(0)); - /* CSTM (LVDS, link A/B, upper) */ - value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | - SOR_CSTM_UPPER; - tegra_sor_writel(sor, value, SOR_CSTM); - - /* PWM setup */ - err = tegra_sor_setup_pwm(sor, 250); - if (err < 0) { - dev_err(sor->dev, "failed to setup PWM: %d\n", err); - goto unlock; - } - - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); - value |= SOR_ENABLE; - tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - - tegra_sor_update(sor); - - err = tegra_sor_attach(sor); - if (err < 0) { - dev_err(sor->dev, "failed to attach SOR: %d\n", err); - goto unlock; - } - - err = tegra_sor_wakeup(sor); - if (err < 0) { - dev_err(sor->dev, "failed to enable DC: %d\n", err); - goto unlock; - } - - sor->enabled = true; - -unlock: - mutex_unlock(&sor->lock); - return err; -} - -static int tegra_sor_detach(struct tegra_sor *sor) -{ - unsigned long value, timeout; - - /* switch to safe mode */ - value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); - value &= ~SOR_SUPER_STATE_MODE_NORMAL; - tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); - tegra_sor_super_update(sor); - - timeout = jiffies + msecs_to_jiffies(250); - - while (time_before(jiffies, timeout)) { - value = tegra_sor_readl(sor, SOR_PWR); - if (value & SOR_PWR_MODE_SAFE) - break; - } - - if ((value & SOR_PWR_MODE_SAFE) == 0) - return -ETIMEDOUT; - - /* go to sleep */ - value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); - value &= ~SOR_SUPER_STATE_HEAD_MODE_MASK; - tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); - tegra_sor_super_update(sor); - - /* detach */ - value = tegra_sor_readl(sor, SOR_SUPER_STATE_1); - value &= ~SOR_SUPER_STATE_ATTACHED; - tegra_sor_writel(sor, value, SOR_SUPER_STATE_1); - tegra_sor_super_update(sor); - - timeout = jiffies + msecs_to_jiffies(250); - - while (time_before(jiffies, timeout)) { - value = tegra_sor_readl(sor, SOR_TEST); - if ((value & SOR_TEST_ATTACHED) == 0) - break; - - usleep_range(25, 100); - } - - if ((value & SOR_TEST_ATTACHED) != 0) - return -ETIMEDOUT; - - return 0; -} - -static int tegra_sor_power_down(struct tegra_sor *sor) -{ - unsigned long value, timeout; - int err; - - value = tegra_sor_readl(sor, SOR_PWR); - value &= ~SOR_PWR_NORMAL_STATE_PU; - value |= SOR_PWR_TRIGGER; - tegra_sor_writel(sor, value, SOR_PWR); - - timeout = jiffies + msecs_to_jiffies(250); - - while (time_before(jiffies, timeout)) { - value = tegra_sor_readl(sor, SOR_PWR); - if ((value & SOR_PWR_TRIGGER) == 0) - return 0; - - usleep_range(25, 100); - } - - if ((value & SOR_PWR_TRIGGER) != 0) - return -ETIMEDOUT; - - err = clk_set_parent(sor->clk, sor->clk_safe); - if (err < 0) - dev_err(sor->dev, "failed to set safe parent clock: %d\n", err); - - value = tegra_sor_readl(sor, SOR_DP_PADCTL_0); - value &= ~(SOR_DP_PADCTL_PD_TXD_3 | SOR_DP_PADCTL_PD_TXD_0 | - SOR_DP_PADCTL_PD_TXD_1 | SOR_DP_PADCTL_PD_TXD_2); - tegra_sor_writel(sor, value, SOR_DP_PADCTL_0); - - /* stop lane sequencer */ - value = SOR_LANE_SEQ_CTL_TRIGGER | SOR_LANE_SEQ_CTL_SEQUENCE_UP | - SOR_LANE_SEQ_CTL_POWER_STATE_DOWN; - tegra_sor_writel(sor, value, SOR_LANE_SEQ_CTL); - - timeout = jiffies + msecs_to_jiffies(250); - - while (time_before(jiffies, timeout)) { - value = tegra_sor_readl(sor, SOR_LANE_SEQ_CTL); - if ((value & SOR_LANE_SEQ_CTL_TRIGGER) == 0) - break; - - usleep_range(25, 100); + /* CSTM (LVDS, link A/B, upper) */ + value = SOR_CSTM_LVDS | SOR_CSTM_LINK_ACT_A | SOR_CSTM_LINK_ACT_B | + SOR_CSTM_UPPER; + tegra_sor_writel(sor, value, SOR_CSTM); + + /* PWM setup */ + err = tegra_sor_setup_pwm(sor, 250); + if (err < 0) { + dev_err(sor->dev, "failed to setup PWM: %d\n", err); + goto unlock; } - if ((value & SOR_LANE_SEQ_CTL_TRIGGER) != 0) - return -ETIMEDOUT; + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); + value |= SOR_ENABLE; + tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - value = tegra_sor_readl(sor, SOR_PLL_2); - value |= SOR_PLL_2_PORT_POWERDOWN; - tegra_sor_writel(sor, value, SOR_PLL_2); + tegra_sor_update(sor); - usleep_range(20, 100); + err = tegra_sor_attach(sor); + if (err < 0) { + dev_err(sor->dev, "failed to attach SOR: %d\n", err); + goto unlock; + } - value = tegra_sor_readl(sor, SOR_PLL_0); - value |= SOR_PLL_0_POWER_OFF; - value |= SOR_PLL_0_VCOPD; - tegra_sor_writel(sor, value, SOR_PLL_0); + err = tegra_sor_wakeup(sor); + if (err < 0) { + dev_err(sor->dev, "failed to enable DC: %d\n", err); + goto unlock; + } - value = tegra_sor_readl(sor, SOR_PLL_2); - value |= SOR_PLL_2_SEQ_PLLCAPPD; - value |= SOR_PLL_2_SEQ_PLLCAPPD_ENFORCE; - tegra_sor_writel(sor, value, SOR_PLL_2); + if (output->panel) + drm_panel_enable(output->panel); - usleep_range(20, 100); + sor->enabled = true; - return 0; +unlock: + mutex_unlock(&sor->lock); } -static int tegra_output_sor_disable(struct tegra_output *output) +static void tegra_sor_encoder_disable(struct drm_encoder *encoder) { - struct tegra_dc *dc = to_tegra_dc(output->encoder.crtc); + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(encoder->crtc); struct tegra_sor *sor = to_sor(output); - unsigned long value; - int err = 0; + u32 value; + int err; mutex_lock(&sor->lock); if (!sor->enabled) goto unlock; + if (output->panel) + drm_panel_disable(output->panel); + err = tegra_sor_detach(sor); if (err < 0) { dev_err(sor->dev, "failed to detach SOR: %d\n", err); @@ -1056,21 +1279,6 @@ static int tegra_output_sor_disable(struct tegra_output *output) * sure it's only executed when the output is attached to one. */ if (dc) { - /* - * XXX: We can't do this here because it causes the SOR to go - * into an erroneous state and the output will look scrambled - * the next time it is enabled. Presumably this is because we - * should be doing this only on the next VBLANK. A possible - * solution would be to queue a "power-off" event to trigger - * this code to be run during the next VBLANK. - */ - /* - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE); - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - */ - value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~SOR_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); @@ -1098,187 +1306,27 @@ static int tegra_output_sor_disable(struct tegra_output *output) goto unlock; } - reset_control_assert(sor->rst); + if (output->panel) + drm_panel_unprepare(output->panel); + clk_disable_unprepare(sor->clk); + reset_control_assert(sor->rst); sor->enabled = false; unlock: mutex_unlock(&sor->lock); - return err; -} - -static int tegra_output_sor_setup_clock(struct tegra_output *output, - struct clk *clk, unsigned long pclk, - unsigned int *div) -{ - struct tegra_sor *sor = to_sor(output); - int err; - - err = clk_set_parent(clk, sor->clk_parent); - if (err < 0) { - dev_err(sor->dev, "failed to set parent clock: %d\n", err); - return err; - } - - err = clk_set_rate(sor->clk_parent, pclk); - if (err < 0) { - dev_err(sor->dev, "failed to set clock rate to %lu Hz\n", pclk); - return err; - } - - *div = 0; - - return 0; -} - -static int tegra_output_sor_check_mode(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status) -{ - /* - * FIXME: For now, always assume that the mode is okay. - */ - - *status = MODE_OK; - - return 0; -} - -static enum drm_connector_status -tegra_output_sor_detect(struct tegra_output *output) -{ - struct tegra_sor *sor = to_sor(output); - - if (sor->dpaux) - return tegra_dpaux_detect(sor->dpaux); - - return connector_status_unknown; -} - -static const struct tegra_output_ops sor_ops = { - .enable = tegra_output_sor_enable, - .disable = tegra_output_sor_disable, - .setup_clock = tegra_output_sor_setup_clock, - .check_mode = tegra_output_sor_check_mode, - .detect = tegra_output_sor_detect, -}; - -static int tegra_sor_crc_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - - return 0; -} - -static int tegra_sor_crc_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static int tegra_sor_crc_wait(struct tegra_sor *sor, unsigned long timeout) -{ - u32 value; - - timeout = jiffies + msecs_to_jiffies(timeout); - - while (time_before(jiffies, timeout)) { - value = tegra_sor_readl(sor, SOR_CRC_A); - if (value & SOR_CRC_A_VALID) - return 0; - - usleep_range(100, 200); - } - - return -ETIMEDOUT; -} - -static ssize_t tegra_sor_crc_read(struct file *file, char __user *buffer, - size_t size, loff_t *ppos) -{ - struct tegra_sor *sor = file->private_data; - ssize_t num, err; - char buf[10]; - u32 value; - - mutex_lock(&sor->lock); - - if (!sor->enabled) { - err = -EAGAIN; - goto unlock; - } - - value = tegra_sor_readl(sor, SOR_STATE_1); - value &= ~SOR_STATE_ASY_CRC_MODE_MASK; - tegra_sor_writel(sor, value, SOR_STATE_1); - - value = tegra_sor_readl(sor, SOR_CRC_CNTRL); - value |= SOR_CRC_CNTRL_ENABLE; - tegra_sor_writel(sor, value, SOR_CRC_CNTRL); - - value = tegra_sor_readl(sor, SOR_TEST); - value &= ~SOR_TEST_CRC_POST_SERIALIZE; - tegra_sor_writel(sor, value, SOR_TEST); - - err = tegra_sor_crc_wait(sor, 100); - if (err < 0) - goto unlock; - - tegra_sor_writel(sor, SOR_CRC_A_RESET, SOR_CRC_A); - value = tegra_sor_readl(sor, SOR_CRC_B); - - num = scnprintf(buf, sizeof(buf), "%08x\n", value); - - err = simple_read_from_buffer(buffer, size, ppos, buf, num); - -unlock: - mutex_unlock(&sor->lock); - return err; } -static const struct file_operations tegra_sor_crc_fops = { - .owner = THIS_MODULE, - .open = tegra_sor_crc_open, - .read = tegra_sor_crc_read, - .release = tegra_sor_crc_release, +static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = { + .dpms = tegra_sor_encoder_dpms, + .mode_fixup = tegra_sor_encoder_mode_fixup, + .prepare = tegra_sor_encoder_prepare, + .commit = tegra_sor_encoder_commit, + .mode_set = tegra_sor_encoder_mode_set, + .disable = tegra_sor_encoder_disable, }; -static int tegra_sor_debugfs_init(struct tegra_sor *sor, - struct drm_minor *minor) -{ - struct dentry *entry; - int err = 0; - - sor->debugfs = debugfs_create_dir("sor", minor->debugfs_root); - if (!sor->debugfs) - return -ENOMEM; - - entry = debugfs_create_file("crc", 0644, sor->debugfs, sor, - &tegra_sor_crc_fops); - if (!entry) { - dev_err(sor->dev, - "cannot create /sys/kernel/debug/dri/%s/sor/crc\n", - minor->debugfs_root->d_name.name); - err = -ENOMEM; - goto remove; - } - - return err; - -remove: - debugfs_remove(sor->debugfs); - sor->debugfs = NULL; - return err; -} - -static int tegra_sor_debugfs_exit(struct tegra_sor *sor) -{ - debugfs_remove_recursive(sor->debugfs); - sor->debugfs = NULL; - - return 0; -} - static int tegra_sor_init(struct host1x_client *client) { struct drm_device *drm = dev_get_drvdata(client->parent); @@ -1288,16 +1336,31 @@ static int tegra_sor_init(struct host1x_client *client) if (!sor->dpaux) return -ENODEV; - sor->output.type = TEGRA_OUTPUT_EDP; - sor->output.dev = sor->dev; - sor->output.ops = &sor_ops; - err = tegra_output_init(drm, &sor->output); - if (err < 0) { - dev_err(sor->dev, "output setup failed: %d\n", err); - return err; - } + drm_connector_init(drm, &sor->output.connector, + &tegra_sor_connector_funcs, + DRM_MODE_CONNECTOR_eDP); + drm_connector_helper_add(&sor->output.connector, + &tegra_sor_connector_helper_funcs); + sor->output.connector.dpms = DRM_MODE_DPMS_OFF; + + if (sor->output.panel) + drm_panel_attach(sor->output.panel, &sor->output.connector); + + drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(&sor->output.encoder, + &tegra_sor_encoder_helper_funcs); + + drm_mode_connector_attach_encoder(&sor->output.connector, + &sor->output.encoder); + drm_connector_register(&sor->output.connector); + + sor->output.encoder.possible_crtcs = 0x3; + + if (gpio_is_valid(sor->output.hpd_gpio)) + enable_irq(sor->output.hpd_irq); if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_sor_debugfs_init(sor, drm->primary); @@ -1313,6 +1376,20 @@ static int tegra_sor_init(struct host1x_client *client) } } + err = clk_prepare_enable(sor->clk); + if (err < 0) { + dev_err(sor->dev, "failed to enable clock: %d\n", err); + return err; + } + + err = clk_prepare_enable(sor->clk_safe); + if (err < 0) + return err; + + err = clk_prepare_enable(sor->clk_dp); + if (err < 0) + return err; + return 0; } @@ -1321,12 +1398,6 @@ static int tegra_sor_exit(struct host1x_client *client) struct tegra_sor *sor = host1x_client_to_sor(client); int err; - err = tegra_output_disable(&sor->output); - if (err < 0) { - dev_err(sor->dev, "output failed to disable: %d\n", err); - return err; - } - if (sor->dpaux) { err = tegra_dpaux_detach(sor->dpaux); if (err < 0) { @@ -1335,18 +1406,16 @@ static int tegra_sor_exit(struct host1x_client *client) } } + clk_disable_unprepare(sor->clk_safe); + clk_disable_unprepare(sor->clk_dp); + clk_disable_unprepare(sor->clk); + if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_sor_debugfs_exit(sor); if (err < 0) dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); } - err = tegra_output_exit(&sor->output); - if (err < 0) { - dev_err(sor->dev, "output cleanup failed: %d\n", err); - return err; - } - return 0; } @@ -1398,26 +1467,14 @@ static int tegra_sor_probe(struct platform_device *pdev) if (IS_ERR(sor->clk_parent)) return PTR_ERR(sor->clk_parent); - err = clk_prepare_enable(sor->clk_parent); - if (err < 0) - return err; - sor->clk_safe = devm_clk_get(&pdev->dev, "safe"); if (IS_ERR(sor->clk_safe)) return PTR_ERR(sor->clk_safe); - err = clk_prepare_enable(sor->clk_safe); - if (err < 0) - return err; - sor->clk_dp = devm_clk_get(&pdev->dev, "dp"); if (IS_ERR(sor->clk_dp)) return PTR_ERR(sor->clk_dp); - err = clk_prepare_enable(sor->clk_dp); - if (err < 0) - return err; - INIT_LIST_HEAD(&sor->client.list); sor->client.ops = &sor_client_ops; sor->client.dev = &pdev->dev; @@ -1448,10 +1505,11 @@ static int tegra_sor_remove(struct platform_device *pdev) return err; } - clk_disable_unprepare(sor->clk_parent); - clk_disable_unprepare(sor->clk_safe); - clk_disable_unprepare(sor->clk_dp); - clk_disable_unprepare(sor->clk); + err = tegra_output_remove(&sor->output); + if (err < 0) { + dev_err(&pdev->dev, "failed to remove output: %d\n", err); + return err; + } return 0; } -- cgit v1.2.3 From 4009c22420593cae6d99b4ba43d3864c5788cd77 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 19 Dec 2014 15:47:30 +0100 Subject: drm/tegra: debugfs cleanup cannot fail The debugfs cleanup code never fails, so no error is returned. Therefore the functions can all return void instead. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 12 +++--------- drivers/gpu/drm/tegra/hdmi.c | 13 +++---------- drivers/gpu/drm/tegra/sor.c | 11 +++-------- 3 files changed, 9 insertions(+), 27 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 0b476e1c005a..a999478b2c47 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -228,7 +228,7 @@ remove: return err; } -static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) +static void tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) { drm_debugfs_remove_files(dsi->debugfs_files, ARRAY_SIZE(debugfs_files), dsi->minor); @@ -239,8 +239,6 @@ static int tegra_dsi_debugfs_exit(struct tegra_dsi *dsi) debugfs_remove(dsi->debugfs); dsi->debugfs = NULL; - - return 0; } #define PKT_ID0(id) ((((id) & 0x3f) << 3) | (1 << 9)) @@ -1025,15 +1023,11 @@ reset: static int tegra_dsi_exit(struct host1x_client *client) { struct tegra_dsi *dsi = host1x_client_to_dsi(client); - int err; tegra_output_exit(&dsi->output); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_dsi_debugfs_exit(dsi); - if (err < 0) - dev_err(dsi->dev, "debugfs cleanup failed: %d\n", err); - } + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_dsi_debugfs_exit(dsi); reset_control_assert(dsi->rst); diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 056bb2c1c426..b4fe90949f27 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1310,7 +1310,7 @@ remove: return err; } -static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) +static void tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) { drm_debugfs_remove_files(hdmi->debugfs_files, ARRAY_SIZE(debugfs_files), hdmi->minor); @@ -1321,8 +1321,6 @@ static int tegra_hdmi_debugfs_exit(struct tegra_hdmi *hdmi) debugfs_remove(hdmi->debugfs); hdmi->debugfs = NULL; - - return 0; } static int tegra_hdmi_init(struct host1x_client *client) @@ -1393,7 +1391,6 @@ static int tegra_hdmi_init(struct host1x_client *client) static int tegra_hdmi_exit(struct host1x_client *client) { struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); - int err; tegra_output_exit(&hdmi->output); @@ -1404,12 +1401,8 @@ static int tegra_hdmi_exit(struct host1x_client *client) regulator_disable(hdmi->pll); regulator_disable(hdmi->hdmi); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_hdmi_debugfs_exit(hdmi); - if (err < 0) - dev_err(client->dev, "debugfs cleanup failed: %d\n", - err); - } + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_hdmi_debugfs_exit(hdmi); return 0; } diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index be1ad42c69be..a1c16c5c0cf6 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -710,12 +710,10 @@ remove: return err; } -static int tegra_sor_debugfs_exit(struct tegra_sor *sor) +static void tegra_sor_debugfs_exit(struct tegra_sor *sor) { debugfs_remove_recursive(sor->debugfs); sor->debugfs = NULL; - - return 0; } static void tegra_sor_connector_dpms(struct drm_connector *connector, int mode) @@ -1410,11 +1408,8 @@ static int tegra_sor_exit(struct host1x_client *client) clk_disable_unprepare(sor->clk_dp); clk_disable_unprepare(sor->clk); - if (IS_ENABLED(CONFIG_DEBUG_FS)) { - err = tegra_sor_debugfs_exit(sor); - if (err < 0) - dev_err(sor->dev, "debugfs cleanup failed: %d\n", err); - } + if (IS_ENABLED(CONFIG_DEBUG_FS)) + tegra_sor_debugfs_exit(sor); return 0; } -- cgit v1.2.3 From ea130b240de820559408eba12b00412326af36ec Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 19 Dec 2014 15:51:35 +0100 Subject: drm/tegra: Remove remnants of the output midlayer The tegra_output midlayer is now completely gone and output drivers use it purely as a helper library. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 52 -------------------- drivers/gpu/drm/tegra/drm.h | 39 --------------- drivers/gpu/drm/tegra/dsi.c | 12 +++-- drivers/gpu/drm/tegra/hdmi.c | 9 ++-- drivers/gpu/drm/tegra/output.c | 107 +++-------------------------------------- drivers/gpu/drm/tegra/rgb.c | 13 +++-- drivers/gpu/drm/tegra/sor.c | 12 ++--- 7 files changed, 32 insertions(+), 212 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 286cc8ce0c66..c5197bdaf0bd 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1070,52 +1070,6 @@ static int tegra_dc_set_timings(struct tegra_dc *dc, return 0; } -static int tegra_crtc_setup_clk(struct drm_crtc *crtc, - struct drm_display_mode *mode) -{ - unsigned long pclk = mode->clock * 1000; - struct tegra_dc *dc = to_tegra_dc(crtc); - struct tegra_output *output = NULL; - struct drm_encoder *encoder; - unsigned int div; - u32 value; - long err; - - list_for_each_entry(encoder, &crtc->dev->mode_config.encoder_list, head) - if (encoder->crtc == crtc) { - output = encoder_to_output(encoder); - break; - } - - if (!output) - return -ENODEV; - - /* - * The ->setup_clock() callback is optional, but if encoders don't - * implement it they most likely need to do the equivalent within the - * ->mode_fixup() callback. - */ - if (!output->ops || !output->ops->setup_clock) - return 0; - - /* - * This assumes that the parent clock is pll_d_out0 or pll_d2_out - * respectively, each of which divides the base pll_d by 2. - */ - err = output->ops->setup_clock(output, dc->clk, pclk, &div); - if (err < 0) { - dev_err(dc->dev, "failed to setup clock: %ld\n", err); - return err; - } - - DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), div); - - value = SHIFT_CLK_DIVIDER(div) | PIXEL_CLK_DIVIDER_PCD1; - tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); - - return 0; -} - int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, unsigned long pclk, unsigned int div) { @@ -1147,12 +1101,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, u32 value; int err; - err = tegra_crtc_setup_clk(crtc, mode); - if (err) { - dev_err(dc->dev, "failed to setup clock for CRTC: %d\n", err); - return err; - } - /* program display mode */ tegra_dc_set_timings(dc, mode); diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index c74d5db47537..699211a89a2e 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -181,23 +181,10 @@ void tegra_dc_commit(struct tegra_dc *dc); int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, unsigned long pclk, unsigned int div); -struct tegra_output_ops { - int (*enable)(struct tegra_output *output); - int (*disable)(struct tegra_output *output); - int (*setup_clock)(struct tegra_output *output, struct clk *clk, - unsigned long pclk, unsigned int *div); - int (*check_mode)(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status); - enum drm_connector_status (*detect)(struct tegra_output *output); -}; - struct tegra_output { struct device_node *of_node; struct device *dev; - const struct tegra_output_ops *ops; - struct drm_panel *panel; struct i2c_adapter *ddc; const struct edid *edid; @@ -218,32 +205,6 @@ static inline struct tegra_output *connector_to_output(struct drm_connector *c) return container_of(c, struct tegra_output, connector); } -static inline int tegra_output_enable(struct tegra_output *output) -{ - if (output && output->ops && output->ops->enable) - return output->ops->enable(output); - - return output ? -ENOSYS : -EINVAL; -} - -static inline int tegra_output_disable(struct tegra_output *output) -{ - if (output && output->ops && output->ops->disable) - return output->ops->disable(output); - - return output ? -ENOSYS : -EINVAL; -} - -static inline int tegra_output_check_mode(struct tegra_output *output, - struct drm_display_mode *mode, - enum drm_mode_status *status) -{ - if (output && output->ops && output->ops->check_mode) - return output->ops->check_mode(output, mode, status); - - return output ? -ENOSYS : -EINVAL; -} - /* from rgb.c */ int tegra_dc_rgb_probe(struct tegra_dc *dc); int tegra_dc_rgb_remove(struct tegra_dc *dc); diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index a999478b2c47..1401d0d7108a 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -990,10 +990,6 @@ static int tegra_dsi_init(struct host1x_client *client) &tegra_dsi_connector_helper_funcs); dsi->output.connector.dpms = DRM_MODE_DPMS_OFF; - if (dsi->output.panel) - drm_panel_attach(dsi->output.panel, - &dsi->output.connector); - drm_encoder_init(drm, &dsi->output.encoder, &tegra_dsi_encoder_funcs, DRM_MODE_ENCODER_DSI); @@ -1004,6 +1000,14 @@ static int tegra_dsi_init(struct host1x_client *client) &dsi->output.encoder); drm_connector_register(&dsi->output.connector); + err = tegra_output_init(drm, &dsi->output); + if (err < 0) { + dev_err(client->dev, + "failed to initialize output: %d\n", + err); + goto reset; + } + dsi->output.encoder.possible_crtcs = 0x3; } diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index b4fe90949f27..03ceb50b1dc9 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1347,10 +1347,13 @@ static int tegra_hdmi_init(struct host1x_client *client) &hdmi->output.encoder); drm_connector_register(&hdmi->output.connector); - hdmi->output.encoder.possible_crtcs = 0x3; + err = tegra_output_init(drm, &hdmi->output); + if (err < 0) { + dev_err(client->dev, "failed to initialize output: %d\n", err); + return err; + } - if (gpio_is_valid(hdmi->output.hpd_gpio)) - enable_irq(hdmi->output.hpd_irq); + hdmi->output.encoder.possible_crtcs = 0x3; if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_hdmi_debugfs_init(hdmi, drm->primary); diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 57313e3ac238..2d3b656bfd22 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -43,20 +43,6 @@ int tegra_output_connector_get_modes(struct drm_connector *connector) return err; } -static int tegra_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - struct tegra_output *output = connector_to_output(connector); - enum drm_mode_status status = MODE_OK; - int err; - - err = tegra_output_check_mode(output, mode, &status); - if (err < 0) - return MODE_ERROR; - - return status; -} - struct drm_encoder * tegra_output_connector_best_encoder(struct drm_connector *connector) { @@ -65,21 +51,12 @@ tegra_output_connector_best_encoder(struct drm_connector *connector) return &output->encoder; } -static const struct drm_connector_helper_funcs connector_helper_funcs = { - .get_modes = tegra_output_connector_get_modes, - .mode_valid = tegra_connector_mode_valid, - .best_encoder = tegra_output_connector_best_encoder, -}; - enum drm_connector_status tegra_output_connector_detect(struct drm_connector *connector, bool force) { struct tegra_output *output = connector_to_output(connector); enum drm_connector_status status = connector_status_unknown; - if (output->ops->detect) - return output->ops->detect(output); - if (gpio_is_valid(output->hpd_gpio)) { if (gpio_get_value(output->hpd_gpio) == 0) status = connector_status_disconnected; @@ -90,9 +67,6 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force) status = connector_status_disconnected; else status = connector_status_connected; - - if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) - status = connector_status_connected; } return status; @@ -104,69 +78,11 @@ void tegra_output_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } -static const struct drm_connector_funcs connector_funcs = { - .dpms = drm_helper_connector_dpms, - .detect = tegra_output_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = tegra_output_connector_destroy, -}; - void tegra_output_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); } -static const struct drm_encoder_funcs encoder_funcs = { - .destroy = tegra_output_encoder_destroy, -}; - -static void tegra_encoder_dpms(struct drm_encoder *encoder, int mode) -{ - struct tegra_output *output = encoder_to_output(encoder); - struct drm_panel *panel = output->panel; - - if (mode != DRM_MODE_DPMS_ON) { - drm_panel_disable(panel); - tegra_output_disable(output); - drm_panel_unprepare(panel); - } else { - drm_panel_prepare(panel); - tegra_output_enable(output); - drm_panel_enable(panel); - } -} - -static bool tegra_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - return true; -} - -static void tegra_encoder_prepare(struct drm_encoder *encoder) -{ - tegra_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); -} - -static void tegra_encoder_commit(struct drm_encoder *encoder) -{ - tegra_encoder_dpms(encoder, DRM_MODE_DPMS_ON); -} - -static void tegra_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ -} - -static const struct drm_encoder_helper_funcs encoder_helper_funcs = { - .dpms = tegra_encoder_dpms, - .mode_fixup = tegra_encoder_mode_fixup, - .prepare = tegra_encoder_prepare, - .commit = tegra_encoder_commit, - .mode_set = tegra_encoder_mode_set, -}; - static irqreturn_t hpd_irq(int irq, void *data) { struct tegra_output *output = data; @@ -271,24 +187,13 @@ int tegra_output_remove(struct tegra_output *output) int tegra_output_init(struct drm_device *drm, struct tegra_output *output) { - int connector = DRM_MODE_CONNECTOR_Unknown; - int encoder = DRM_MODE_ENCODER_NONE; - - drm_connector_init(drm, &output->connector, &connector_funcs, - connector); - drm_connector_helper_add(&output->connector, &connector_helper_funcs); - output->connector.dpms = DRM_MODE_DPMS_OFF; - - if (output->panel) - drm_panel_attach(output->panel, &output->connector); - - drm_encoder_init(drm, &output->encoder, &encoder_funcs, encoder); - drm_encoder_helper_add(&output->encoder, &encoder_helper_funcs); - - drm_mode_connector_attach_encoder(&output->connector, &output->encoder); - drm_connector_register(&output->connector); + int err; - output->encoder.possible_crtcs = 0x3; + if (output->panel) { + err = drm_panel_attach(output->panel, &output->connector); + if (err < 0) + return err; + } /* * The connector is now registered and ready to receive hotplug events diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 30d7ae02ace8..ab6093889be8 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -315,13 +315,6 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) &tegra_rgb_connector_helper_funcs); output->connector.dpms = DRM_MODE_DPMS_OFF; - if (output->panel) { - err = drm_panel_attach(output->panel, &output->connector); - if (err < 0) - dev_err(output->dev, "failed to attach panel: %d\n", - err); - } - drm_encoder_init(drm, &output->encoder, &tegra_rgb_encoder_funcs, DRM_MODE_ENCODER_LVDS); drm_encoder_helper_add(&output->encoder, @@ -331,6 +324,12 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) &output->encoder); drm_connector_register(&output->connector); + err = tegra_output_init(drm, output); + if (err < 0) { + dev_err(output->dev, "failed to initialize output: %d\n", err); + return err; + } + /* * Other outputs can be attached to either display controller. The RGB * outputs are an exception and work only with their parent display diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index a1c16c5c0cf6..ee18adf7f653 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1343,9 +1343,6 @@ static int tegra_sor_init(struct host1x_client *client) &tegra_sor_connector_helper_funcs); sor->output.connector.dpms = DRM_MODE_DPMS_OFF; - if (sor->output.panel) - drm_panel_attach(sor->output.panel, &sor->output.connector); - drm_encoder_init(drm, &sor->output.encoder, &tegra_sor_encoder_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&sor->output.encoder, @@ -1355,10 +1352,13 @@ static int tegra_sor_init(struct host1x_client *client) &sor->output.encoder); drm_connector_register(&sor->output.connector); - sor->output.encoder.possible_crtcs = 0x3; + err = tegra_output_init(drm, &sor->output); + if (err < 0) { + dev_err(client->dev, "failed to initialize output: %d\n", err); + return err; + } - if (gpio_is_valid(sor->output.hpd_gpio)) - enable_irq(sor->output.hpd_irq); + sor->output.encoder.possible_crtcs = 0x3; if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_sor_debugfs_init(sor, drm->primary); -- cgit v1.2.3 From 328ec69e7f9e7192c3f7653a5ec46d6e9a5fe60d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 19 Dec 2014 15:55:08 +0100 Subject: drm/tegra: Output cleanup functions cannot fail The tegra_output_exit() and tegra_output_remove() functions cannot fail, so make them return void. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.h | 4 ++-- drivers/gpu/drm/tegra/dsi.c | 6 +----- drivers/gpu/drm/tegra/hdmi.c | 6 +----- drivers/gpu/drm/tegra/output.c | 8 ++------ drivers/gpu/drm/tegra/rgb.c | 13 ++++--------- drivers/gpu/drm/tegra/sor.c | 8 +++----- 6 files changed, 13 insertions(+), 32 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 699211a89a2e..bf1c47ec46b6 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -213,9 +213,9 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); /* from output.c */ int tegra_output_probe(struct tegra_output *output); -int tegra_output_remove(struct tegra_output *output); +void tegra_output_remove(struct tegra_output *output); int tegra_output_init(struct drm_device *drm, struct tegra_output *output); -int tegra_output_exit(struct tegra_output *output); +void tegra_output_exit(struct tegra_output *output); int tegra_output_connector_get_modes(struct drm_connector *connector); struct drm_encoder * diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 1401d0d7108a..23f4b0fc9734 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -1577,11 +1577,7 @@ static int tegra_dsi_remove(struct platform_device *pdev) return err; } - err = tegra_output_remove(&dsi->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to remove output: %d\n", err); - return err; - } + tegra_output_remove(&dsi->output); mipi_dsi_host_unregister(&dsi->host); tegra_mipi_free(dsi->mipi); diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 03ceb50b1dc9..d41530f1097c 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1563,11 +1563,7 @@ static int tegra_hdmi_remove(struct platform_device *pdev) return err; } - err = tegra_output_remove(&hdmi->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to remove output: %d\n", err); - return err; - } + tegra_output_remove(&hdmi->output); clk_disable_unprepare(hdmi->clk_parent); clk_disable_unprepare(hdmi->clk); diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 2d3b656bfd22..8bb66008b8aa 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -172,7 +172,7 @@ int tegra_output_probe(struct tegra_output *output) return 0; } -int tegra_output_remove(struct tegra_output *output) +void tegra_output_remove(struct tegra_output *output) { if (gpio_is_valid(output->hpd_gpio)) { free_irq(output->hpd_irq, output); @@ -181,8 +181,6 @@ int tegra_output_remove(struct tegra_output *output) if (output->ddc) put_device(&output->ddc->dev); - - return 0; } int tegra_output_init(struct drm_device *drm, struct tegra_output *output) @@ -205,7 +203,7 @@ int tegra_output_init(struct drm_device *drm, struct tegra_output *output) return 0; } -int tegra_output_exit(struct tegra_output *output) +void tegra_output_exit(struct tegra_output *output) { /* * The connector is going away, so the interrupt must be disabled to @@ -216,6 +214,4 @@ int tegra_output_exit(struct tegra_output *output) if (output->panel) drm_panel_detach(output->panel); - - return 0; } diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index ab6093889be8..0c932f9dc12d 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -287,15 +287,10 @@ int tegra_dc_rgb_probe(struct tegra_dc *dc) int tegra_dc_rgb_remove(struct tegra_dc *dc) { - int err; - if (!dc->rgb) return 0; - err = tegra_output_remove(dc->rgb); - if (err < 0) - return err; - + tegra_output_remove(dc->rgb); dc->rgb = NULL; return 0; @@ -342,8 +337,8 @@ int tegra_dc_rgb_init(struct drm_device *drm, struct tegra_dc *dc) int tegra_dc_rgb_exit(struct tegra_dc *dc) { - if (!dc->rgb) - return 0; + if (dc->rgb) + tegra_output_exit(dc->rgb); - return tegra_output_exit(dc->rgb); + return 0; } diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index ee18adf7f653..9e67838c1562 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1396,6 +1396,8 @@ static int tegra_sor_exit(struct host1x_client *client) struct tegra_sor *sor = host1x_client_to_sor(client); int err; + tegra_output_exit(&sor->output); + if (sor->dpaux) { err = tegra_dpaux_detach(sor->dpaux); if (err < 0) { @@ -1500,11 +1502,7 @@ static int tegra_sor_remove(struct platform_device *pdev) return err; } - err = tegra_output_remove(&sor->output); - if (err < 0) { - dev_err(&pdev->dev, "failed to remove output: %d\n", err); - return err; - } + tegra_output_remove(&sor->output); return 0; } -- cgit v1.2.3 From 05f175f4f38de4e89facbb1420978e072ed4870c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 16 Jan 2015 16:30:55 +0100 Subject: drm/tegra: dc: Do not needlessly deassert reset Commit 9c0127004ff4 ("drm/tegra: dc: Add powergate support") changed the driver's ->probe() implementation to deassert the module reset, and with there being nobody else to assert it until ->remove() there is no need to deassert again later on. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index c5197bdaf0bd..03c9ccf7d273 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1153,10 +1153,6 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) drm_crtc_vblank_off(crtc); - /* hardware initialization */ - reset_control_deassert(dc->rst); - usleep_range(10000, 20000); - if (dc->pipe) syncpt = SYNCPT_VBLANK1; else -- cgit v1.2.3 From 4aa3df7149a00cb061d2ba74e2136cd14a6d885a Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 16:27:13 +0100 Subject: drm/tegra: Atomic conversion, phase 1 Implement initial atomic state handling. Hook up the CRTCs, planes' and connectors' ->atomic_destroy_state() callback to ensure that the atomic state objects don't leak. Furthermore the CRTC now implements the ->mode_set_nofb() callback that is used by new helpers to implement ->mode_set() and ->mode_set_base(). These new helpers also make use of the new plane helper functions which the driver now provides. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 397 ++++++++++++++++++++++------------------- drivers/gpu/drm/tegra/drm.c | 2 +- drivers/gpu/drm/tegra/dsi.c | 2 + drivers/gpu/drm/tegra/hdmi.c | 2 + drivers/gpu/drm/tegra/output.c | 1 + drivers/gpu/drm/tegra/rgb.c | 2 + drivers/gpu/drm/tegra/sor.c | 2 + 7 files changed, 223 insertions(+), 185 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 03c9ccf7d273..f7254ab7a5c7 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -18,6 +18,7 @@ #include "drm.h" #include "gem.h" +#include #include struct tegra_dc_soc_info { @@ -200,8 +201,8 @@ static inline u32 compute_initial_dda(unsigned int in) return dfixed_frac(inf); } -static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, - const struct tegra_dc_window *window) +static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, + const struct tegra_dc_window *window) { unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp; unsigned long value, flags; @@ -310,9 +311,11 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; case TEGRA_BO_TILING_MODE_BLOCK: - DRM_ERROR("hardware doesn't support block linear mode\n"); - spin_unlock_irqrestore(&dc->lock, flags); - return -EINVAL; + /* + * No need to handle this here because ->atomic_check + * will already have filtered it out. + */ + break; } tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); @@ -371,34 +374,6 @@ static int tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, tegra_dc_window_commit(dc, index); spin_unlock_irqrestore(&dc->lock, flags); - - return 0; -} - -static int tegra_window_plane_disable(struct drm_plane *plane) -{ - struct tegra_dc *dc = to_tegra_dc(plane->crtc); - struct tegra_plane *p = to_tegra_plane(plane); - unsigned long flags; - u32 value; - - if (!plane->crtc) - return 0; - - spin_lock_irqsave(&dc->lock, flags); - - value = WINDOW_A_SELECT << p->index; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); - - value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - value &= ~WIN_ENABLE; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - tegra_dc_window_commit(dc, p->index); - - spin_unlock_irqrestore(&dc->lock, flags); - - return 0; } static void tegra_plane_destroy(struct drm_plane *plane) @@ -415,57 +390,139 @@ static const u32 tegra_primary_plane_formats[] = { DRM_FORMAT_RGB565, }; -static int tegra_primary_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, - int crtc_y, unsigned int crtc_w, - unsigned int crtc_h, uint32_t src_x, - uint32_t src_y, uint32_t src_w, - uint32_t src_h) +static void tegra_primary_plane_destroy(struct drm_plane *plane) { - struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); + tegra_plane_destroy(plane); +} + +static const struct drm_plane_funcs tegra_primary_plane_funcs = { + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, + .destroy = tegra_primary_plane_destroy, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static int tegra_plane_prepare_fb(struct drm_plane *plane, + struct drm_framebuffer *fb) +{ + return 0; +} + +static void tegra_plane_cleanup_fb(struct drm_plane *plane, + struct drm_framebuffer *fb) +{ +} + +static int tegra_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct tegra_dc *dc = to_tegra_dc(state->crtc); + struct tegra_bo_tiling tiling; + int err; + + /* no need for further checks if the plane is being disabled */ + if (!state->crtc) + return 0; + + err = tegra_fb_get_tiling(state->fb, &tiling); + if (err < 0) + return err; + + if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK && + !dc->soc->supports_block_linear) { + DRM_ERROR("hardware doesn't support block linear mode\n"); + return -EINVAL; + } + + /* + * Tegra doesn't support different strides for U and V planes so we + * error out if the user tries to display a framebuffer with such a + * configuration. + */ + if (drm_format_num_planes(state->fb->pixel_format) > 2) { + if (state->fb->pitches[2] != state->fb->pitches[1]) { + DRM_ERROR("unsupported UV-plane configuration\n"); + return -EINVAL; + } + } + + return 0; +} + +static void tegra_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + struct drm_framebuffer *fb = plane->state->fb; struct tegra_plane *p = to_tegra_plane(plane); - struct tegra_dc *dc = to_tegra_dc(crtc); struct tegra_dc_window window; + unsigned int i; int err; + /* rien ne va plus */ + if (!plane->state->crtc || !plane->state->fb) + return; + memset(&window, 0, sizeof(window)); - window.src.x = src_x >> 16; - window.src.y = src_y >> 16; - window.src.w = src_w >> 16; - window.src.h = src_h >> 16; - window.dst.x = crtc_x; - window.dst.y = crtc_y; - window.dst.w = crtc_w; - window.dst.h = crtc_h; + window.src.x = plane->state->src_x >> 16; + window.src.y = plane->state->src_y >> 16; + window.src.w = plane->state->src_w >> 16; + window.src.h = plane->state->src_h >> 16; + window.dst.x = plane->state->crtc_x; + window.dst.y = plane->state->crtc_y; + window.dst.w = plane->state->crtc_w; + window.dst.h = plane->state->crtc_h; window.format = tegra_dc_format(fb->pixel_format, &window.swap); window.bits_per_pixel = fb->bits_per_pixel; window.bottom_up = tegra_fb_is_bottom_up(fb); err = tegra_fb_get_tiling(fb, &window.tiling); - if (err < 0) - return err; + WARN_ON(err < 0); - window.base[0] = bo->paddr + fb->offsets[0]; - window.stride[0] = fb->pitches[0]; + for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { + struct tegra_bo *bo = tegra_fb_get_plane(fb, i); - err = tegra_dc_setup_window(dc, p->index, &window); - if (err < 0) - return err; + window.base[i] = bo->paddr + fb->offsets[i]; + window.stride[i] = fb->pitches[i]; + } - return 0; + tegra_dc_setup_window(dc, p->index, &window); } -static void tegra_primary_plane_destroy(struct drm_plane *plane) +static void tegra_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) { - tegra_window_plane_disable(plane); - tegra_plane_destroy(plane); + struct tegra_plane *p = to_tegra_plane(plane); + struct tegra_dc *dc; + unsigned long flags; + u32 value; + + /* rien ne va plus */ + if (!old_state || !old_state->crtc) + return; + + dc = to_tegra_dc(old_state->crtc); + + spin_lock_irqsave(&dc->lock, flags); + + value = WINDOW_A_SELECT << p->index; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_WINDOW_HEADER); + + value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); + value &= ~WIN_ENABLE; + tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); + + tegra_dc_window_commit(dc, p->index); + + spin_unlock_irqrestore(&dc->lock, flags); } -static const struct drm_plane_funcs tegra_primary_plane_funcs = { - .update_plane = tegra_primary_plane_update, - .disable_plane = tegra_window_plane_disable, - .destroy = tegra_primary_plane_destroy, +static const struct drm_plane_helper_funcs tegra_primary_plane_helper_funcs = { + .prepare_fb = tegra_plane_prepare_fb, + .cleanup_fb = tegra_plane_cleanup_fb, + .atomic_check = tegra_plane_atomic_check, + .atomic_update = tegra_plane_atomic_update, + .atomic_disable = tegra_plane_atomic_disable, }; static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, @@ -504,6 +561,8 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, return ERR_PTR(err); } + drm_plane_helper_add(&plane->base, &tegra_primary_plane_helper_funcs); + return &plane->base; } @@ -511,27 +570,42 @@ static const u32 tegra_cursor_plane_formats[] = { DRM_FORMAT_RGBA8888, }; -static int tegra_cursor_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, - int crtc_y, unsigned int crtc_w, - unsigned int crtc_h, uint32_t src_x, - uint32_t src_y, uint32_t src_w, - uint32_t src_h) +static int tegra_cursor_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) { - struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); - struct tegra_dc *dc = to_tegra_dc(crtc); - u32 value = CURSOR_CLIP_DISPLAY; + /* no need for further checks if the plane is being disabled */ + if (!state->crtc) + return 0; /* scaling not supported for cursor */ - if ((src_w >> 16 != crtc_w) || (src_h >> 16 != crtc_h)) + if ((state->src_w >> 16 != state->crtc_w) || + (state->src_h >> 16 != state->crtc_h)) return -EINVAL; /* only square cursors supported */ - if (src_w != src_h) + if (state->src_w != state->src_h) + return -EINVAL; + + if (state->crtc_w != 32 && state->crtc_w != 64 && + state->crtc_w != 128 && state->crtc_w != 256) return -EINVAL; - switch (crtc_w) { + return 0; +} + +static void tegra_cursor_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0); + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); + struct drm_plane_state *state = plane->state; + u32 value = CURSOR_CLIP_DISPLAY; + + /* rien ne va plus */ + if (!plane->state->crtc || !plane->state->fb) + return; + + switch (state->crtc_w) { case 32: value |= CURSOR_SIZE_32x32; break; @@ -549,7 +623,9 @@ static int tegra_cursor_plane_update(struct drm_plane *plane, break; default: - return -EINVAL; + WARN(1, "cursor size %ux%u not supported\n", state->crtc_w, + state->crtc_h); + return; } value |= (bo->paddr >> 10) & 0x3fffff; @@ -575,23 +651,25 @@ static int tegra_cursor_plane_update(struct drm_plane *plane, tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL); /* position the cursor */ - value = (crtc_y & 0x3fff) << 16 | (crtc_x & 0x3fff); + value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff); tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); /* apply changes */ tegra_dc_cursor_commit(dc); tegra_dc_commit(dc); - - return 0; } -static int tegra_cursor_plane_disable(struct drm_plane *plane) +static void tegra_cursor_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) { - struct tegra_dc *dc = to_tegra_dc(plane->crtc); + struct tegra_dc *dc; u32 value; - if (!plane->crtc) - return 0; + /* rien ne va plus */ + if (!old_state || !old_state->crtc) + return; + + dc = to_tegra_dc(old_state->crtc); value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~CURSOR_ENABLE; @@ -599,14 +677,21 @@ static int tegra_cursor_plane_disable(struct drm_plane *plane) tegra_dc_cursor_commit(dc); tegra_dc_commit(dc); - - return 0; } static const struct drm_plane_funcs tegra_cursor_plane_funcs = { - .update_plane = tegra_cursor_plane_update, - .disable_plane = tegra_cursor_plane_disable, + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, .destroy = tegra_plane_destroy, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { + .prepare_fb = tegra_plane_prepare_fb, + .cleanup_fb = tegra_plane_cleanup_fb, + .atomic_check = tegra_cursor_atomic_check, + .atomic_update = tegra_cursor_atomic_update, + .atomic_disable = tegra_cursor_atomic_disable, }; static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, @@ -632,71 +717,21 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, return ERR_PTR(err); } - return &plane->base; -} - -static int tegra_overlay_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, - struct drm_framebuffer *fb, int crtc_x, - int crtc_y, unsigned int crtc_w, - unsigned int crtc_h, uint32_t src_x, - uint32_t src_y, uint32_t src_w, - uint32_t src_h) -{ - struct tegra_plane *p = to_tegra_plane(plane); - struct tegra_dc *dc = to_tegra_dc(crtc); - struct tegra_dc_window window; - unsigned int i; - int err; - - memset(&window, 0, sizeof(window)); - window.src.x = src_x >> 16; - window.src.y = src_y >> 16; - window.src.w = src_w >> 16; - window.src.h = src_h >> 16; - window.dst.x = crtc_x; - window.dst.y = crtc_y; - window.dst.w = crtc_w; - window.dst.h = crtc_h; - window.format = tegra_dc_format(fb->pixel_format, &window.swap); - window.bits_per_pixel = fb->bits_per_pixel; - window.bottom_up = tegra_fb_is_bottom_up(fb); - - err = tegra_fb_get_tiling(fb, &window.tiling); - if (err < 0) - return err; + drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs); - for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { - struct tegra_bo *bo = tegra_fb_get_plane(fb, i); - - window.base[i] = bo->paddr + fb->offsets[i]; - - /* - * Tegra doesn't support different strides for U and V planes - * so we display a warning if the user tries to display a - * framebuffer with such a configuration. - */ - if (i >= 2) { - if (fb->pitches[i] != window.stride[1]) - DRM_ERROR("unsupported UV-plane configuration\n"); - } else { - window.stride[i] = fb->pitches[i]; - } - } - - return tegra_dc_setup_window(dc, p->index, &window); + return &plane->base; } static void tegra_overlay_plane_destroy(struct drm_plane *plane) { - tegra_window_plane_disable(plane); tegra_plane_destroy(plane); } static const struct drm_plane_funcs tegra_overlay_plane_funcs = { - .update_plane = tegra_overlay_plane_update, - .disable_plane = tegra_window_plane_disable, + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, .destroy = tegra_overlay_plane_destroy, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; static const uint32_t tegra_overlay_plane_formats[] = { @@ -709,6 +744,14 @@ static const uint32_t tegra_overlay_plane_formats[] = { DRM_FORMAT_YUV422, }; +static const struct drm_plane_helper_funcs tegra_overlay_plane_helper_funcs = { + .prepare_fb = tegra_plane_prepare_fb, + .cleanup_fb = tegra_plane_cleanup_fb, + .atomic_check = tegra_plane_atomic_check, + .atomic_update = tegra_plane_atomic_update, + .atomic_disable = tegra_plane_atomic_disable, +}; + static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, struct tegra_dc *dc, unsigned int index) @@ -735,6 +778,8 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, return ERR_PTR(err); } + drm_plane_helper_add(&plane->base, &tegra_overlay_plane_helper_funcs); + return &plane->base; } @@ -953,6 +998,7 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = tegra_dc_page_flip, .set_config = drm_crtc_helper_set_config, .destroy = tegra_dc_destroy, + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; static void tegra_dc_stop(struct tegra_dc *dc) @@ -1090,16 +1136,11 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, return 0; } -static int tegra_crtc_mode_set(struct drm_crtc *crtc, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted, - int x, int y, struct drm_framebuffer *old_fb) +static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) { - struct tegra_bo *bo = tegra_fb_get_plane(crtc->primary->fb, 0); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct tegra_dc *dc = to_tegra_dc(crtc); - struct tegra_dc_window window; u32 value; - int err; /* program display mode */ tegra_dc_set_timings(dc, mode); @@ -1113,36 +1154,6 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, value &= ~INTERLACE_ENABLE; tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); } - - /* setup window parameters */ - memset(&window, 0, sizeof(window)); - window.src.x = 0; - window.src.y = 0; - window.src.w = mode->hdisplay; - window.src.h = mode->vdisplay; - window.dst.x = 0; - window.dst.y = 0; - window.dst.w = mode->hdisplay; - window.dst.h = mode->vdisplay; - window.format = tegra_dc_format(crtc->primary->fb->pixel_format, - &window.swap); - window.bits_per_pixel = crtc->primary->fb->bits_per_pixel; - window.stride[0] = crtc->primary->fb->pitches[0]; - window.base[0] = bo->paddr; - - err = tegra_dc_setup_window(dc, 0, &window); - if (err < 0) - dev_err(dc->dev, "failed to enable root plane\n"); - - return 0; -} - -static int tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct tegra_dc *dc = to_tegra_dc(crtc); - - return tegra_dc_set_base(dc, x, y, crtc->primary->fb); } static void tegra_crtc_prepare(struct drm_crtc *crtc) @@ -1193,13 +1204,31 @@ static void tegra_crtc_commit(struct drm_crtc *crtc) tegra_dc_commit(dc); } +static int tegra_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return 0; +} + +static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) +{ +} + +static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) +{ +} + static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { .disable = tegra_crtc_disable, .mode_fixup = tegra_crtc_mode_fixup, - .mode_set = tegra_crtc_mode_set, - .mode_set_base = tegra_crtc_mode_set_base, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = tegra_crtc_mode_set_nofb, + .mode_set_base = drm_helper_crtc_mode_set_base, .prepare = tegra_crtc_prepare, .commit = tegra_crtc_commit, + .atomic_check = tegra_crtc_atomic_check, + .atomic_begin = tegra_crtc_atomic_begin, + .atomic_flush = tegra_crtc_atomic_flush, }; static irqreturn_t tegra_dc_irq(int irq, void *data) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index e29d9146b7c7..0e0da86f627c 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -121,8 +121,8 @@ static int tegra_drm_unload(struct drm_device *drm) drm_kms_helper_poll_fini(drm); tegra_drm_fb_exit(drm); - drm_vblank_cleanup(drm); drm_mode_config_cleanup(drm); + drm_vblank_cleanup(drm); err = host1x_device_exit(device); if (err < 0) diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 23f4b0fc9734..234cd1c3079f 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -17,6 +17,7 @@ #include +#include #include #include @@ -736,6 +737,7 @@ static const struct drm_connector_funcs tegra_dsi_connector_funcs = { .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static enum drm_mode_status diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index d41530f1097c..bf868d54ef7e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -781,6 +782,7 @@ static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static enum drm_mode_status diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c index 8bb66008b8aa..37db47975d48 100644 --- a/drivers/gpu/drm/tegra/output.c +++ b/drivers/gpu/drm/tegra/output.c @@ -9,6 +9,7 @@ #include +#include #include #include "drm.h" diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 0c932f9dc12d..23ba40370eb2 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -9,6 +9,7 @@ #include +#include #include #include "drm.h" @@ -97,6 +98,7 @@ static const struct drm_connector_funcs tegra_rgb_connector_funcs = { .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static enum drm_mode_status diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 9e67838c1562..e2f399363f09 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -15,6 +15,7 @@ #include +#include #include #include @@ -737,6 +738,7 @@ static const struct drm_connector_funcs tegra_sor_connector_funcs = { .detect = tegra_sor_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static int tegra_sor_connector_get_modes(struct drm_connector *connector) -- cgit v1.2.3 From 9d44189f55c77face595982bad3310bd4078b9fe Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 17:02:53 +0100 Subject: drm/tegra: Atomic conversion, phase 2 Hook up the default ->reset() and ->atomic_duplicate_state() helpers. This ensures that state objects are properly created and framebuffer reference counts correctly maintained. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 12 ++++++++++++ drivers/gpu/drm/tegra/drm.c | 2 ++ drivers/gpu/drm/tegra/dsi.c | 2 ++ drivers/gpu/drm/tegra/hdmi.c | 2 ++ drivers/gpu/drm/tegra/rgb.c | 2 ++ drivers/gpu/drm/tegra/sor.c | 2 ++ 6 files changed, 22 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index f7254ab7a5c7..6bf5014ad2dc 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -18,6 +18,7 @@ #include "drm.h" #include "gem.h" +#include #include #include @@ -399,6 +400,8 @@ static const struct drm_plane_funcs tegra_primary_plane_funcs = { .update_plane = drm_plane_helper_update, .disable_plane = drm_plane_helper_disable, .destroy = tegra_primary_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; @@ -683,6 +686,8 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = { .update_plane = drm_plane_helper_update, .disable_plane = drm_plane_helper_disable, .destroy = tegra_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; @@ -731,6 +736,8 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = { .update_plane = drm_plane_helper_update, .disable_plane = drm_plane_helper_disable, .destroy = tegra_overlay_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; @@ -983,6 +990,9 @@ static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, drm_crtc_vblank_get(crtc); } + if (crtc->primary->state) + drm_atomic_set_fb_for_plane(crtc->primary->state, fb); + tegra_dc_set_base(dc, 0, 0, fb); crtc->primary->fb = fb; @@ -998,6 +1008,8 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = tegra_dc_page_flip, .set_config = drm_crtc_helper_set_config, .destroy = tegra_dc_destroy, + .reset = drm_atomic_helper_crtc_reset, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, }; diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 0e0da86f627c..c1bb0d901454 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -77,6 +77,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) if (err < 0) goto fbdev; + drm_mode_config_reset(drm); + /* * We don't use the drm_irq_install() helpers provided by the DRM * core, so we need to set this manually in order to allow the diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 234cd1c3079f..d98058dc4580 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -734,9 +734,11 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode) static const struct drm_connector_funcs tegra_dsi_connector_funcs = { .dpms = tegra_dsi_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index bf868d54ef7e..728681a3f8c9 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -779,9 +779,11 @@ static void tegra_hdmi_connector_dpms(struct drm_connector *connector, static const struct drm_connector_funcs tegra_hdmi_connector_funcs = { .dpms = tegra_hdmi_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 23ba40370eb2..78e3cb1529d0 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -95,9 +95,11 @@ static void tegra_rgb_connector_dpms(struct drm_connector *connector, static const struct drm_connector_funcs tegra_rgb_connector_funcs = { .dpms = tegra_rgb_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index e2f399363f09..2793e75c6573 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -735,9 +735,11 @@ tegra_sor_connector_detect(struct drm_connector *connector, bool force) static const struct drm_connector_funcs tegra_sor_connector_funcs = { .dpms = tegra_sor_connector_dpms, + .reset = drm_atomic_helper_connector_reset, .detect = tegra_sor_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -- cgit v1.2.3 From 07866963b675b358d82baf8df73dba545d967a1d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 17:08:06 +0100 Subject: drm/tegra: Atomic conversion, phase 3, step 1 Switch out the regular plane helpers for the atomic plane helpers. Also use the default atomic helpers to implement the ->atomic_check() and ->atomic_commit() callbacks. The driver now exclusively uses the atomic interfaces. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 12 ++++++------ drivers/gpu/drm/tegra/drm.c | 4 ++++ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 6bf5014ad2dc..4fb9048b2f65 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -397,8 +397,8 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane) } static const struct drm_plane_funcs tegra_primary_plane_funcs = { - .update_plane = drm_plane_helper_update, - .disable_plane = drm_plane_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_primary_plane_destroy, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, @@ -683,8 +683,8 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane, } static const struct drm_plane_funcs tegra_cursor_plane_funcs = { - .update_plane = drm_plane_helper_update, - .disable_plane = drm_plane_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_plane_destroy, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, @@ -733,8 +733,8 @@ static void tegra_overlay_plane_destroy(struct drm_plane *plane) } static const struct drm_plane_funcs tegra_overlay_plane_funcs = { - .update_plane = drm_plane_helper_update, - .disable_plane = drm_plane_helper_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_overlay_plane_destroy, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index c1bb0d901454..d01484cf3432 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -10,6 +10,8 @@ #include #include +#include + #include "drm.h" #include "gem.h" @@ -29,6 +31,8 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { #ifdef CONFIG_DRM_TEGRA_FBDEV .output_poll_changed = tegra_fb_output_poll_changed, #endif + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) -- cgit v1.2.3 From ca915b108a39081edff1206ec00ecdb4136408ac Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:14:45 +0100 Subject: drm/tegra: dc: Store clock setup in atomic state This allows the clock setup to be separated from the clock programming and better matches the expectations of the atomic modesetting where no code paths must fail during modeset. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 71 +++++++++++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/tegra/drm.h | 4 +++ 2 files changed, 72 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4fb9048b2f65..b905a4e2bdf9 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -41,6 +41,22 @@ static inline struct tegra_plane *to_tegra_plane(struct drm_plane *plane) return container_of(plane, struct tegra_plane, base); } +struct tegra_dc_state { + struct drm_crtc_state base; + + struct clk *clk; + unsigned long pclk; + unsigned int div; +}; + +static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) +{ + if (state) + return container_of(state, struct tegra_dc_state, base); + + return NULL; +} + static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index) { u32 value = WIN_A_ACT_REQ << index; @@ -1004,13 +1020,48 @@ static void tegra_dc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); } +static void tegra_crtc_reset(struct drm_crtc *crtc) +{ + struct tegra_dc_state *state; + + kfree(crtc->state); + crtc->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + crtc->state = &state->base; +} + +static struct drm_crtc_state * +tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc) +{ + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc_state *copy; + + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (!copy) + return NULL; + + copy->base.mode_changed = false; + copy->base.planes_changed = false; + copy->base.event = NULL; + + return ©->base; +} + +static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + kfree(state); +} + static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = tegra_dc_page_flip, .set_config = drm_crtc_helper_set_config, .destroy = tegra_dc_destroy, - .reset = drm_atomic_helper_crtc_reset, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .reset = tegra_crtc_reset, + .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, + .atomic_destroy_state = tegra_crtc_atomic_destroy_state, }; static void tegra_dc_stop(struct tegra_dc *dc) @@ -1148,6 +1199,20 @@ int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, return 0; } +int tegra_dc_state_setup_clock(struct tegra_dc *dc, + struct drm_crtc_state *crtc_state, + struct clk *clk, unsigned long pclk, + unsigned int div) +{ + struct tegra_dc_state *state = to_dc_state(crtc_state); + + state->clk = clk; + state->pclk = pclk; + state->div = div; + + return 0; +} + static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct drm_display_mode *mode = &crtc->state->adjusted_mode; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index bf1c47ec46b6..3db719de312f 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -180,6 +180,10 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file); void tegra_dc_commit(struct tegra_dc *dc); int tegra_dc_setup_clock(struct tegra_dc *dc, struct clk *parent, unsigned long pclk, unsigned int div); +int tegra_dc_state_setup_clock(struct tegra_dc *dc, + struct drm_crtc_state *crtc_state, + struct clk *clk, unsigned long pclk, + unsigned int div); struct tegra_output { struct device_node *of_node; -- cgit v1.2.3 From 3cebae6737b100323baca21de6bce6647249e778 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 17 Dec 2014 17:04:36 +0100 Subject: drm/tegra: rgb: Implement ->atomic_check() The implementation of the ->atomic_check() callback precomputes all parameters to check if the given configuration can be applied. If so the precomputed values are stored in the atomic state object for the encoder and applied during modeset. In that way the modeset no longer needs to perform any checking but simply program values into registers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/rgb.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 78e3cb1529d0..be1b38936dbe 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -235,6 +235,47 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder) drm_panel_unprepare(output->panel); } +static int +tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(conn_state->crtc); + unsigned long pclk = crtc_state->mode.clock * 1000; + struct tegra_rgb *rgb = to_rgb(output); + unsigned int div; + int err; + + /* + * We may not want to change the frequency of the parent clock, since + * it may be a parent for other peripherals. This is due to the fact + * that on Tegra20 there's only a single clock dedicated to display + * (pll_d_out0), whereas later generations have a second one that can + * be used to independently drive a second output (pll_d2_out0). + * + * As a way to support multiple outputs on Tegra20 as well, pll_p is + * typically used as the parent clock for the display controllers. + * But this comes at a cost: pll_p is the parent of several other + * peripherals, so its frequency shouldn't change out of the blue. + * + * The best we can do at this point is to use the shift clock divider + * and hope that the desired frequency can be matched (or at least + * matched sufficiently close that the panel will still work). + */ + div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2; + pclk = 0; + + err = tegra_dc_state_setup_clock(dc, crtc_state, rgb->clk_parent, + pclk, div); + if (err < 0) { + dev_err(output->dev, "failed to setup CRTC state: %d\n", err); + return err; + } + + return err; +} + static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = { .dpms = tegra_rgb_encoder_dpms, .mode_fixup = tegra_rgb_encoder_mode_fixup, @@ -242,6 +283,7 @@ static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = { .commit = tegra_rgb_encoder_commit, .mode_set = tegra_rgb_encoder_mode_set, .disable = tegra_rgb_encoder_disable, + .atomic_check = tegra_rgb_encoder_atomic_check, }; int tegra_dc_rgb_probe(struct tegra_dc *dc) -- cgit v1.2.3 From ebd14afe8fa7d3f00f2ab1c8c4befee2fa6c0504 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:22:28 +0100 Subject: drm/tegra: dsi: Implement ->atomic_check() The implementation of the ->atomic_check() callback precomputes all parameters to check if the given configuration can be applied. If so the precomputed values are stored in the atomic state object for the encoder and applied during modeset. In that way the modeset no longer needs to perform any checking but simply program values into registers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 269 ++++++++++++++++++++++++++++++++------------ 1 file changed, 196 insertions(+), 73 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index d98058dc4580..00b307120cdd 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -28,6 +28,28 @@ #include "dsi.h" #include "mipi-phy.h" +struct tegra_dsi_state { + struct drm_connector_state base; + + struct mipi_dphy_timing timing; + unsigned long period; + + unsigned int vrefresh; + unsigned int lanes; + unsigned long pclk; + unsigned long bclk; + + enum tegra_dsi_format format; + unsigned int mul; + unsigned int div; +}; + +static inline struct tegra_dsi_state * +to_dsi_state(struct drm_connector_state *state) +{ + return container_of(state, struct tegra_dsi_state, base); +} + struct tegra_dsi { struct host1x_client client; struct tegra_output output; @@ -77,6 +99,11 @@ static inline struct tegra_dsi *to_dsi(struct tegra_output *output) return container_of(output, struct tegra_dsi, output); } +static struct tegra_dsi_state *tegra_dsi_get_state(struct tegra_dsi *dsi) +{ + return to_dsi_state(dsi->output.connector.state); +} + static inline u32 tegra_dsi_readl(struct tegra_dsi *dsi, unsigned long reg) { return readl(dsi->regs + (reg << 2)); @@ -335,62 +362,36 @@ static const u32 pkt_seq_command_mode[NUM_PKT_SEQ] = { [11] = 0, }; -static int tegra_dsi_set_phy_timing(struct tegra_dsi *dsi) +static void tegra_dsi_set_phy_timing(struct tegra_dsi *dsi, + unsigned long period, + const struct mipi_dphy_timing *timing) { - struct mipi_dphy_timing timing; - unsigned long period; u32 value; - long rate; - int err; - - rate = clk_get_rate(dsi->clk); - if (rate < 0) - return rate; - - period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate * 2); - err = mipi_dphy_timing_get_default(&timing, period); - if (err < 0) - return err; - - err = mipi_dphy_timing_validate(&timing, period); - if (err < 0) { - dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err); - return err; - } - - /* - * The D-PHY timing fields below are expressed in byte-clock cycles, - * so multiply the period by 8. - */ - period *= 8; - - value = DSI_TIMING_FIELD(timing.hsexit, period, 1) << 24 | - DSI_TIMING_FIELD(timing.hstrail, period, 0) << 16 | - DSI_TIMING_FIELD(timing.hszero, period, 3) << 8 | - DSI_TIMING_FIELD(timing.hsprepare, period, 1); + value = DSI_TIMING_FIELD(timing->hsexit, period, 1) << 24 | + DSI_TIMING_FIELD(timing->hstrail, period, 0) << 16 | + DSI_TIMING_FIELD(timing->hszero, period, 3) << 8 | + DSI_TIMING_FIELD(timing->hsprepare, period, 1); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_0); - value = DSI_TIMING_FIELD(timing.clktrail, period, 1) << 24 | - DSI_TIMING_FIELD(timing.clkpost, period, 1) << 16 | - DSI_TIMING_FIELD(timing.clkzero, period, 1) << 8 | - DSI_TIMING_FIELD(timing.lpx, period, 1); + value = DSI_TIMING_FIELD(timing->clktrail, period, 1) << 24 | + DSI_TIMING_FIELD(timing->clkpost, period, 1) << 16 | + DSI_TIMING_FIELD(timing->clkzero, period, 1) << 8 | + DSI_TIMING_FIELD(timing->lpx, period, 1); tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_1); - value = DSI_TIMING_FIELD(timing.clkprepare, period, 1) << 16 | - DSI_TIMING_FIELD(timing.clkpre, period, 1) << 8 | + value = DSI_TIMING_FIELD(timing->clkprepare, period, 1) << 16 | + DSI_TIMING_FIELD(timing->clkpre, period, 1) << 8 | DSI_TIMING_FIELD(0xff * period, period, 0) << 0; tegra_dsi_writel(dsi, value, DSI_PHY_TIMING_2); - value = DSI_TIMING_FIELD(timing.taget, period, 1) << 16 | - DSI_TIMING_FIELD(timing.tasure, period, 1) << 8 | - DSI_TIMING_FIELD(timing.tago, period, 1); + value = DSI_TIMING_FIELD(timing->taget, period, 1) << 16 | + DSI_TIMING_FIELD(timing->tasure, period, 1) << 8 | + DSI_TIMING_FIELD(timing->tago, period, 1); tegra_dsi_writel(dsi, value, DSI_BTA_TIMING); if (dsi->slave) - return tegra_dsi_set_phy_timing(dsi->slave); - - return 0; + tegra_dsi_set_phy_timing(dsi->slave, period, timing); } static int tegra_dsi_get_muldiv(enum mipi_dsi_pixel_format format, @@ -482,14 +483,22 @@ static unsigned int tegra_dsi_get_lanes(struct tegra_dsi *dsi) return dsi->lanes; } -static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, - const struct drm_display_mode *mode) +static void tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, + const struct drm_display_mode *mode) { unsigned int hact, hsw, hbp, hfp, i, mul, div; - enum tegra_dsi_format format; + struct tegra_dsi_state *state; const u32 *pkt_seq; u32 value; - int err; + + /* XXX: pass in state into this function? */ + if (dsi->master) + state = tegra_dsi_get_state(dsi->master); + else + state = tegra_dsi_get_state(dsi); + + mul = state->mul; + div = state->div; if (dsi->flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { DRM_DEBUG_KMS("Non-burst video mode with sync pulses\n"); @@ -502,15 +511,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, pkt_seq = pkt_seq_command_mode; } - err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); - if (err < 0) - return err; - - err = tegra_dsi_get_format(dsi->format, &format); - if (err < 0) - return err; - - value = DSI_CONTROL_CHANNEL(0) | DSI_CONTROL_FORMAT(format) | + value = DSI_CONTROL_CHANNEL(0) | + DSI_CONTROL_FORMAT(state->format) | DSI_CONTROL_LANES(dsi->lanes - 1) | DSI_CONTROL_SOURCE(pipe); tegra_dsi_writel(dsi, value, DSI_CONTROL); @@ -589,8 +591,8 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, /* set SOL delay */ if (dsi->master || dsi->slave) { - unsigned int lanes = tegra_dsi_get_lanes(dsi); unsigned long delay, bclk, bclk_ganged; + unsigned int lanes = state->lanes; /* SOL to valid, valid to FIFO and FIFO write delay */ delay = 4 + 4 + 2; @@ -610,9 +612,7 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, } if (dsi->slave) { - err = tegra_dsi_configure(dsi->slave, pipe, mode); - if (err < 0) - return err; + tegra_dsi_configure(dsi->slave, pipe, mode); /* * TODO: Support modes other than symmetrical left-right @@ -622,8 +622,6 @@ static int tegra_dsi_configure(struct tegra_dsi *dsi, unsigned int pipe, tegra_dsi_ganged_enable(dsi->slave, mode->hdisplay / 2, mode->hdisplay / 2); } - - return 0; } static int tegra_dsi_wait_idle(struct tegra_dsi *dsi, unsigned long timeout) @@ -732,13 +730,38 @@ static void tegra_dsi_connector_dpms(struct drm_connector *connector, int mode) { } +static void tegra_dsi_connector_reset(struct drm_connector *connector) +{ + struct tegra_dsi_state *state; + + kfree(connector->state); + connector->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) + connector->state = &state->base; +} + +static struct drm_connector_state * +tegra_dsi_connector_duplicate_state(struct drm_connector *connector) +{ + struct tegra_dsi_state *state = to_dsi_state(connector->state); + struct tegra_dsi_state *copy; + + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (!copy) + return NULL; + + return ©->base; +} + static const struct drm_connector_funcs tegra_dsi_connector_funcs = { .dpms = tegra_dsi_connector_dpms, - .reset = drm_atomic_helper_connector_reset, + .reset = tegra_dsi_connector_reset, .detect = tegra_output_connector_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = tegra_output_connector_destroy, - .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_duplicate_state = tegra_dsi_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -771,7 +794,9 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, struct tegra_dc *dc = to_tegra_dc(encoder->crtc); unsigned int mul, div, scdiv, vrefresh, lanes; struct tegra_dsi *dsi = to_dsi(output); + struct mipi_dphy_timing timing; unsigned long pclk, bclk, plld; + unsigned long period; int err; lanes = tegra_dsi_get_lanes(dsi); @@ -792,6 +817,7 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, * Compute bit clock and round up to the next MHz. */ plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; + period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld); /* * We divide the frequency by two here, but we make up for that by @@ -827,12 +853,22 @@ static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, tegra_dsi_set_timeout(dsi, bclk, vrefresh); - err = tegra_dsi_set_phy_timing(dsi); + err = mipi_dphy_timing_get_default(&timing, period); + if (err < 0) + return err; + + err = mipi_dphy_timing_validate(&timing, period); if (err < 0) { - dev_err(dsi->dev, "failed to setup D-PHY timing: %d\n", err); - return false; + dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err); + return err; } + /* + * The D-PHY timing fields are expressed in byte-clock cycles, so + * multiply the period by 8. + */ + tegra_dsi_set_phy_timing(dsi, period * 8, &timing); + return true; } @@ -851,19 +887,24 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder, struct tegra_output *output = encoder_to_output(encoder); struct tegra_dc *dc = to_tegra_dc(encoder->crtc); struct tegra_dsi *dsi = to_dsi(output); + struct tegra_dsi_state *state; u32 value; - int err; + state = tegra_dsi_get_state(dsi); - err = tegra_dsi_configure(dsi, dc->pipe, mode); - if (err < 0) { - dev_err(dsi->dev, "failed to configure DSI: %d\n", err); - return; - } + tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh); + + /* + * The D-PHY timing fields are expressed in byte-clock cycles, so + * multiply the period by 8. + */ + tegra_dsi_set_phy_timing(dsi, state->period * 8, &state->timing); if (output->panel) drm_panel_prepare(output->panel); + tegra_dsi_configure(dsi, dc->pipe, mode); + /* enable display controller */ value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value |= DSI_ENABLE; @@ -929,6 +970,87 @@ static void tegra_dsi_encoder_disable(struct drm_encoder *encoder) return; } +static int +tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dsi_state *state = to_dsi_state(conn_state); + struct tegra_dc *dc = to_tegra_dc(conn_state->crtc); + struct tegra_dsi *dsi = to_dsi(output); + unsigned int scdiv; + unsigned long plld; + int err; + + state->pclk = crtc_state->mode.clock * 1000; + + err = tegra_dsi_get_muldiv(dsi->format, &state->mul, &state->div); + if (err < 0) + return err; + + state->lanes = tegra_dsi_get_lanes(dsi); + + err = tegra_dsi_get_format(dsi->format, &state->format); + if (err < 0) + return err; + + state->vrefresh = drm_mode_vrefresh(&crtc_state->mode); + + /* compute byte clock */ + state->bclk = (state->pclk * state->mul) / (state->div * state->lanes); + + DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", state->mul, state->div, + state->lanes); + DRM_DEBUG_KMS("format: %u, vrefresh: %u\n", state->format, + state->vrefresh); + DRM_DEBUG_KMS("bclk: %lu\n", state->bclk); + + /* + * Compute bit clock and round up to the next MHz. + */ + plld = DIV_ROUND_UP(state->bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; + state->period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld); + + err = mipi_dphy_timing_get_default(&state->timing, state->period); + if (err < 0) + return err; + + err = mipi_dphy_timing_validate(&state->timing, state->period); + if (err < 0) { + dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err); + return err; + } + + /* + * We divide the frequency by two here, but we make up for that by + * setting the shift clock divider (further below) to half of the + * correct value. + */ + plld /= 2; + + /* + * Derive pixel clock from bit clock using the shift clock divider. + * Note that this is only half of what we would expect, but we need + * that to make up for the fact that we divided the bit clock by a + * factor of two above. + * + * It's not clear exactly why this is necessary, but the display is + * not working properly otherwise. Perhaps the PLLs cannot generate + * frequencies sufficiently high. + */ + scdiv = ((8 * state->mul) / (state->div * state->lanes)) - 2; + + err = tegra_dc_state_setup_clock(dc, crtc_state, dsi->clk_parent, + plld, scdiv); + if (err < 0) { + dev_err(output->dev, "failed to setup CRTC state: %d\n", err); + return err; + } + + return err; +} + static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { .dpms = tegra_dsi_encoder_dpms, .mode_fixup = tegra_dsi_encoder_mode_fixup, @@ -936,6 +1058,7 @@ static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { .commit = tegra_dsi_encoder_commit, .mode_set = tegra_dsi_encoder_mode_set, .disable = tegra_dsi_encoder_disable, + .atomic_check = tegra_dsi_encoder_atomic_check, }; static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) -- cgit v1.2.3 From a9825a6e27a81dac1641ef13fae8b480d7d11d5f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:33:03 +0100 Subject: drm/tegra: hdmi: Implement ->atomic_check() The implementation of the ->atomic_check() callback precomputes all parameters to check if the given configuration can be applied. If so the precomputed values are stored in the atomic state object for the encoder and applied during modeset. In that way the modeset no longer needs to perform any checking but simply program values into registers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/hdmi.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 728681a3f8c9..abb1ea0385ec 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1081,6 +1081,27 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) } } +static int +tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(conn_state->crtc); + unsigned long pclk = crtc_state->mode.clock * 1000; + struct tegra_hdmi *hdmi = to_hdmi(output); + int err; + + err = tegra_dc_state_setup_clock(dc, crtc_state, hdmi->clk_parent, + pclk, 0); + if (err < 0) { + dev_err(output->dev, "failed to setup CRTC state: %d\n", err); + return err; + } + + return err; +} + static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .dpms = tegra_hdmi_encoder_dpms, .mode_fixup = tegra_hdmi_encoder_mode_fixup, @@ -1088,6 +1109,7 @@ static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .commit = tegra_hdmi_encoder_commit, .mode_set = tegra_hdmi_encoder_mode_set, .disable = tegra_hdmi_encoder_disable, + .atomic_check = tegra_hdmi_encoder_atomic_check, }; static int tegra_hdmi_show_regs(struct seq_file *s, void *data) -- cgit v1.2.3 From 82f1511c35643575e6f28013a3bb348e35676491 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 17:26:46 +0100 Subject: drm/tegra: sor: Implement ->atomic_check() The implementation of the ->atomic_check() callback precomputes all parameters to check if the given configuration can be applied. If so the precomputed values are stored in the atomic state object for the encoder and applied during modeset. In that way the modeset no longer needs to perform any checking but simply program values into registers. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/sor.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 2793e75c6573..7463ea02a083 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1320,6 +1320,27 @@ unlock: mutex_unlock(&sor->lock); } +static int +tegra_sor_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct tegra_output *output = encoder_to_output(encoder); + struct tegra_dc *dc = to_tegra_dc(conn_state->crtc); + unsigned long pclk = crtc_state->mode.clock * 1000; + struct tegra_sor *sor = to_sor(output); + int err; + + err = tegra_dc_state_setup_clock(dc, crtc_state, sor->clk_parent, + pclk, 0); + if (err < 0) { + dev_err(output->dev, "failed to setup CRTC state: %d\n", err); + return err; + } + + return 0; +} + static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = { .dpms = tegra_sor_encoder_dpms, .mode_fixup = tegra_sor_encoder_mode_fixup, @@ -1327,6 +1348,7 @@ static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = { .commit = tegra_sor_encoder_commit, .mode_set = tegra_sor_encoder_mode_set, .disable = tegra_sor_encoder_disable, + .atomic_check = tegra_sor_encoder_atomic_check, }; static int tegra_sor_init(struct host1x_client *client) -- cgit v1.2.3 From 76d59ed049197bdaaa24c0e061a105af6ce74457 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 19 Dec 2014 15:09:16 +0100 Subject: drm/tegra: dc: Use atomic clock state in modeset All clock state is now stored in the display controller's atomic state, so the output drivers no longer need to call back into the display controller driver to set up the clock. This is also required to make sure no hardware changes are made before validating a configuration. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index b905a4e2bdf9..5d6c00abd203 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1213,12 +1213,49 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc, return 0; } +static void tegra_dc_commit_state(struct tegra_dc *dc, + struct tegra_dc_state *state) +{ + u32 value; + int err; + + err = clk_set_parent(dc->clk, state->clk); + if (err < 0) + dev_err(dc->dev, "failed to set parent clock: %d\n", err); + + /* + * Outputs may not want to change the parent clock rate. This is only + * relevant to Tegra20 where only a single display PLL is available. + * Since that PLL would typically be used for HDMI, an internal LVDS + * panel would need to be driven by some other clock such as PLL_P + * which is shared with other peripherals. Changing the clock rate + * should therefore be avoided. + */ + if (state->pclk > 0) { + err = clk_set_rate(state->clk, state->pclk); + if (err < 0) + dev_err(dc->dev, + "failed to set clock rate to %lu Hz\n", + state->pclk); + } + + DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk), + state->div); + DRM_DEBUG_KMS("pclk: %lu\n", state->pclk); + + value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1; + tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL); +} + static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct tegra_dc_state *state = to_dc_state(crtc->state); struct tegra_dc *dc = to_tegra_dc(crtc); u32 value; + tegra_dc_commit_state(dc, state); + /* program display mode */ tegra_dc_set_timings(dc, mode); -- cgit v1.2.3 From 74f48791ad69789bc70eb4d99233d83e016f5f14 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 17:08:20 +0100 Subject: drm/tegra: Atomic conversion, phase 3, step 2 Replace drm_crtc_helper_set_config() by drm_atomic_helper_set_config(). All drivers have now been converted to use ->atomic_check() to set the atomic state, therefore the atomic mode setting helpers can be used. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 5d6c00abd203..aa66abb57b1c 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1057,7 +1057,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, static const struct drm_crtc_funcs tegra_crtc_funcs = { .page_flip = tegra_dc_page_flip, - .set_config = drm_crtc_helper_set_config, + .set_config = drm_atomic_helper_set_config, .destroy = tegra_dc_destroy, .reset = tegra_crtc_reset, .atomic_duplicate_state = tegra_crtc_atomic_duplicate_state, -- cgit v1.2.3 From 1503ca47d76e184eaeabe7cfa31de97b5ec36a04 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 24 Nov 2014 17:41:23 +0100 Subject: drm/tegra: Atomic conversion, phase 3, step 3 Provide a custom ->atomic_commit() implementation which supports async commits. The generic atomic page-flip helper can use this to implement page-flipping. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 129 ++++---------------------------------------- drivers/gpu/drm/tegra/drm.c | 84 ++++++++++++++++++++++++++++- drivers/gpu/drm/tegra/drm.h | 6 +++ 3 files changed, 100 insertions(+), 119 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index aa66abb57b1c..2f9af13905da 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -820,99 +820,6 @@ static int tegra_dc_add_planes(struct drm_device *drm, struct tegra_dc *dc) return 0; } -static int tegra_dc_set_base(struct tegra_dc *dc, int x, int y, - struct drm_framebuffer *fb) -{ - struct tegra_bo *bo = tegra_fb_get_plane(fb, 0); - unsigned int h_offset = 0, v_offset = 0; - struct tegra_bo_tiling tiling; - unsigned long value, flags; - unsigned int format, swap; - int err; - - err = tegra_fb_get_tiling(fb, &tiling); - if (err < 0) - return err; - - spin_lock_irqsave(&dc->lock, flags); - - tegra_dc_writel(dc, WINDOW_A_SELECT, DC_CMD_DISPLAY_WINDOW_HEADER); - - value = fb->offsets[0] + y * fb->pitches[0] + - x * fb->bits_per_pixel / 8; - - tegra_dc_writel(dc, bo->paddr + value, DC_WINBUF_START_ADDR); - tegra_dc_writel(dc, fb->pitches[0], DC_WIN_LINE_STRIDE); - - format = tegra_dc_format(fb->pixel_format, &swap); - tegra_dc_writel(dc, format, DC_WIN_COLOR_DEPTH); - tegra_dc_writel(dc, swap, DC_WIN_BYTE_SWAP); - - if (dc->soc->supports_block_linear) { - unsigned long height = tiling.value; - - switch (tiling.mode) { - case TEGRA_BO_TILING_MODE_PITCH: - value = DC_WINBUF_SURFACE_KIND_PITCH; - break; - - case TEGRA_BO_TILING_MODE_TILED: - value = DC_WINBUF_SURFACE_KIND_TILED; - break; - - case TEGRA_BO_TILING_MODE_BLOCK: - value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | - DC_WINBUF_SURFACE_KIND_BLOCK; - break; - } - - tegra_dc_writel(dc, value, DC_WINBUF_SURFACE_KIND); - } else { - switch (tiling.mode) { - case TEGRA_BO_TILING_MODE_PITCH: - value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV | - DC_WIN_BUFFER_ADDR_MODE_LINEAR; - break; - - case TEGRA_BO_TILING_MODE_TILED: - value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV | - DC_WIN_BUFFER_ADDR_MODE_TILE; - break; - - case TEGRA_BO_TILING_MODE_BLOCK: - DRM_ERROR("hardware doesn't support block linear mode\n"); - spin_unlock_irqrestore(&dc->lock, flags); - return -EINVAL; - } - - tegra_dc_writel(dc, value, DC_WIN_BUFFER_ADDR_MODE); - } - - /* make sure bottom-up buffers are properly displayed */ - if (tegra_fb_is_bottom_up(fb)) { - value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - value |= V_DIRECTION; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - - v_offset += fb->height - 1; - } else { - value = tegra_dc_readl(dc, DC_WIN_WIN_OPTIONS); - value &= ~V_DIRECTION; - tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - } - - tegra_dc_writel(dc, h_offset, DC_WINBUF_ADDR_H_OFFSET); - tegra_dc_writel(dc, v_offset, DC_WINBUF_ADDR_V_OFFSET); - - value = GENERAL_ACT_REQ | WIN_A_ACT_REQ; - tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); - - spin_unlock_irqrestore(&dc->lock, flags); - - return 0; -} - void tegra_dc_enable_vblank(struct tegra_dc *dc) { unsigned long value, flags; @@ -991,30 +898,6 @@ void tegra_dc_cancel_page_flip(struct drm_crtc *crtc, struct drm_file *file) spin_unlock_irqrestore(&drm->event_lock, flags); } -static int tegra_dc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, - struct drm_pending_vblank_event *event, uint32_t page_flip_flags) -{ - unsigned int pipe = drm_crtc_index(crtc); - struct tegra_dc *dc = to_tegra_dc(crtc); - - if (dc->event) - return -EBUSY; - - if (event) { - event->pipe = pipe; - dc->event = event; - drm_crtc_vblank_get(crtc); - } - - if (crtc->primary->state) - drm_atomic_set_fb_for_plane(crtc->primary->state, fb); - - tegra_dc_set_base(dc, 0, 0, fb); - crtc->primary->fb = fb; - - return 0; -} - static void tegra_dc_destroy(struct drm_crtc *crtc) { drm_crtc_cleanup(crtc); @@ -1056,7 +939,7 @@ static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc, } static const struct drm_crtc_funcs tegra_crtc_funcs = { - .page_flip = tegra_dc_page_flip, + .page_flip = drm_atomic_helper_page_flip, .set_config = drm_atomic_helper_set_config, .destroy = tegra_dc_destroy, .reset = tegra_crtc_reset, @@ -1326,6 +1209,16 @@ static int tegra_crtc_atomic_check(struct drm_crtc *crtc, static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) { + struct tegra_dc *dc = to_tegra_dc(crtc); + + if (crtc->state->event) { + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + dc->event = crtc->state->event; + crtc->state->event = NULL; + } } static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index d01484cf3432..0edfb5e0b374 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "drm.h" @@ -26,13 +27,90 @@ struct tegra_drm_file { struct list_head contexts; }; +static void tegra_atomic_schedule(struct tegra_drm *tegra, + struct drm_atomic_state *state) +{ + tegra->commit.state = state; + schedule_work(&tegra->commit.work); +} + +static void tegra_atomic_complete(struct tegra_drm *tegra, + struct drm_atomic_state *state) +{ + struct drm_device *drm = tegra->drm; + + /* + * Everything below can be run asynchronously without the need to grab + * any modeset locks at all under one condition: It must be guaranteed + * that the asynchronous work has either been cancelled (if the driver + * supports it, which at least requires that the framebuffers get + * cleaned up with drm_atomic_helper_cleanup_planes()) or completed + * before the new state gets committed on the software side with + * drm_atomic_helper_swap_state(). + * + * This scheme allows new atomic state updates to be prepared and + * checked in parallel to the asynchronous completion of the previous + * update. Which is important since compositors need to figure out the + * composition of the next frame right after having submitted the + * current layout. + */ + + drm_atomic_helper_commit_pre_planes(drm, state); + drm_atomic_helper_commit_planes(drm, state); + drm_atomic_helper_commit_post_planes(drm, state); + + drm_atomic_helper_wait_for_vblanks(drm, state); + + drm_atomic_helper_cleanup_planes(drm, state); + drm_atomic_state_free(state); +} + +static void tegra_atomic_work(struct work_struct *work) +{ + struct tegra_drm *tegra = container_of(work, struct tegra_drm, + commit.work); + + tegra_atomic_complete(tegra, tegra->commit.state); +} + +static int tegra_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *state, bool async) +{ + struct tegra_drm *tegra = drm->dev_private; + int err; + + err = drm_atomic_helper_prepare_planes(drm, state); + if (err) + return err; + + /* serialize outstanding asynchronous commits */ + mutex_lock(&tegra->commit.lock); + flush_work(&tegra->commit.work); + + /* + * This is the point of no return - everything below never fails except + * when the hw goes bonghits. Which means we can commit the new state on + * the software side now. + */ + + drm_atomic_helper_swap_state(drm, state); + + if (async) + tegra_atomic_schedule(tegra, state); + else + tegra_atomic_complete(tegra, state); + + mutex_unlock(&tegra->commit.lock); + return 0; +} + static const struct drm_mode_config_funcs tegra_drm_mode_funcs = { .fb_create = tegra_fb_create, #ifdef CONFIG_DRM_TEGRA_FBDEV .output_poll_changed = tegra_fb_output_poll_changed, #endif .atomic_check = drm_atomic_helper_check, - .atomic_commit = drm_atomic_helper_commit, + .atomic_commit = tegra_atomic_commit, }; static int tegra_drm_load(struct drm_device *drm, unsigned long flags) @@ -58,6 +136,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) mutex_init(&tegra->clients_lock); INIT_LIST_HEAD(&tegra->clients); + + mutex_init(&tegra->commit.lock); + INIT_WORK(&tegra->commit.work, tegra_atomic_work); + drm->dev_private = tegra; tegra->drm = drm; diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index 3db719de312f..b1c7027b26e7 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -50,6 +50,12 @@ struct tegra_drm { #endif unsigned int pitch_align; + + struct { + struct drm_atomic_state *state; + struct work_struct work; + struct mutex lock; + } commit; }; struct tegra_drm_client; -- cgit v1.2.3 From 3f0fb52ef013e76159b35386f22924f99d8034a4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 19 Dec 2014 15:19:21 +0100 Subject: drm/tegra: Remove unused ->mode_fixup() callbacks All output drivers have now been converted to use the ->atomic_check() callback, so the ->mode_fixup() callbacks are no longer used. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dsi.c | 87 -------------------------------------------- drivers/gpu/drm/tegra/hdmi.c | 27 -------------- drivers/gpu/drm/tegra/rgb.c | 38 ------------------- drivers/gpu/drm/tegra/sor.c | 27 -------------- 4 files changed, 179 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 00b307120cdd..6875885a2dca 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -786,92 +786,6 @@ static void tegra_dsi_encoder_dpms(struct drm_encoder *encoder, int mode) { } -static bool tegra_dsi_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - struct tegra_output *output = encoder_to_output(encoder); - struct tegra_dc *dc = to_tegra_dc(encoder->crtc); - unsigned int mul, div, scdiv, vrefresh, lanes; - struct tegra_dsi *dsi = to_dsi(output); - struct mipi_dphy_timing timing; - unsigned long pclk, bclk, plld; - unsigned long period; - int err; - - lanes = tegra_dsi_get_lanes(dsi); - pclk = mode->clock * 1000; - - err = tegra_dsi_get_muldiv(dsi->format, &mul, &div); - if (err < 0) - return err; - - DRM_DEBUG_KMS("mul: %u, div: %u, lanes: %u\n", mul, div, lanes); - vrefresh = drm_mode_vrefresh(mode); - DRM_DEBUG_KMS("vrefresh: %u\n", vrefresh); - - /* compute byte clock */ - bclk = (pclk * mul) / (div * lanes); - - /* - * Compute bit clock and round up to the next MHz. - */ - plld = DIV_ROUND_UP(bclk * 8, USEC_PER_SEC) * USEC_PER_SEC; - period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, plld); - - /* - * We divide the frequency by two here, but we make up for that by - * setting the shift clock divider (further below) to half of the - * correct value. - */ - plld /= 2; - - /* - * Derive pixel clock from bit clock using the shift clock divider. - * Note that this is only half of what we would expect, but we need - * that to make up for the fact that we divided the bit clock by a - * factor of two above. - * - * It's not clear exactly why this is necessary, but the display is - * not working properly otherwise. Perhaps the PLLs cannot generate - * frequencies sufficiently high. - */ - scdiv = ((8 * mul) / (div * lanes)) - 2; - - err = tegra_dc_setup_clock(dc, dsi->clk_parent, plld, scdiv); - if (err < 0) { - dev_err(output->dev, "failed to setup DC clock: %d\n", err); - return false; - } - - err = clk_set_rate(dsi->clk_parent, plld); - if (err < 0) { - dev_err(dsi->dev, "failed to set clock rate to %lu Hz\n", - plld); - return false; - } - - tegra_dsi_set_timeout(dsi, bclk, vrefresh); - - err = mipi_dphy_timing_get_default(&timing, period); - if (err < 0) - return err; - - err = mipi_dphy_timing_validate(&timing, period); - if (err < 0) { - dev_err(dsi->dev, "failed to validate D-PHY timing: %d\n", err); - return err; - } - - /* - * The D-PHY timing fields are expressed in byte-clock cycles, so - * multiply the period by 8. - */ - tegra_dsi_set_phy_timing(dsi, period * 8, &timing); - - return true; -} - static void tegra_dsi_encoder_prepare(struct drm_encoder *encoder) { } @@ -1053,7 +967,6 @@ tegra_dsi_encoder_atomic_check(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs tegra_dsi_encoder_helper_funcs = { .dpms = tegra_dsi_encoder_dpms, - .mode_fixup = tegra_dsi_encoder_mode_fixup, .prepare = tegra_dsi_encoder_prepare, .commit = tegra_dsi_encoder_commit, .mode_set = tegra_dsi_encoder_mode_set, diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index abb1ea0385ec..07771956cc94 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -822,32 +822,6 @@ static void tegra_hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) { } -static bool tegra_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - struct tegra_output *output = encoder_to_output(encoder); - struct tegra_dc *dc = to_tegra_dc(encoder->crtc); - struct tegra_hdmi *hdmi = to_hdmi(output); - unsigned long pclk = mode->clock * 1000; - int err; - - err = tegra_dc_setup_clock(dc, hdmi->clk_parent, pclk, 0); - if (err < 0) { - dev_err(output->dev, "failed to setup DC clock: %d\n", err); - return false; - } - - err = clk_set_rate(hdmi->clk_parent, pclk); - if (err < 0) { - dev_err(output->dev, "failed to set clock rate to %lu Hz\n", - pclk); - return false; - } - - return true; -} - static void tegra_hdmi_encoder_prepare(struct drm_encoder *encoder) { } @@ -1104,7 +1078,6 @@ tegra_hdmi_encoder_atomic_check(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs tegra_hdmi_encoder_helper_funcs = { .dpms = tegra_hdmi_encoder_dpms, - .mode_fixup = tegra_hdmi_encoder_mode_fixup, .prepare = tegra_hdmi_encoder_prepare, .commit = tegra_hdmi_encoder_commit, .mode_set = tegra_hdmi_encoder_mode_set, diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index be1b38936dbe..0c8b458b2364 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -129,43 +129,6 @@ static void tegra_rgb_encoder_dpms(struct drm_encoder *encoder, int mode) { } -static bool tegra_rgb_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - struct tegra_output *output = encoder_to_output(encoder); - unsigned long pclk = mode->clock * 1000; - struct tegra_rgb *rgb = to_rgb(output); - unsigned int div; - int err; - - /* - * We may not want to change the frequency of the parent clock, since - * it may be a parent for other peripherals. This is due to the fact - * that on Tegra20 there's only a single clock dedicated to display - * (pll_d_out0), whereas later generations have a second one that can - * be used to independently drive a second output (pll_d2_out0). - * - * As a way to support multiple outputs on Tegra20 as well, pll_p is - * typically used as the parent clock for the display controllers. - * But this comes at a cost: pll_p is the parent of several other - * peripherals, so its frequency shouldn't change out of the blue. - * - * The best we can do at this point is to use the shift clock divider - * and hope that the desired frequency can be matched (or at least - * matched sufficiently close that the panel will still work). - */ - div = ((clk_get_rate(rgb->clk) * 2) / pclk) - 2; - - err = tegra_dc_setup_clock(rgb->dc, rgb->clk_parent, pclk, div); - if (err < 0) { - dev_err(output->dev, "failed to setup DC clock: %d\n", err); - return false; - } - - return true; -} - static void tegra_rgb_encoder_prepare(struct drm_encoder *encoder) { } @@ -278,7 +241,6 @@ tegra_rgb_encoder_atomic_check(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs tegra_rgb_encoder_helper_funcs = { .dpms = tegra_rgb_encoder_dpms, - .mode_fixup = tegra_rgb_encoder_mode_fixup, .prepare = tegra_rgb_encoder_prepare, .commit = tegra_rgb_encoder_commit, .mode_set = tegra_rgb_encoder_mode_set, diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 7463ea02a083..e813df71e30c 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -781,32 +781,6 @@ static void tegra_sor_encoder_dpms(struct drm_encoder *encoder, int mode) { } -static bool tegra_sor_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted) -{ - struct tegra_output *output = encoder_to_output(encoder); - struct tegra_dc *dc = to_tegra_dc(encoder->crtc); - unsigned long pclk = mode->clock * 1000; - struct tegra_sor *sor = to_sor(output); - int err; - - err = tegra_dc_setup_clock(dc, sor->clk_parent, pclk, 0); - if (err < 0) { - dev_err(output->dev, "failed to setup DC clock: %d\n", err); - return false; - } - - err = clk_set_rate(sor->clk_parent, pclk); - if (err < 0) { - dev_err(output->dev, "failed to set clock rate to %lu Hz\n", - pclk); - return false; - } - - return true; -} - static void tegra_sor_encoder_prepare(struct drm_encoder *encoder) { } @@ -1343,7 +1317,6 @@ tegra_sor_encoder_atomic_check(struct drm_encoder *encoder, static const struct drm_encoder_helper_funcs tegra_sor_encoder_helper_funcs = { .dpms = tegra_sor_encoder_dpms, - .mode_fixup = tegra_sor_encoder_mode_fixup, .prepare = tegra_sor_encoder_prepare, .commit = tegra_sor_encoder_commit, .mode_set = tegra_sor_encoder_mode_set, -- cgit v1.2.3 From 47802b09a9c2d1f8a562c7fae2f61a8923ac8d06 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 26 Nov 2014 12:28:39 +0100 Subject: drm/tegra: Track active planes in CRTC state Wrap struct drm_crtc_state in a driver-specific structure and add the planes field which keeps track of which planes are updated or disabled during a modeset. This allows atomic updates of the the display engine at ->atomic_flush() time. v2: open-code getting the state of the CRTC that the plane is being attached to (Daniel Vetter) Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 72 ++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 28 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 2f9af13905da..4073a7b5bf26 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -47,6 +47,8 @@ struct tegra_dc_state { struct clk *clk; unsigned long pclk; unsigned int div; + + u32 planes; }; static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) @@ -57,20 +59,6 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) return NULL; } -static void tegra_dc_window_commit(struct tegra_dc *dc, unsigned int index) -{ - u32 value = WIN_A_ACT_REQ << index; - - tegra_dc_writel(dc, value << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL); -} - -static void tegra_dc_cursor_commit(struct tegra_dc *dc) -{ - tegra_dc_writel(dc, CURSOR_ACT_REQ << 8, DC_CMD_STATE_CONTROL); - tegra_dc_writel(dc, CURSOR_ACT_REQ, DC_CMD_STATE_CONTROL); -} - /* * Reads the active copy of a register. This takes the dc->lock spinlock to * prevent races with the VBLANK processing which also needs access to the @@ -388,8 +376,6 @@ static void tegra_dc_setup_window(struct tegra_dc *dc, unsigned int index, break; } - tegra_dc_window_commit(dc, index); - spin_unlock_irqrestore(&dc->lock, flags); } @@ -432,9 +418,28 @@ static void tegra_plane_cleanup_fb(struct drm_plane *plane, { } +static int tegra_plane_state_add(struct tegra_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct tegra_dc_state *tegra; + + /* Propagate errors from allocation or locking failures. */ + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + tegra = to_dc_state(crtc_state); + + tegra->planes |= WIN_A_ACT_REQ << plane->index; + + return 0; +} + static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { + struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); struct tegra_bo_tiling tiling; int err; @@ -465,6 +470,10 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, } } + err = tegra_plane_state_add(tegra, state); + if (err < 0) + return err; + return 0; } @@ -531,8 +540,6 @@ static void tegra_plane_atomic_disable(struct drm_plane *plane, value &= ~WIN_ENABLE; tegra_dc_writel(dc, value, DC_WIN_WIN_OPTIONS); - tegra_dc_window_commit(dc, p->index); - spin_unlock_irqrestore(&dc->lock, flags); } @@ -592,6 +599,9 @@ static const u32 tegra_cursor_plane_formats[] = { static int tegra_cursor_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { + struct tegra_plane *tegra = to_tegra_plane(plane); + int err; + /* no need for further checks if the plane is being disabled */ if (!state->crtc) return 0; @@ -609,6 +619,10 @@ static int tegra_cursor_atomic_check(struct drm_plane *plane, state->crtc_w != 128 && state->crtc_w != 256) return -EINVAL; + err = tegra_plane_state_add(tegra, state); + if (err < 0) + return err; + return 0; } @@ -673,9 +687,6 @@ static void tegra_cursor_atomic_update(struct drm_plane *plane, value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff); tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION); - /* apply changes */ - tegra_dc_cursor_commit(dc); - tegra_dc_commit(dc); } static void tegra_cursor_atomic_disable(struct drm_plane *plane, @@ -693,9 +704,6 @@ static void tegra_cursor_atomic_disable(struct drm_plane *plane, value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value &= ~CURSOR_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - - tegra_dc_cursor_commit(dc); - tegra_dc_commit(dc); } static const struct drm_plane_funcs tegra_cursor_plane_funcs = { @@ -727,6 +735,13 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm, if (!plane) return ERR_PTR(-ENOMEM); + /* + * We'll treat the cursor as an overlay plane with index 6 here so + * that the update and activation request bits in DC_CMD_STATE_CONTROL + * match up. + */ + plane->index = 6; + num_formats = ARRAY_SIZE(tegra_cursor_plane_formats); formats = tegra_cursor_plane_formats; @@ -1022,7 +1037,6 @@ static void tegra_crtc_disable(struct drm_crtc *crtc) } drm_crtc_vblank_off(crtc); - tegra_dc_commit(dc); } static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc, @@ -1195,10 +1209,7 @@ static void tegra_crtc_prepare(struct drm_crtc *crtc) static void tegra_crtc_commit(struct drm_crtc *crtc) { - struct tegra_dc *dc = to_tegra_dc(crtc); - drm_crtc_vblank_on(crtc); - tegra_dc_commit(dc); } static int tegra_crtc_atomic_check(struct drm_crtc *crtc, @@ -1223,6 +1234,11 @@ static void tegra_crtc_atomic_begin(struct drm_crtc *crtc) static void tegra_crtc_atomic_flush(struct drm_crtc *crtc) { + struct tegra_dc_state *state = to_dc_state(crtc->state); + struct tegra_dc *dc = to_tegra_dc(crtc); + + tegra_dc_writel(dc, state->planes << 8, DC_CMD_STATE_CONTROL); + tegra_dc_writel(dc, state->planes, DC_CMD_STATE_CONTROL); } static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = { -- cgit v1.2.3 From 8f604f8c4dd2d5383f567856450ba12764061c12 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 28 Nov 2014 13:14:55 +0100 Subject: drm/tegra: Track tiling and format in plane state Tracking these in the plane state allows them to be computed in the ->atomic_check() callback and reused when applying the configuration in ->atomic_update(). Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 127 ++++++++++++++++++++++++++++++++++---------- drivers/gpu/drm/tegra/drm.h | 4 +- 2 files changed, 101 insertions(+), 30 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4073a7b5bf26..0cceabd11798 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -59,6 +59,23 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) return NULL; } +struct tegra_plane_state { + struct drm_plane_state base; + + struct tegra_bo_tiling tiling; + u32 format; + u32 swap; +}; + +static inline struct tegra_plane_state * +to_tegra_plane_state(struct drm_plane_state *state) +{ + if (state) + return container_of(state, struct tegra_plane_state, base); + + return NULL; +} + /* * Reads the active copy of a register. This takes the dc->lock spinlock to * prevent races with the VBLANK processing which also needs access to the @@ -97,43 +114,49 @@ void tegra_dc_commit(struct tegra_dc *dc) tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); } -static unsigned int tegra_dc_format(uint32_t format, uint32_t *swap) +static int tegra_dc_format(u32 fourcc, u32 *format, u32 *swap) { /* assume no swapping of fetched data */ if (swap) *swap = BYTE_SWAP_NOSWAP; - switch (format) { + switch (fourcc) { case DRM_FORMAT_XBGR8888: - return WIN_COLOR_DEPTH_R8G8B8A8; + *format = WIN_COLOR_DEPTH_R8G8B8A8; + break; case DRM_FORMAT_XRGB8888: - return WIN_COLOR_DEPTH_B8G8R8A8; + *format = WIN_COLOR_DEPTH_B8G8R8A8; + break; case DRM_FORMAT_RGB565: - return WIN_COLOR_DEPTH_B5G6R5; + *format = WIN_COLOR_DEPTH_B5G6R5; + break; case DRM_FORMAT_UYVY: - return WIN_COLOR_DEPTH_YCbCr422; + *format = WIN_COLOR_DEPTH_YCbCr422; + break; case DRM_FORMAT_YUYV: if (swap) *swap = BYTE_SWAP_SWAP2; - return WIN_COLOR_DEPTH_YCbCr422; + *format = WIN_COLOR_DEPTH_YCbCr422; + break; case DRM_FORMAT_YUV420: - return WIN_COLOR_DEPTH_YCbCr420P; + *format = WIN_COLOR_DEPTH_YCbCr420P; + break; case DRM_FORMAT_YUV422: - return WIN_COLOR_DEPTH_YCbCr422P; + *format = WIN_COLOR_DEPTH_YCbCr422P; + break; default: - break; + return -EINVAL; } - WARN(1, "unsupported pixel format %u, using default\n", format); - return WIN_COLOR_DEPTH_B8G8R8A8; + return 0; } static bool tegra_dc_format_is_yuv(unsigned int format, bool *planar) @@ -398,13 +421,54 @@ static void tegra_primary_plane_destroy(struct drm_plane *plane) tegra_plane_destroy(plane); } +static void tegra_plane_reset(struct drm_plane *plane) +{ + struct tegra_plane_state *state; + + if (plane->state && plane->state->fb) + drm_framebuffer_unreference(plane->state->fb); + + kfree(plane->state); + plane->state = NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (state) { + plane->state = &state->base; + plane->state->plane = plane; + } +} + +static struct drm_plane_state *tegra_plane_atomic_duplicate_state(struct drm_plane *plane) +{ + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); + struct tegra_plane_state *copy; + + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); + if (!copy) + return NULL; + + if (copy->base.fb) + drm_framebuffer_reference(copy->base.fb); + + return ©->base; +} + +static void tegra_plane_atomic_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_unreference(state->fb); + + kfree(state); +} + static const struct drm_plane_funcs tegra_primary_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_primary_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .reset = tegra_plane_reset, + .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, + .atomic_destroy_state = tegra_plane_atomic_destroy_state, }; static int tegra_plane_prepare_fb(struct drm_plane *plane, @@ -439,20 +503,26 @@ static int tegra_plane_state_add(struct tegra_plane *plane, static int tegra_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state) { + struct tegra_plane_state *plane_state = to_tegra_plane_state(state); + struct tegra_bo_tiling *tiling = &plane_state->tiling; struct tegra_plane *tegra = to_tegra_plane(plane); struct tegra_dc *dc = to_tegra_dc(state->crtc); - struct tegra_bo_tiling tiling; int err; /* no need for further checks if the plane is being disabled */ if (!state->crtc) return 0; - err = tegra_fb_get_tiling(state->fb, &tiling); + err = tegra_dc_format(state->fb->pixel_format, &plane_state->format, + &plane_state->swap); + if (err < 0) + return err; + + err = tegra_fb_get_tiling(state->fb, tiling); if (err < 0) return err; - if (tiling.mode == TEGRA_BO_TILING_MODE_BLOCK && + if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && !dc->soc->supports_block_linear) { DRM_ERROR("hardware doesn't support block linear mode\n"); return -EINVAL; @@ -480,12 +550,12 @@ static int tegra_plane_atomic_check(struct drm_plane *plane, static void tegra_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); struct drm_framebuffer *fb = plane->state->fb; struct tegra_plane *p = to_tegra_plane(plane); struct tegra_dc_window window; unsigned int i; - int err; /* rien ne va plus */ if (!plane->state->crtc || !plane->state->fb) @@ -500,12 +570,13 @@ static void tegra_plane_atomic_update(struct drm_plane *plane, window.dst.y = plane->state->crtc_y; window.dst.w = plane->state->crtc_w; window.dst.h = plane->state->crtc_h; - window.format = tegra_dc_format(fb->pixel_format, &window.swap); window.bits_per_pixel = fb->bits_per_pixel; window.bottom_up = tegra_fb_is_bottom_up(fb); - err = tegra_fb_get_tiling(fb, &window.tiling); - WARN_ON(err < 0); + /* copy from state */ + window.tiling = state->tiling; + window.format = state->format; + window.swap = state->swap; for (i = 0; i < drm_format_num_planes(fb->pixel_format); i++) { struct tegra_bo *bo = tegra_fb_get_plane(fb, i); @@ -710,9 +781,9 @@ static const struct drm_plane_funcs tegra_cursor_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .reset = tegra_plane_reset, + .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, + .atomic_destroy_state = tegra_plane_atomic_destroy_state, }; static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = { @@ -767,9 +838,9 @@ static const struct drm_plane_funcs tegra_overlay_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = tegra_overlay_plane_destroy, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + .reset = tegra_plane_reset, + .atomic_duplicate_state = tegra_plane_atomic_duplicate_state, + .atomic_destroy_state = tegra_plane_atomic_destroy_state, }; static const uint32_t tegra_overlay_plane_formats[] = { diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h index b1c7027b26e7..8cb2dfeaa957 100644 --- a/drivers/gpu/drm/tegra/drm.h +++ b/drivers/gpu/drm/tegra/drm.h @@ -170,13 +170,13 @@ struct tegra_dc_window { unsigned int h; } dst; unsigned int bits_per_pixel; - unsigned int format; - unsigned int swap; unsigned int stride[2]; unsigned long base[3]; bool bottom_up; struct tegra_bo_tiling tiling; + u32 format; + u32 swap; }; /* from dc.c */ -- cgit v1.2.3 From 666cb873328b5075eb511662858bab02d084ff64 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 8 Dec 2014 16:32:47 +0100 Subject: drm/tegra: dc: Unify enabling the display controller Previously output drivers would enable continuous display mode and power up the display controller at various points during the initialization. This is suboptimal because it accesses display controller registers in output drivers and duplicates a bit of code. Move this code into the display controller driver and enable the display controller as the final step of the ->mode_set_nofb() implementation. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 12 ++++++++++++ drivers/gpu/drm/tegra/dsi.c | 10 ---------- drivers/gpu/drm/tegra/hdmi.c | 10 ---------- drivers/gpu/drm/tegra/rgb.c | 11 +---------- drivers/gpu/drm/tegra/sor.c | 25 +++---------------------- 5 files changed, 16 insertions(+), 52 deletions(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 0cceabd11798..3aaa84ae2681 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1236,6 +1236,18 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc) value &= ~INTERLACE_ENABLE; tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL); } + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); + value &= ~DISP_CTRL_MODE_MASK; + value |= DISP_CTRL_MODE_C_DISPLAY; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); + + value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); + value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | + PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; + tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); + + tegra_dc_commit(dc); } static void tegra_crtc_prepare(struct drm_crtc *crtc) diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 6875885a2dca..ed970f622903 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -824,16 +824,6 @@ static void tegra_dsi_encoder_mode_set(struct drm_encoder *encoder, value |= DSI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_commit(dc); /* enable DSI controller */ diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 07771956cc94..7e06657ae58b 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1022,16 +1022,6 @@ static void tegra_hdmi_encoder_mode_set(struct drm_encoder *encoder, value |= HDMI_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_commit(dc); /* TODO: add HDCP support */ diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 0c8b458b2364..7cd833f5b5b5 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -168,16 +168,6 @@ static void tegra_rgb_encoder_mode_set(struct drm_encoder *encoder, value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE; tegra_dc_writel(rgb->dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS); - value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_COMMAND); - value &= ~DISP_CTRL_MODE_MASK; - value |= DISP_CTRL_MODE_C_DISPLAY; - tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(rgb->dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(rgb->dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - tegra_dc_commit(rgb->dc); if (output->panel) @@ -193,6 +183,7 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder) drm_panel_disable(output->panel); tegra_dc_write_regs(rgb->dc, rgb_disable, ARRAY_SIZE(rgb_disable)); + tegra_dc_commit(rgb->dc); if (output->panel) drm_panel_unprepare(output->panel); diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index e813df71e30c..2afe478ded3b 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -261,17 +261,8 @@ static int tegra_sor_attach(struct tegra_sor *sor) static int tegra_sor_wakeup(struct tegra_sor *sor) { - struct tegra_dc *dc = to_tegra_dc(sor->output.encoder.crtc); unsigned long value, timeout; - /* enable display controller outputs */ - value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL); - value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | - PW4_ENABLE | PM0_ENABLE | PM1_ENABLE; - tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL); - - tegra_dc_commit(dc); - timeout = jiffies + msecs_to_jiffies(250); /* wait for head to wake up */ @@ -1112,18 +1103,6 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder, goto unlock; } - /* start display controller in continuous mode */ - value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); - value |= WRITE_MUX; - tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); - - tegra_dc_writel(dc, VSYNC_H_POSITION(1), DC_DISP_DISP_TIMING_OPTIONS); - tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); - - value = tegra_dc_readl(dc, DC_CMD_STATE_ACCESS); - value &= ~WRITE_MUX; - tegra_dc_writel(dc, value, DC_CMD_STATE_ACCESS); - /* * configure panel (24bpp, vsync-, hsync-, DP-A protocol, complete * raster, associate with display controller) @@ -1198,11 +1177,13 @@ static void tegra_sor_encoder_mode_set(struct drm_encoder *encoder, goto unlock; } + tegra_sor_update(sor); + value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); value |= SOR_ENABLE; tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS); - tegra_sor_update(sor); + tegra_dc_commit(dc); err = tegra_sor_attach(sor); if (err < 0) { -- cgit v1.2.3 From 359ae687dbd16c76f5519a5750444f5a3b00ad18 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 18 Dec 2014 17:15:25 +0100 Subject: drm/tegra: Add minimal power management For now only disable the KMS hotplug polling helper logic upon suspend and re-enable it on resume. Reviewed-by: Sean Paul Reviewed-by: Mark Zhang Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 0edfb5e0b374..5bccb20889f7 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -996,6 +996,30 @@ static int host1x_drm_remove(struct host1x_device *dev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int host1x_drm_suspend(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + + drm_kms_helper_poll_disable(drm); + + return 0; +} + +static int host1x_drm_resume(struct device *dev) +{ + struct drm_device *drm = dev_get_drvdata(dev); + + drm_kms_helper_poll_enable(drm); + + return 0; +} +#endif + +static const struct dev_pm_ops host1x_drm_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(host1x_drm_suspend, host1x_drm_resume) +}; + static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra20-dc", }, { .compatible = "nvidia,tegra20-hdmi", }, @@ -1017,6 +1041,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { static struct host1x_driver host1x_drm_driver = { .driver = { .name = "drm", + .pm = &host1x_drm_pm_ops, }, .probe = host1x_drm_probe, .remove = host1x_drm_remove, -- cgit v1.2.3 From 31f40f86526b71009973854c1dfe799ee70f7588 Mon Sep 17 00:00:00 2001 From: David Ung Date: Tue, 20 Jan 2015 18:37:35 -0800 Subject: drm/tegra: Use correct relocation target offsets When copying a relocation from userspace, copy the correct target offset. Signed-off-by: David Ung Fixes: 961e3beae3b2 ("drm/tegra: Make job submission 64-bit safe") Cc: stable@vger.kernel.org [treding@nvidia.com: provide a better commit message] Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/tegra') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 5bccb20889f7..7dd328d77996 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -293,7 +293,7 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, if (err < 0) return err; - err = get_user(dest->target.offset, &src->cmdbuf.offset); + err = get_user(dest->target.offset, &src->target.offset); if (err < 0) return err; -- cgit v1.2.3