diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 8e7fa4d..d1cc2f6 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -199,3 +199,5 @@ source "drivers/gpu/drm/msm/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" source "drivers/gpu/drm/panel/Kconfig" + +source "drivers/gpu/drm/bridge/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 292a79d..48e38ba 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -13,7 +13,8 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ drm_trace_points.o drm_global.o drm_prime.o \ - drm_rect.o drm_vma_manager.o drm_flip_work.o + drm_rect.o drm_vma_manager.o drm_flip_work.o \ + drm_plane_helper.o drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o @@ -22,7 +23,7 @@ drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-usb-y := drm_usb.o -drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o +drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o @@ -63,3 +64,4 @@ obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-y += i2c/ obj-y += panel/ +obj-y += bridge/ diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig new file mode 100644 index 0000000..884923f --- /dev/null +++ b/drivers/gpu/drm/bridge/Kconfig @@ -0,0 +1,5 @@ +config DRM_PTN3460 + tristate "PTN3460 DP/LVDS bridge" + depends on DRM + select DRM_KMS_HELPER + ---help--- diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile new file mode 100644 index 0000000..b4733e1 --- /dev/null +++ b/drivers/gpu/drm/bridge/Makefile @@ -0,0 +1,3 @@ +ccflags-y := -Iinclude/drm + +obj-$(CONFIG_DRM_PTN3460) += ptn3460.o diff --git a/drivers/gpu/drm/bridge/ptn3460.c b/drivers/gpu/drm/bridge/ptn3460.c new file mode 100644 index 0000000..b171901 --- /dev/null +++ b/drivers/gpu/drm/bridge/ptn3460.c @@ -0,0 +1,350 @@ +/* + * NXP PTN3460 DP/LVDS bridge driver + * + * Copyright (C) 2013 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "drmP.h" +#include "drm_edid.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "bridge/ptn3460.h" + +#define PTN3460_EDID_ADDR 0x0 +#define PTN3460_EDID_EMULATION_ADDR 0x84 +#define PTN3460_EDID_ENABLE_EMULATION 0 +#define PTN3460_EDID_EMULATION_SELECTION 1 +#define PTN3460_EDID_SRAM_LOAD_ADDR 0x85 + +struct ptn3460_bridge { + struct drm_connector connector; + struct i2c_client *client; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + struct edid *edid; + int gpio_pd_n; + int gpio_rst_n; + u32 edid_emulation; + bool enabled; +}; + +static int ptn3460_read_bytes(struct ptn3460_bridge *ptn_bridge, char addr, + u8 *buf, int len) +{ + int ret; + + ret = i2c_master_send(ptn_bridge->client, &addr, 1); + if (ret <= 0) { + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); + return ret; + } + + ret = i2c_master_recv(ptn_bridge->client, buf, len); + if (ret <= 0) { + DRM_ERROR("Failed to recv i2c data, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ptn3460_write_byte(struct ptn3460_bridge *ptn_bridge, char addr, + char val) +{ + int ret; + char buf[2]; + + buf[0] = addr; + buf[1] = val; + + ret = i2c_master_send(ptn_bridge->client, buf, ARRAY_SIZE(buf)); + if (ret <= 0) { + DRM_ERROR("Failed to send i2c command, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static int ptn3460_select_edid(struct ptn3460_bridge *ptn_bridge) +{ + int ret; + char val; + + /* Load the selected edid into SRAM (accessed at PTN3460_EDID_ADDR) */ + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_SRAM_LOAD_ADDR, + ptn_bridge->edid_emulation); + if (ret) { + DRM_ERROR("Failed to transfer edid to sram, ret=%d\n", ret); + return ret; + } + + /* Enable EDID emulation and select the desired EDID */ + val = 1 << PTN3460_EDID_ENABLE_EMULATION | + ptn_bridge->edid_emulation << PTN3460_EDID_EMULATION_SELECTION; + + ret = ptn3460_write_byte(ptn_bridge, PTN3460_EDID_EMULATION_ADDR, val); + if (ret) { + DRM_ERROR("Failed to write edid value, ret=%d\n", ret); + return ret; + } + + return 0; +} + +static void ptn3460_pre_enable(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + int ret; + + if (ptn_bridge->enabled) + return; + + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) + gpio_set_value(ptn_bridge->gpio_pd_n, 1); + + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { + gpio_set_value(ptn_bridge->gpio_rst_n, 0); + udelay(10); + gpio_set_value(ptn_bridge->gpio_rst_n, 1); + } + + /* + * There's a bug in the PTN chip where it falsely asserts hotplug before + * it is fully functional. We're forced to wait for the maximum start up + * time specified in the chip's datasheet to make sure we're really up. + */ + msleep(90); + + ret = ptn3460_select_edid(ptn_bridge); + if (ret) + DRM_ERROR("Select edid failed ret=%d\n", ret); + + ptn_bridge->enabled = true; +} + +static void ptn3460_enable(struct drm_bridge *bridge) +{ +} + +static void ptn3460_disable(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + + if (!ptn_bridge->enabled) + return; + + ptn_bridge->enabled = false; + + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) + gpio_set_value(ptn_bridge->gpio_rst_n, 1); + + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) + gpio_set_value(ptn_bridge->gpio_pd_n, 0); +} + +static void ptn3460_post_disable(struct drm_bridge *bridge) +{ +} + +void ptn3460_bridge_destroy(struct drm_bridge *bridge) +{ + struct ptn3460_bridge *ptn_bridge = bridge->driver_private; + + drm_bridge_cleanup(bridge); + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) + gpio_free(ptn_bridge->gpio_pd_n); + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) + gpio_free(ptn_bridge->gpio_rst_n); + /* Nothing else to free, we've got devm allocated memory */ +} + +struct drm_bridge_funcs ptn3460_bridge_funcs = { + .pre_enable = ptn3460_pre_enable, + .enable = ptn3460_enable, + .disable = ptn3460_disable, + .post_disable = ptn3460_post_disable, + .destroy = ptn3460_bridge_destroy, +}; + +int ptn3460_get_modes(struct drm_connector *connector) +{ + struct ptn3460_bridge *ptn_bridge; + u8 *edid; + int ret, num_modes; + bool power_off; + + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + + if (ptn_bridge->edid) + return drm_add_edid_modes(connector, ptn_bridge->edid); + + power_off = !ptn_bridge->enabled; + ptn3460_pre_enable(ptn_bridge->bridge); + + edid = kmalloc(EDID_LENGTH, GFP_KERNEL); + if (!edid) { + DRM_ERROR("Failed to allocate edid\n"); + return 0; + } + + ret = ptn3460_read_bytes(ptn_bridge, PTN3460_EDID_ADDR, edid, + EDID_LENGTH); + if (ret) { + kfree(edid); + num_modes = 0; + goto out; + } + + ptn_bridge->edid = (struct edid *)edid; + drm_mode_connector_update_edid_property(connector, ptn_bridge->edid); + + num_modes = drm_add_edid_modes(connector, ptn_bridge->edid); + +out: + if (power_off) + ptn3460_disable(ptn_bridge->bridge); + + return num_modes; +} + +static int ptn3460_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +struct drm_encoder *ptn3460_best_encoder(struct drm_connector *connector) +{ + struct ptn3460_bridge *ptn_bridge; + + ptn_bridge = container_of(connector, struct ptn3460_bridge, connector); + + return ptn_bridge->encoder; +} + +struct drm_connector_helper_funcs ptn3460_connector_helper_funcs = { + .get_modes = ptn3460_get_modes, + .mode_valid = ptn3460_mode_valid, + .best_encoder = ptn3460_best_encoder, +}; + +enum drm_connector_status ptn3460_detect(struct drm_connector *connector, + bool force) +{ + return connector_status_connected; +} + +void ptn3460_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +struct drm_connector_funcs ptn3460_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = ptn3460_detect, + .destroy = ptn3460_connector_destroy, +}; + +int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, + struct i2c_client *client, struct device_node *node) +{ + int ret; + struct drm_bridge *bridge; + struct ptn3460_bridge *ptn_bridge; + + bridge = devm_kzalloc(dev->dev, sizeof(*bridge), GFP_KERNEL); + if (!bridge) { + DRM_ERROR("Failed to allocate drm bridge\n"); + return -ENOMEM; + } + + ptn_bridge = devm_kzalloc(dev->dev, sizeof(*ptn_bridge), GFP_KERNEL); + if (!ptn_bridge) { + DRM_ERROR("Failed to allocate ptn bridge\n"); + return -ENOMEM; + } + + ptn_bridge->client = client; + ptn_bridge->encoder = encoder; + ptn_bridge->bridge = bridge; + ptn_bridge->gpio_pd_n = of_get_named_gpio(node, "powerdown-gpio", 0); + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) { + ret = gpio_request_one(ptn_bridge->gpio_pd_n, + GPIOF_OUT_INIT_HIGH, "PTN3460_PD_N"); + if (ret) { + DRM_ERROR("Request powerdown-gpio failed (%d)\n", ret); + return ret; + } + } + + ptn_bridge->gpio_rst_n = of_get_named_gpio(node, "reset-gpio", 0); + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) { + /* + * Request the reset pin low to avoid the bridge being + * initialized prematurely + */ + ret = gpio_request_one(ptn_bridge->gpio_rst_n, + GPIOF_OUT_INIT_LOW, "PTN3460_RST_N"); + if (ret) { + DRM_ERROR("Request reset-gpio failed (%d)\n", ret); + gpio_free(ptn_bridge->gpio_pd_n); + return ret; + } + } + + ret = of_property_read_u32(node, "edid-emulation", + &ptn_bridge->edid_emulation); + if (ret) { + DRM_ERROR("Can't read edid emulation value\n"); + goto err; + } + + ret = drm_bridge_init(dev, bridge, &ptn3460_bridge_funcs); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + goto err; + } + + bridge->driver_private = ptn_bridge; + encoder->bridge = bridge; + + ret = drm_connector_init(dev, &ptn_bridge->connector, + &ptn3460_connector_funcs, DRM_MODE_CONNECTOR_LVDS); + if (ret) { + DRM_ERROR("Failed to initialize connector with drm\n"); + goto err; + } + drm_connector_helper_add(&ptn_bridge->connector, + &ptn3460_connector_helper_funcs); + drm_sysfs_connector_add(&ptn_bridge->connector); + drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); + + return 0; + +err: + if (gpio_is_valid(ptn_bridge->gpio_pd_n)) + gpio_free(ptn_bridge->gpio_pd_n); + if (gpio_is_valid(ptn_bridge->gpio_rst_n)) + gpio_free(ptn_bridge->gpio_rst_n); + return ret; +} +EXPORT_SYMBOL(ptn3460_init); diff --git a/drivers/gpu/drm/drm_bufs.c b/drivers/gpu/drm/drm_bufs.c index edec31f..ef7f019 100644 --- a/drivers/gpu/drm/drm_bufs.c +++ b/drivers/gpu/drm/drm_bufs.c @@ -656,13 +656,13 @@ int drm_addbufs_agp(struct drm_device * dev, struct drm_buf_desc * request) DRM_DEBUG("zone invalid\n"); return -EINVAL; } - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -805,13 +805,13 @@ int drm_addbufs_pci(struct drm_device * dev, struct drm_buf_desc * request) page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; total = PAGE_SIZE << page_order; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -1015,13 +1015,13 @@ static int drm_addbufs_sg(struct drm_device * dev, struct drm_buf_desc * request if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (dev->buf_use) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } atomic_inc(&dev->buf_alloc); - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); mutex_lock(&dev->struct_mutex); entry = &dma->bufs[order]; @@ -1175,7 +1175,7 @@ int drm_addbufs(struct drm_device *dev, void *data, * \param arg pointer to a drm_buf_info structure. * \return zero on success or a negative number on failure. * - * Increments drm_device::buf_use while holding the drm_device::count_lock + * Increments drm_device::buf_use while holding the drm_device::buf_lock * lock, preventing of allocating more buffers after this call. Information * about each requested buffer is then copied into user space. */ @@ -1196,13 +1196,13 @@ int drm_infobufs(struct drm_device *dev, void *data, if (!dma) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } ++dev->buf_use; /* Can't allocate more after this call */ - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); for (i = 0, count = 0; i < DRM_MAX_ORDER + 1; i++) { if (dma->bufs[i].buf_count) @@ -1381,13 +1381,13 @@ int drm_mapbufs(struct drm_device *dev, void *data, if (!dma) return -EINVAL; - spin_lock(&dev->count_lock); + spin_lock(&dev->buf_lock); if (atomic_read(&dev->buf_alloc)) { - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); return -EBUSY; } dev->buf_use++; /* Can't allocate more after this call */ - spin_unlock(&dev->count_lock); + spin_unlock(&dev->buf_lock); if (request->count >= dma->buf_count) { if ((dev->agp && (dma->flags & _DRM_DMA_USE_AGP)) diff --git a/drivers/gpu/drm/drm_cache.c b/drivers/gpu/drm/drm_cache.c index bb8f580..ae251b8 100644 --- a/drivers/gpu/drm/drm_cache.c +++ b/drivers/gpu/drm/drm_cache.c @@ -32,6 +32,12 @@ #include #if defined(CONFIG_X86) + +/* + * clflushopt is an unordered instruction which needs fencing with mfence or + * sfence to avoid ordering issues. For drm_clflush_page this fencing happens + * in the caller. + */ static void drm_clflush_page(struct page *page) { @@ -44,7 +50,7 @@ drm_clflush_page(struct page *page) page_virtual = kmap_atomic(page); for (i = 0; i < PAGE_SIZE; i += size) - clflush(page_virtual + i); + clflushopt(page_virtual + i); kunmap_atomic(page_virtual); } @@ -125,15 +131,15 @@ drm_clflush_sg(struct sg_table *st) EXPORT_SYMBOL(drm_clflush_sg); void -drm_clflush_virt_range(char *addr, unsigned long length) +drm_clflush_virt_range(void *addr, unsigned long length) { #if defined(CONFIG_X86) if (cpu_has_clflush) { - char *end = addr + length; + void *end = addr + length; mb(); for (; addr < end; addr += boot_cpu_data.x86_clflush_size) clflush(addr); - clflush(end - 1); + clflushopt(end - 1); mb(); return; } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3b7d32d..d8b7099 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -38,12 +38,15 @@ #include #include +#include "drm_crtc_internal.h" + /** * drm_modeset_lock_all - take all modeset locks * @dev: drm device * * This function takes all modeset locks, suitable where a more fine-grained - * scheme isn't (yet) implemented. + * scheme isn't (yet) implemented. Locks must be dropped with + * drm_modeset_unlock_all. */ void drm_modeset_lock_all(struct drm_device *dev) { @@ -59,6 +62,8 @@ EXPORT_SYMBOL(drm_modeset_lock_all); /** * drm_modeset_unlock_all - drop all modeset locks * @dev: device + * + * This function drop all modeset locks taken by drm_modeset_lock_all. */ void drm_modeset_unlock_all(struct drm_device *dev) { @@ -74,6 +79,8 @@ EXPORT_SYMBOL(drm_modeset_unlock_all); /** * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked * @dev: device + * + * Useful as a debug assert. */ void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) { @@ -114,6 +121,13 @@ static const struct drm_prop_enum_list drm_dpms_enum_list[] = DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) +static const struct drm_prop_enum_list drm_plane_type_enum_list[] = +{ + { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, + { DRM_PLANE_TYPE_PRIMARY, "Primary" }, + { DRM_PLANE_TYPE_CURSOR, "Cursor" }, +}; + /* * Optional properties */ @@ -215,6 +229,16 @@ static const struct drm_prop_enum_list drm_encoder_enum_list[] = { DRM_MODE_ENCODER_DSI, "DSI" }, }; +static const struct drm_prop_enum_list drm_subpixel_enum_list[] = +{ + { SubPixelUnknown, "Unknown" }, + { SubPixelHorizontalRGB, "Horizontal RGB" }, + { SubPixelHorizontalBGR, "Horizontal BGR" }, + { SubPixelVerticalRGB, "Vertical RGB" }, + { SubPixelVerticalBGR, "Vertical BGR" }, + { SubPixelNone, "None" }, +}; + void drm_connector_ida_init(void) { int i; @@ -231,6 +255,15 @@ void drm_connector_ida_destroy(void) ida_destroy(&drm_connector_enum_list[i].ida); } +/** + * drm_get_encoder_name - return a string for encoder + * @encoder: encoder to compute name of + * + * Note that the buffer used by this function is globally shared and owned by + * the function itself. + * + * FIXME: This isn't really multithreading safe. + */ const char *drm_get_encoder_name(const struct drm_encoder *encoder) { static char buf[32]; @@ -242,6 +275,15 @@ const char *drm_get_encoder_name(const struct drm_encoder *encoder) } EXPORT_SYMBOL(drm_get_encoder_name); +/** + * drm_get_connector_name - return a string for connector + * @connector: connector to compute name of + * + * Note that the buffer used by this function is globally shared and owned by + * the function itself. + * + * FIXME: This isn't really multithreading safe. + */ const char *drm_get_connector_name(const struct drm_connector *connector) { static char buf[32]; @@ -253,6 +295,13 @@ const char *drm_get_connector_name(const struct drm_connector *connector) } EXPORT_SYMBOL(drm_get_connector_name); +/** + * drm_get_connector_status_name - return a string for connector status + * @status: connector status to compute name of + * + * In contrast to the other drm_get_*_name functions this one here returns a + * const pointer and hence is threadsafe. + */ const char *drm_get_connector_status_name(enum drm_connector_status status) { if (status == connector_status_connected) @@ -264,11 +313,33 @@ const char *drm_get_connector_status_name(enum drm_connector_status status) } EXPORT_SYMBOL(drm_get_connector_status_name); +/** + * drm_get_subpixel_order_name - return a string for a given subpixel enum + * @order: enum of subpixel_order + * + * Note you could abuse this and return something out of bounds, but that + * would be a caller error. No unscrubbed user data should make it here. + */ +const char *drm_get_subpixel_order_name(enum subpixel_order order) +{ + return drm_subpixel_enum_list[order].name; +} +EXPORT_SYMBOL(drm_get_subpixel_order_name); + static char printable_char(int c) { return isascii(c) && isprint(c) ? c : '?'; } +/** + * drm_get_format_name - return a string for drm fourcc format + * @format: format to compute name of + * + * Note that the buffer used by this function is globally shared and owned by + * the function itself. + * + * FIXME: This isn't really multithreading safe. + */ const char *drm_get_format_name(uint32_t format) { static char buf[32]; @@ -293,14 +364,16 @@ EXPORT_SYMBOL(drm_get_format_name); * @obj_type: object type * * Create a unique identifier based on @ptr in @dev's identifier space. Used - * for tracking modes, CRTCs and connectors. + * for tracking modes, CRTCs and connectors. Note that despite the _get postfix + * modeset identifiers are _not_ reference counted. Hence don't use this for + * reference counted modeset objects like framebuffers. * - * RETURNS: + * Returns: * New unique (relative to other objects in @dev) integer identifier for the * object. */ -static int drm_mode_object_get(struct drm_device *dev, - struct drm_mode_object *obj, uint32_t obj_type) +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type) { int ret; @@ -324,10 +397,12 @@ static int drm_mode_object_get(struct drm_device *dev, * @dev: DRM device * @object: object to free * - * Free @id from @dev's unique identifier pool. + * Free @id from @dev's unique identifier pool. Note that despite the _get + * postfix modeset identifiers are _not_ reference counted. Hence don't use this + * for reference counted modeset objects like framebuffers. */ -static void drm_mode_object_put(struct drm_device *dev, - struct drm_mode_object *object) +void drm_mode_object_put(struct drm_device *dev, + struct drm_mode_object *object) { mutex_lock(&dev->mode_config.idr_mutex); idr_remove(&dev->mode_config.crtc_idr, object->id); @@ -377,7 +452,7 @@ EXPORT_SYMBOL(drm_mode_object_find); * since all the fb attributes are invariant over its lifetime, no further * locking but only correct reference counting is required. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, @@ -438,7 +513,7 @@ static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, * * If successful, this grabs an additional reference to the framebuffer - * callers need to make sure to eventually unreference the returned framebuffer - * again. + * again, using @drm_framebuffer_unreference. */ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, uint32_t id) @@ -471,6 +546,8 @@ EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt * @fb: framebuffer + * + * This functions increments the fb's refcount. */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { @@ -527,8 +604,9 @@ EXPORT_SYMBOL(drm_framebuffer_unregister_private); * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * - * Cleanup references to a user-created framebuffer. This function is intended - * to be used from the drivers ->destroy callback. + * Cleanup framebuffer. This function is intended to be used from the drivers + * ->destroy callback. It can also be used to clean up driver private + * framebuffers embedded into a larger structure. * * Note that this function does not remove the fb from active usuage - if it is * still used anywhere, hilarity can ensue since userspace could call getfb on @@ -591,7 +669,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) drm_modeset_lock_all(dev); /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) { + if (crtc->primary->fb == fb) { /* should turn off the crtc */ memset(&set, 0, sizeof(struct drm_mode_set)); set.crtc = crtc; @@ -614,18 +692,23 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) EXPORT_SYMBOL(drm_framebuffer_remove); /** - * drm_crtc_init - Initialise a new CRTC object + * drm_crtc_init_with_planes - Initialise a new CRTC object with + * specified primary and cursor planes. * @dev: DRM device * @crtc: CRTC object to init + * @primary: Primary plane for CRTC + * @cursor: Cursor plane for CRTC * @funcs: callbacks for the new CRTC * * Inits a new object created as base part of a driver crtc object. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ -int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, - const struct drm_crtc_funcs *funcs) +int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, + struct drm_plane *primary, + void *cursor, + const struct drm_crtc_funcs *funcs) { int ret; @@ -646,12 +729,16 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; + crtc->primary = primary; + if (primary) + primary->possible_crtcs = 1 << drm_crtc_index(crtc); + out: drm_modeset_unlock_all(dev); return ret; } -EXPORT_SYMBOL(drm_crtc_init); +EXPORT_SYMBOL(drm_crtc_init_with_planes); /** * drm_crtc_cleanup - Clean up the core crtc usage @@ -697,20 +784,6 @@ unsigned int drm_crtc_index(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_index); -/** - * drm_mode_probed_add - add a mode to a connector's probed mode list - * @connector: connector the new mode - * @mode: mode data - * - * Add @mode to @connector's mode list for later use. - */ -void drm_mode_probed_add(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - list_add_tail(&mode->head, &connector->probed_modes); -} -EXPORT_SYMBOL(drm_mode_probed_add); - /* * drm_mode_remove - remove and free a mode * @connector: connector list to modify @@ -735,7 +808,7 @@ static void drm_mode_remove(struct drm_connector *connector, * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ int drm_connector_init(struct drm_device *dev, @@ -813,6 +886,14 @@ void drm_connector_cleanup(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_cleanup); +/** + * drm_connector_unplug_all - unregister connector userspace interfaces + * @dev: drm device + * + * This function unregisters all connector userspace interfaces in sysfs. Should + * be call when the device is disconnected, e.g. from an usb driver's + * ->disconnect callback. + */ void drm_connector_unplug_all(struct drm_device *dev) { struct drm_connector *connector; @@ -824,6 +905,18 @@ void drm_connector_unplug_all(struct drm_device *dev) } EXPORT_SYMBOL(drm_connector_unplug_all); +/** + * drm_bridge_init - initialize a drm transcoder/bridge + * @dev: drm device + * @bridge: transcoder/bridge to set up + * @funcs: bridge function table + * + * Initialises a preallocated bridge. Bridges should be + * subclassed as part of driver connector objects. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, const struct drm_bridge_funcs *funcs) { @@ -847,6 +940,12 @@ int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_bridge_init); +/** + * drm_bridge_cleanup - cleans up an initialised bridge + * @bridge: bridge to cleanup + * + * Cleans up the bridge but doesn't free the object. + */ void drm_bridge_cleanup(struct drm_bridge *bridge) { struct drm_device *dev = bridge->dev; @@ -859,6 +958,19 @@ void drm_bridge_cleanup(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_cleanup); +/** + * drm_encoder_init - Init a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder + * @encoder_type: user visible type of the encoder + * + * Initialises a preallocated encoder. Encoder should be + * subclassed as part of driver encoder objects. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, @@ -886,6 +998,12 @@ int drm_encoder_init(struct drm_device *dev, } EXPORT_SYMBOL(drm_encoder_init); +/** + * drm_encoder_cleanup - cleans up an initialised encoder + * @encoder: encoder to cleanup + * + * Cleans up the encoder but doesn't free the object. + */ void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; @@ -898,25 +1016,25 @@ void drm_encoder_cleanup(struct drm_encoder *encoder) EXPORT_SYMBOL(drm_encoder_cleanup); /** - * drm_plane_init - Initialise a new plane object + * drm_universal_plane_init - Initialize a new universal plane object * @dev: DRM device * @plane: plane object to init * @possible_crtcs: bitmask of possible CRTCs * @funcs: callbacks for the new plane * @formats: array of supported formats (%DRM_FORMAT_*) * @format_count: number of elements in @formats - * @priv: plane is private (hidden from userspace)? + * @type: type of plane (overlay, primary, cursor) * - * Inits a new object created as base part of a driver plane object. + * Initializes a plane object of type @type. * - * RETURNS: + * Returns: * Zero on success, error code on failure. */ -int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, - const struct drm_plane_funcs *funcs, - const uint32_t *formats, uint32_t format_count, - bool priv) +int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, uint32_t format_count, + enum drm_plane_type type) { int ret; @@ -941,23 +1059,53 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); plane->format_count = format_count; plane->possible_crtcs = possible_crtcs; + plane->type = type; - /* private planes are not exposed to userspace, but depending on - * display hardware, might be convenient to allow sharing programming - * for the scanout engine with the crtc implementation. - */ - if (!priv) { - list_add_tail(&plane->head, &dev->mode_config.plane_list); - dev->mode_config.num_plane++; - } else { - INIT_LIST_HEAD(&plane->head); - } + list_add_tail(&plane->head, &dev->mode_config.plane_list); + dev->mode_config.num_total_plane++; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + dev->mode_config.num_overlay_plane++; + + drm_object_attach_property(&plane->base, + dev->mode_config.plane_type_property, + plane->type); out: drm_modeset_unlock_all(dev); return ret; } +EXPORT_SYMBOL(drm_universal_plane_init); + +/** + * drm_plane_init - Initialize a legacy plane + * @dev: DRM device + * @plane: plane object to init + * @possible_crtcs: bitmask of possible CRTCs + * @funcs: callbacks for the new plane + * @formats: array of supported formats (%DRM_FORMAT_*) + * @format_count: number of elements in @formats + * @is_primary: plane type (primary vs overlay) + * + * Legacy API to initialize a DRM plane. + * + * New drivers should call drm_universal_plane_init() instead. + * + * Returns: + * Zero on success, error code on failure. + */ +int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, + unsigned long possible_crtcs, + const struct drm_plane_funcs *funcs, + const uint32_t *formats, uint32_t format_count, + bool is_primary) +{ + enum drm_plane_type type; + + type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; + return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, + formats, format_count, type); +} EXPORT_SYMBOL(drm_plane_init); /** @@ -975,11 +1123,13 @@ void drm_plane_cleanup(struct drm_plane *plane) drm_modeset_lock_all(dev); kfree(plane->format_types); drm_mode_object_put(dev, &plane->base); - /* if not added to a list, it must be a private plane */ - if (!list_empty(&plane->head)) { - list_del(&plane->head); - dev->mode_config.num_plane--; - } + + BUG_ON(list_empty(&plane->head)); + + list_del(&plane->head); + dev->mode_config.num_total_plane--; + if (plane->type == DRM_PLANE_TYPE_OVERLAY) + dev->mode_config.num_overlay_plane--; drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_plane_cleanup); @@ -1010,50 +1160,6 @@ void drm_plane_force_disable(struct drm_plane *plane) } EXPORT_SYMBOL(drm_plane_force_disable); -/** - * drm_mode_create - create a new display mode - * @dev: DRM device - * - * Create a new drm_display_mode, give it an ID, and return it. - * - * RETURNS: - * Pointer to new mode on success, NULL on error. - */ -struct drm_display_mode *drm_mode_create(struct drm_device *dev) -{ - struct drm_display_mode *nmode; - - nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); - if (!nmode) - return NULL; - - if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { - kfree(nmode); - return NULL; - } - - return nmode; -} -EXPORT_SYMBOL(drm_mode_create); - -/** - * drm_mode_destroy - remove a mode - * @dev: DRM device - * @mode: mode to remove - * - * Free @mode's unique identifier, then free it. - */ -void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) -{ - if (!mode) - return; - - drm_mode_object_put(dev, &mode->base); - - kfree(mode); -} -EXPORT_SYMBOL(drm_mode_destroy); - static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { struct drm_property *edid; @@ -1075,6 +1181,21 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) return 0; } +static int drm_mode_create_standard_plane_properties(struct drm_device *dev) +{ + struct drm_property *type; + + /* + * Standard properties (apply to all planes) + */ + type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, + "type", drm_plane_type_enum_list, + ARRAY_SIZE(drm_plane_type_enum_list)); + dev->mode_config.plane_type_property = type; + + return 0; +} + /** * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties * @dev: DRM device @@ -1257,6 +1378,10 @@ static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *gr return 0; } +/* + * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is + * the drm core's responsibility to set up mode control groups. + */ int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group) { @@ -1333,7 +1458,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ static int drm_crtc_convert_umode(struct drm_display_mode *out, @@ -1376,7 +1501,7 @@ static int drm_crtc_convert_umode(struct drm_display_mode *out, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getresources(struct drm_device *dev, void *data, @@ -1429,9 +1554,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data, mutex_unlock(&file_priv->fbs_lock); drm_modeset_lock_all(dev); - mode_group = &file_priv->master->minor->mode_group; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!drm_is_primary_client(file_priv)) { + mode_group = NULL; list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; @@ -1442,6 +1567,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, encoder_count++; } else { + mode_group = &file_priv->master->minor->mode_group; crtc_count = mode_group->num_crtcs; connector_count = mode_group->num_connectors; encoder_count = mode_group->num_encoders; @@ -1456,7 +1582,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_crtcs >= crtc_count) { copied = 0; crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); @@ -1483,7 +1609,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_encoders >= encoder_count) { copied = 0; encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { @@ -1514,7 +1640,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data, if (card_res->count_connectors >= connector_count) { copied = 0; connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; - if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { + if (!mode_group) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) { @@ -1561,7 +1687,7 @@ out: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getcrtc(struct drm_device *dev, @@ -1588,8 +1714,8 @@ int drm_mode_getcrtc(struct drm_device *dev, crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; crtc_resp->gamma_size = crtc->gamma_size; - if (crtc->fb) - crtc_resp->fb_id = crtc->fb->base.id; + if (crtc->primary->fb) + crtc_resp->fb_id = crtc->primary->fb->base.id; else crtc_resp->fb_id = 0; @@ -1630,7 +1756,7 @@ static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getconnector(struct drm_device *dev, void *data, @@ -1765,6 +1891,19 @@ out: return ret; } +/** + * drm_mode_getencoder - get encoder configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Construct a encoder configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1800,21 +1939,27 @@ out: } /** - * drm_mode_getplane_res - get plane info + * drm_mode_getplane_res - enumerate all plane resources * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * - * Return an plane count and set of IDs. + * Construct a list of plane ids to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_get_plane_res *plane_resp = data; struct drm_mode_config *config; struct drm_plane *plane; uint32_t __user *plane_ptr; int copied = 0, ret = 0; + unsigned num_planes; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; @@ -1822,15 +1967,28 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); config = &dev->mode_config; + if (file_priv->universal_planes) + num_planes = config->num_total_plane; + else + num_planes = config->num_overlay_plane; + /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ - if (config->num_plane && - (plane_resp->count_planes >= config->num_plane)) { + if (num_planes && + (plane_resp->count_planes >= num_planes)) { plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; list_for_each_entry(plane, &config->plane_list, head) { + /* + * Unless userspace set the 'universal planes' + * capability bit, only advertise overlays. + */ + if (plane->type != DRM_PLANE_TYPE_OVERLAY && + !file_priv->universal_planes) + continue; + if (put_user(plane->base.id, plane_ptr + copied)) { ret = -EFAULT; goto out; @@ -1838,7 +1996,7 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, copied++; } } - plane_resp->count_planes = config->num_plane; + plane_resp->count_planes = num_planes; out: drm_modeset_unlock_all(dev); @@ -1846,16 +2004,20 @@ out: } /** - * drm_mode_getplane - get plane info + * drm_mode_getplane - get plane configuration * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * - * Return plane info, including formats supported, gamma size, any - * current fb, etc. + * Construct a plane configuration structure to return to the user. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_getplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data; struct drm_mode_object *obj; @@ -1911,16 +2073,19 @@ out: } /** - * drm_mode_setplane - set up or tear down an plane + * drm_mode_setplane - configure a plane's configuration * @dev: DRM device * @data: ioctl data* * @file_priv: DRM file info * - * Set plane info, including placement, fb, scaling, and other factors. + * Set plane configuration, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_setplane(struct drm_device *dev, void *data, - struct drm_file *file_priv) + struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; struct drm_mode_object *obj; @@ -2050,6 +2215,9 @@ out: * * This is a little helper to wrap internal calls to the ->set_config driver * interface. The only thing it adds is correct refcounting dance. + * + * Returns: + * Zero on success, errno on failure. */ int drm_mode_set_config_internal(struct drm_mode_set *set) { @@ -2064,19 +2232,21 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) * crtcs. Atomic modeset will have saner semantics ... */ list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) - tmp->old_fb = tmp->fb; + tmp->old_fb = tmp->primary->fb; fb = set->fb; ret = crtc->funcs->set_config(set); if (ret == 0) { + crtc->primary->crtc = crtc; + /* crtc->fb must be updated by ->set_config, enforces this. */ - WARN_ON(fb != crtc->fb); + WARN_ON(fb != crtc->primary->fb); } list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { - if (tmp->fb) - drm_framebuffer_reference(tmp->fb); + if (tmp->primary->fb) + drm_framebuffer_reference(tmp->primary->fb); if (tmp->old_fb) drm_framebuffer_unreference(tmp->old_fb); } @@ -2085,14 +2255,19 @@ int drm_mode_set_config_internal(struct drm_mode_set *set) } EXPORT_SYMBOL(drm_mode_set_config_internal); -/* - * Checks that the framebuffer is big enough for the CRTC viewport - * (x, y, hdisplay, vdisplay) +/** + * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the + * CRTC viewport + * @crtc: CRTC that framebuffer will be displayed on + * @x: x panning + * @y: y panning + * @mode: mode that framebuffer will be displayed under + * @fb: framebuffer to check size of */ -static int drm_crtc_check_viewport(const struct drm_crtc *crtc, - int x, int y, - const struct drm_display_mode *mode, - const struct drm_framebuffer *fb) +int drm_crtc_check_viewport(const struct drm_crtc *crtc, + int x, int y, + const struct drm_display_mode *mode, + const struct drm_framebuffer *fb) { int hdisplay, vdisplay; @@ -2123,6 +2298,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc, return 0; } +EXPORT_SYMBOL(drm_crtc_check_viewport); /** * drm_mode_setcrtc - set CRTC configuration @@ -2134,7 +2310,7 @@ static int drm_crtc_check_viewport(const struct drm_crtc *crtc, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_setcrtc(struct drm_device *dev, void *data, @@ -2174,12 +2350,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ if (crtc_req->fb_id == -1) { - if (!crtc->fb) { + if (!crtc->primary->fb) { DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } - fb = crtc->fb; + fb = crtc->primary->fb; /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_reference(fb); } else { @@ -2336,8 +2512,23 @@ out: return ret; } + + +/** + * drm_mode_cursor_ioctl - set CRTC's cursor configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Set the cursor configuration based on user request. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_cursor_ioctl(struct drm_device *dev, - void *data, struct drm_file *file_priv) + void *data, struct drm_file *file_priv) { struct drm_mode_cursor *req = data; struct drm_mode_cursor2 new_req; @@ -2348,6 +2539,21 @@ int drm_mode_cursor_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, &new_req, file_priv); } +/** + * drm_mode_cursor2_ioctl - set CRTC's cursor configuration + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Set the cursor configuration based on user request. This implements the 2nd + * version of the cursor ioctl, which allows userspace to additionally specify + * the hotspot of the pointer. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2355,7 +2561,14 @@ int drm_mode_cursor2_ioctl(struct drm_device *dev, return drm_mode_cursor_common(dev, req, file_priv); } -/* Original addfb only supported RGB formats, so figure out which one */ +/** + * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description + * @bpp: bits per pixels + * @depth: bit depth per pixel + * + * Computes a drm fourcc pixel format code for the given @bpp/@depth values. + * Useful in fbdev emulation code, since that deals in those values. + */ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) { uint32_t fmt; @@ -2397,11 +2610,12 @@ EXPORT_SYMBOL(drm_mode_legacy_fb_format); * @data: data pointer for the ioctl * @file_priv: drm file for the ioctl call * - * Add a new FB to the specified CRTC, given a user request. + * Add a new FB to the specified CRTC, given a user request. This is the + * original addfb ioclt which only supported RGB formats. * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_addfb(struct drm_device *dev, @@ -2574,11 +2788,13 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) * @data: data pointer for the ioctl * @file_priv: drm file for the ioctl call * - * Add a new FB to the specified CRTC, given a user request with format. + * Add a new FB to the specified CRTC, given a user request with format. This is + * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers + * and uses fourcc codes as pixel format specifiers. * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_addfb2(struct drm_device *dev, @@ -2638,7 +2854,7 @@ int drm_mode_addfb2(struct drm_device *dev, * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_rmfb(struct drm_device *dev, @@ -2692,7 +2908,7 @@ fail_lookup: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ int drm_mode_getfb(struct drm_device *dev, @@ -2715,7 +2931,8 @@ int drm_mode_getfb(struct drm_device *dev, r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; if (fb->funcs->create_handle) { - if (file_priv->is_master || capable(CAP_SYS_ADMIN)) { + if (file_priv->is_master || capable(CAP_SYS_ADMIN) || + drm_is_control_client(file_priv)) { ret = fb->funcs->create_handle(fb, file_priv, &r->handle); } else { @@ -2736,6 +2953,25 @@ int drm_mode_getfb(struct drm_device *dev, return ret; } +/** + * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB + * @dev: drm device for the ioctl + * @data: data pointer for the ioctl + * @file_priv: drm file for the ioctl call + * + * Lookup the FB and flush out the damaged area supplied by userspace as a clip + * rectangle list. Generic userspace which does frontbuffer rendering must call + * this ioctl to flush out the changes on manual-update display outputs, e.g. + * usb display-link, mipi manual update panels or edp panel self refresh modes. + * + * Modesetting drivers which always update the frontbuffer do not need to + * implement the corresponding ->dirty framebuffer callback. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -2813,7 +3049,7 @@ out_err1: * * Called by the user via ioctl. * - * RETURNS: + * Returns: * Zero on success, errno on failure. */ void drm_fb_release(struct drm_file *priv) @@ -2837,6 +3073,20 @@ void drm_fb_release(struct drm_file *priv) mutex_unlock(&priv->fbs_lock); } +/** + * drm_property_create - create a new property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create(struct drm_device *dev, int flags, const char *name, int num_values) { @@ -2875,6 +3125,24 @@ fail: } EXPORT_SYMBOL(drm_property_create); +/** + * drm_property_create - create a new enumeration property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property values + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is only allowed to set one of the predefined values for enumeration + * properties. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, @@ -2903,6 +3171,24 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, } EXPORT_SYMBOL(drm_property_create_enum); +/** + * drm_property_create - create a new bitmask property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @props: enumeration lists with property bitflags + * @num_values: number of pre-defined values + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Compared to plain enumeration properties userspace is allowed to set any + * or'ed together combination of the predefined property bitflag values + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, @@ -2931,6 +3217,24 @@ struct drm_property *drm_property_create_bitmask(struct drm_device *dev, } EXPORT_SYMBOL(drm_property_create_bitmask); +/** + * drm_property_create - create a new ranged property type + * @dev: drm device + * @flags: flags specifying the property type + * @name: name of the property + * @min: minimum value of the property + * @max: maximum value of the property + * + * This creates a new generic drm property which can then be attached to a drm + * object with drm_object_attach_property. The returned property object must be + * freed with drm_property_destroy. + * + * Userspace is allowed to set any interger value in the (min, max) range + * inclusive. + * + * Returns: + * A pointer to the newly created property on success, NULL on failure. + */ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max) @@ -2950,6 +3254,21 @@ struct drm_property *drm_property_create_range(struct drm_device *dev, int flags } EXPORT_SYMBOL(drm_property_create_range); +/** + * drm_property_add_enum - add a possible value to an enumeration property + * @property: enumeration property to change + * @index: index of the new enumeration + * @value: value of the new enumeration + * @name: symbolic name of the new enumeration + * + * This functions adds enumerations to a property. + * + * It's use is deprecated, drivers should use one of the more specific helpers + * to directly create the property with all enumerations already attached. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name) { @@ -2989,6 +3308,14 @@ int drm_property_add_enum(struct drm_property *property, int index, } EXPORT_SYMBOL(drm_property_add_enum); +/** + * drm_property_destroy - destroy a drm property + * @dev: drm device + * @property: property to destry + * + * This function frees a property including any attached resources like + * enumeration values. + */ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) { struct drm_property_enum *prop_enum, *pt; @@ -3006,6 +3333,16 @@ void drm_property_destroy(struct drm_device *dev, struct drm_property *property) } EXPORT_SYMBOL(drm_property_destroy); +/** + * drm_object_attach_property - attach a property to a modeset object + * @obj: drm modeset object + * @property: property to attach + * @init_val: initial value of the property + * + * This attaches the given property to the modeset object with the given initial + * value. Currently this function cannot fail since the properties are stored in + * a statically sized array. + */ void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val) @@ -3026,6 +3363,19 @@ void drm_object_attach_property(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_attach_property); +/** + * drm_object_property_set_value - set the value of a property + * @obj: drm mode object to set property value for + * @property: property to set + * @val: value the property should be set to + * + * This functions sets a given property on a given object. This function only + * changes the software state of the property, it does not call into the + * driver's ->set_property callback. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val) { @@ -3042,6 +3392,20 @@ int drm_object_property_set_value(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_property_set_value); +/** + * drm_object_property_get_value - retrieve the value of a property + * @obj: drm mode object to get property value from + * @property: property to retrieve + * @val: storage for the property value + * + * This function retrieves the softare state of the given property for the given + * property. Since there is no driver callback to retrieve the current property + * value this might be out of sync with the hardware, depending upon the driver + * and property. + * + * Returns: + * Zero on success, error code on failure. + */ int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { @@ -3058,6 +3422,19 @@ int drm_object_property_get_value(struct drm_mode_object *obj, } EXPORT_SYMBOL(drm_object_property_get_value); +/** + * drm_mode_getproperty_ioctl - get the current value of a connector's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an connectors's property. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3196,6 +3573,20 @@ static void drm_property_destroy_blob(struct drm_device *dev, kfree(blob); } +/** + * drm_mode_getblob_ioctl - get the contents of a blob property value + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the contents of a blob property. The value stored in + * an object's blob property is just a normal modeset object id. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3230,6 +3621,17 @@ done: return ret; } +/** + * drm_mode_connector_update_edid_property - update the edid property of a connector + * @connector: drm connector + * @edid: new value of the edid property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's edid property. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { @@ -3287,6 +3689,20 @@ static bool drm_property_change_is_valid(struct drm_property *property, } } +/** + * drm_mode_connector_property_set_ioctl - set the current value of a connector property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function sets the current value for a connectors's property. It also + * calls into a driver's ->set_property callback to update the hardware state + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3353,6 +3769,21 @@ static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, return ret; } +/** + * drm_mode_getproperty_ioctl - get the current value of a object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function retrieves the current value for an object's property. Compared + * to the connector specific ioctl this one is extended to also work on crtc and + * plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3409,6 +3840,22 @@ out: return ret; } +/** + * drm_mode_obj_set_property_ioctl - set the current value of an object's property + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This function sets the current value for an object's property. It also calls + * into a driver's ->set_property callback to update the hardware state. + * Compared to the connector specific ioctl this one is extended to also work on + * crtc and plane objects. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3468,6 +3915,18 @@ out: return ret; } +/** + * drm_mode_connector_attach_encoder - attach a connector to an encoder + * @connector: connector to attach + * @encoder: encoder to attach @connector to + * + * This function links up a connector to an encoder. Note that the routing + * restrictions between encoders and crtcs are exposed to userspace through the + * possible_clones and possible_crtcs bitmasks. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder) { @@ -3483,23 +3942,20 @@ int drm_mode_connector_attach_encoder(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_connector_attach_encoder); -void drm_mode_connector_detach_encoder(struct drm_connector *connector, - struct drm_encoder *encoder) -{ - int i; - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == encoder->base.id) { - connector->encoder_ids[i] = 0; - if (connector->encoder == encoder) - connector->encoder = NULL; - break; - } - } -} -EXPORT_SYMBOL(drm_mode_connector_detach_encoder); - +/** + * drm_mode_crtc_set_gamma_size - set the gamma table size + * @crtc: CRTC to set the gamma table size for + * @gamma_size: size of the gamma table + * + * Drivers which support gamma tables should set this to the supported gamma + * table size when initializing the CRTC. Currently the drm core only supports a + * fixed gamma table size. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, - int gamma_size) + int gamma_size) { crtc->gamma_size = gamma_size; @@ -3513,6 +3969,20 @@ int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, } EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); +/** + * drm_mode_gamma_set_ioctl - set the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Set the gamma table of a CRTC to the one passed in by the user. Userspace can + * inquire the required gamma table size through drm_mode_gamma_get_ioctl. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3572,6 +4042,21 @@ out: } +/** + * drm_mode_gamma_get_ioctl - get the gamma table + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Copy the current gamma table into the storage provided. This also provides + * the gamma table size the driver expects, which can be used to size the + * allocated storage. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3622,6 +4107,24 @@ out: return ret; } +/** + * drm_mode_page_flip_ioctl - schedule an asynchronous fb update + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This schedules an asynchronous update on a given CRTC, called page flip. + * Optionally a drm event is generated to signal the completion of the event. + * Generic drivers cannot assume that a pageflip with changed framebuffer + * properties (including driver specific metadata like tiling layout) will work, + * but some drivers support e.g. pixel format changes through the pageflip + * ioctl. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3646,7 +4149,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, crtc = obj_to_crtc(obj); mutex_lock(&crtc->mutex); - if (crtc->fb == NULL) { + if (crtc->primary->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. @@ -3668,7 +4171,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if (ret) goto out; - if (crtc->fb->pixel_format != fb->pixel_format) { + if (crtc->primary->fb->pixel_format != fb->pixel_format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; @@ -3701,7 +4204,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, (void (*) (struct drm_pending_event *)) kfree; } - old_fb = crtc->fb; + old_fb = crtc->primary->fb; ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { @@ -3719,7 +4222,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, * Failing to do so will screw with the reference counting * on framebuffers. */ - WARN_ON(crtc->fb != fb); + WARN_ON(crtc->primary->fb != fb); /* Unref only the old framebuffer. */ fb = NULL; } @@ -3734,6 +4237,14 @@ out: return ret; } +/** + * drm_mode_config_reset - call ->reset callbacks + * @dev: drm device + * + * This functions calls all the crtc's, encoder's and connector's ->reset + * callback. Drivers can use this in e.g. their driver load or resume code to + * reset hardware and software state. + */ void drm_mode_config_reset(struct drm_device *dev) { struct drm_crtc *crtc; @@ -3757,16 +4268,66 @@ void drm_mode_config_reset(struct drm_device *dev) } EXPORT_SYMBOL(drm_mode_config_reset); +/** + * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This creates a new dumb buffer in the driver's backing storage manager (GEM, + * TTM or something else entirely) and returns the resulting buffer handle. This + * handle can then be wrapped up into a framebuffer modeset object. + * + * Note that userspace is not allowed to use such objects for render + * acceleration - drivers must create their own private ioctls for such a use + * case. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_create_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_create_dumb *args = data; + u32 cpp, stride, size; if (!dev->driver->dumb_create) return -ENOSYS; + if (!args->width || !args->height || !args->bpp) + return -EINVAL; + + /* overflow checks for 32bit size calculations */ + cpp = DIV_ROUND_UP(args->bpp, 8); + if (cpp > 0xffffffffU / args->width) + return -EINVAL; + stride = cpp * args->width; + if (args->height > 0xffffffffU / stride) + return -EINVAL; + + /* test for wrap-around */ + size = args->height * stride; + if (PAGE_ALIGN(size) == 0) + return -EINVAL; + return dev->driver->dumb_create(file_priv, dev, args); } +/** + * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * Allocate an offset in the drm device node's address space to be able to + * memory map a dumb buffer. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3779,6 +4340,21 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); } +/** + * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer + * @dev: DRM device + * @data: ioctl data + * @file_priv: DRM file info + * + * This destroys the userspace handle for the given dumb backing storage buffer. + * Since buffer objects must be reference counted in the kernel a buffer object + * won't be immediately freed if a framebuffer modeset object still uses it. + * + * Called by the user via ioctl. + * + * Returns: + * Zero on success, errno on failure. + */ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -3790,9 +4366,14 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, return dev->driver->dumb_destroy(file_priv, dev, args->handle); } -/* - * Just need to support RGB formats here for compat with code that doesn't - * use pixel formats directly yet. +/** + * drm_fb_get_bpp_depth - get the bpp/depth values for format + * @format: pixel format (DRM_FORMAT_*) + * @depth: storage for the depth value + * @bpp: storage for the bpp value + * + * This only supports RGB formats here for compat with code that doesn't use + * pixel formats directly yet. */ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp) @@ -3864,7 +4445,7 @@ EXPORT_SYMBOL(drm_fb_get_bpp_depth); * drm_format_num_planes - get the number of planes for format * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The number of planes used by the specified pixel format. */ int drm_format_num_planes(uint32_t format) @@ -3899,7 +4480,7 @@ EXPORT_SYMBOL(drm_format_num_planes); * @format: pixel format (DRM_FORMAT_*) * @plane: plane index * - * RETURNS: + * Returns: * The bytes per pixel value for the specified plane. */ int drm_format_plane_cpp(uint32_t format, int plane) @@ -3945,7 +4526,7 @@ EXPORT_SYMBOL(drm_format_plane_cpp); * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The horizontal chroma subsampling factor for the * specified pixel format. */ @@ -3980,7 +4561,7 @@ EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * - * RETURNS: + * Returns: * The vertical chroma subsampling factor for the * specified pixel format. */ @@ -4030,6 +4611,7 @@ void drm_mode_config_init(struct drm_device *dev) drm_modeset_lock_all(dev); drm_mode_create_standard_connector_properties(dev); + drm_mode_create_standard_plane_properties(dev); drm_modeset_unlock_all(dev); /* Just to be sure */ @@ -4037,6 +4619,8 @@ void drm_mode_config_init(struct drm_device *dev) dev->mode_config.num_connector = 0; dev->mode_config.num_crtc = 0; dev->mode_config.num_encoder = 0; + dev->mode_config.num_overlay_plane = 0; + dev->mode_config.num_total_plane = 0; } EXPORT_SYMBOL(drm_mode_config_init); diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index ea92b82..df281b5 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -72,165 +72,23 @@ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) } EXPORT_SYMBOL(drm_helper_move_panel_connectors_to_head); -static bool drm_kms_helper_poll = true; -module_param_named(poll, drm_kms_helper_poll, bool, 0600); - -static void drm_mode_validate_flag(struct drm_connector *connector, - int flags) -{ - struct drm_display_mode *mode; - - if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE | - DRM_MODE_FLAG_3D_MASK)) - return; - - list_for_each_entry(mode, &connector->modes, head) { - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && - !(flags & DRM_MODE_FLAG_INTERLACE)) - mode->status = MODE_NO_INTERLACE; - if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) && - !(flags & DRM_MODE_FLAG_DBLSCAN)) - mode->status = MODE_NO_DBLESCAN; - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) && - !(flags & DRM_MODE_FLAG_3D_MASK)) - mode->status = MODE_NO_STEREO; - } - - return; -} - -/** - * drm_helper_probe_single_connector_modes - get complete set of display modes - * @connector: connector to probe - * @maxX: max width for modes - * @maxY: max height for modes - * - * LOCKING: - * Caller must hold mode config lock. - * - * Based on the helper callbacks implemented by @connector try to detect all - * valid modes. Modes will first be added to the connector's probed_modes list, - * then culled (based on validity and the @maxX, @maxY parameters) and put into - * the normal modes list. - * - * Intended to be use as a generic implementation of the ->fill_modes() - * @connector vfunc for drivers that use the crtc helpers for output mode - * filtering and detection. - * - * RETURNS: - * Number of modes found on @connector. - */ -int drm_helper_probe_single_connector_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY) -{ - struct drm_device *dev = connector->dev; - struct drm_display_mode *mode; - struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; - int count = 0; - int mode_flags = 0; - bool verbose_prune = true; - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, - drm_get_connector_name(connector)); - /* set all modes to the unverified state */ - list_for_each_entry(mode, &connector->modes, head) - mode->status = MODE_UNVERIFIED; - - if (connector->force) { - if (connector->force == DRM_FORCE_ON) - connector->status = connector_status_connected; - else - connector->status = connector_status_disconnected; - if (connector->funcs->force) - connector->funcs->force(connector); - } else { - connector->status = connector->funcs->detect(connector, true); - } - - /* Re-enable polling in case the global poll config changed. */ - if (drm_kms_helper_poll != dev->mode_config.poll_running) - drm_kms_helper_poll_enable(dev); - - dev->mode_config.poll_running = drm_kms_helper_poll; - - if (connector->status == connector_status_disconnected) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] disconnected\n", - connector->base.id, drm_get_connector_name(connector)); - drm_mode_connector_update_edid_property(connector, NULL); - verbose_prune = false; - goto prune; - } - -#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE - count = drm_load_edid_firmware(connector); - if (count == 0) -#endif - count = (*connector_funcs->get_modes)(connector); - - if (count == 0 && connector->status == connector_status_connected) - count = drm_add_modes_noedid(connector, 1024, 768); - if (count == 0) - goto prune; - - drm_mode_connector_list_update(connector); - - if (maxX && maxY) - drm_mode_validate_size(dev, &connector->modes, maxX, - maxY, 0); - - if (connector->interlace_allowed) - mode_flags |= DRM_MODE_FLAG_INTERLACE; - if (connector->doublescan_allowed) - mode_flags |= DRM_MODE_FLAG_DBLSCAN; - if (connector->stereo_allowed) - mode_flags |= DRM_MODE_FLAG_3D_MASK; - drm_mode_validate_flag(connector, mode_flags); - - list_for_each_entry(mode, &connector->modes, head) { - if (mode->status == MODE_OK) - mode->status = connector_funcs->mode_valid(connector, - mode); - } - -prune: - drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); - - if (list_empty(&connector->modes)) - return 0; - - list_for_each_entry(mode, &connector->modes, head) - mode->vrefresh = drm_mode_vrefresh(mode); - - drm_mode_sort(&connector->modes); - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id, - drm_get_connector_name(connector)); - list_for_each_entry(mode, &connector->modes, head) { - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); - drm_mode_debug_printmodeline(mode); - } - - return count; -} -EXPORT_SYMBOL(drm_helper_probe_single_connector_modes); - /** * drm_helper_encoder_in_use - check if a given encoder is in use * @encoder: encoder to check * - * LOCKING: - * Caller must hold mode config lock. + * Checks whether @encoder is with the current mode setting output configuration + * in use by any connector. This doesn't mean that it is actually enabled since + * the DPMS state is tracked separately. * - * Walk @encoders's DRM device's mode_config and see if it's in use. - * - * RETURNS: - * True if @encoder is part of the mode_config, false otherwise. + * Returns: + * True if @encoder is used, false otherwise. */ bool drm_helper_encoder_in_use(struct drm_encoder *encoder) { struct drm_connector *connector; struct drm_device *dev = encoder->dev; + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); list_for_each_entry(connector, &dev->mode_config.connector_list, head) if (connector->encoder == encoder) return true; @@ -242,19 +100,19 @@ EXPORT_SYMBOL(drm_helper_encoder_in_use); * drm_helper_crtc_in_use - check if a given CRTC is in a mode_config * @crtc: CRTC to check * - * LOCKING: - * Caller must hold mode config lock. - * - * Walk @crtc's DRM device's mode_config and see if it's in use. + * Checks whether @crtc is with the current mode setting output configuration + * in use by any connector. This doesn't mean that it is actually enabled since + * the DPMS state is tracked separately. * - * RETURNS: - * True if @crtc is part of the mode_config, false otherwise. + * Returns: + * True if @crtc is used, false otherwise. */ bool drm_helper_crtc_in_use(struct drm_crtc *crtc) { struct drm_encoder *encoder; struct drm_device *dev = crtc->dev; - /* FIXME: Locking around list access? */ + + WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) if (encoder->crtc == crtc && drm_helper_encoder_in_use(encoder)) return true; @@ -279,27 +137,17 @@ drm_encoder_disable(struct drm_encoder *encoder) encoder->bridge->funcs->post_disable(encoder->bridge); } -/** - * drm_helper_disable_unused_functions - disable unused objects - * @dev: DRM device - * - * LOCKING: - * Caller must hold mode config lock. - * - * If an connector or CRTC isn't part of @dev's mode_config, it can be disabled - * by calling its dpms function, which should power it off. - */ -void drm_helper_disable_unused_functions(struct drm_device *dev) +static void __drm_helper_disable_unused_functions(struct drm_device *dev) { struct drm_encoder *encoder; struct drm_connector *connector; struct drm_crtc *crtc; + drm_warn_on_modeset_not_all_locked(dev); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (!connector->encoder) continue; - if (connector->status == connector_status_disconnected) - connector->encoder = NULL; } list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { @@ -318,10 +166,27 @@ void drm_helper_disable_unused_functions(struct drm_device *dev) (*crtc_funcs->disable)(crtc); else (*crtc_funcs->dpms)(crtc, DRM_MODE_DPMS_OFF); - crtc->fb = NULL; + crtc->primary->fb = NULL; } } } + +/** + * drm_helper_disable_unused_functions - disable unused objects + * @dev: DRM device + * + * This function walks through the entire mode setting configuration of @dev. It + * will remove any crtc links of unused encoders and encoder links of + * disconnected connectors. Then it will disable all unused encoders and crtcs + * either by calling their disable callback if available or by calling their + * dpms callback with DRM_MODE_DPMS_OFF. + */ +void drm_helper_disable_unused_functions(struct drm_device *dev) +{ + drm_modeset_lock_all(dev); + __drm_helper_disable_unused_functions(dev); + drm_modeset_unlock_all(dev); +} EXPORT_SYMBOL(drm_helper_disable_unused_functions); /* @@ -355,9 +220,6 @@ drm_crtc_prepare_encoders(struct drm_device *dev) * @y: vertical offset into the surface * @old_fb: old framebuffer, for cleanup * - * LOCKING: - * Caller must hold mode config lock. - * * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance * to fixup or reject the mode prior to trying to set it. This is an internal * helper that drivers could e.g. use to update properties that require the @@ -367,8 +229,8 @@ drm_crtc_prepare_encoders(struct drm_device *dev) * drm_crtc_helper_set_config() helper function to drive the mode setting * sequence. * - * RETURNS: - * True if the mode was set successfully, or false otherwise. + * Returns: + * True if the mode was set successfully, false otherwise. */ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, @@ -384,6 +246,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, struct drm_encoder *encoder; bool ret = true; + drm_warn_on_modeset_not_all_locked(dev); + saved_enabled = crtc->enabled; crtc->enabled = drm_helper_crtc_in_use(crtc); if (!crtc->enabled) @@ -552,7 +416,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) } } - drm_helper_disable_unused_functions(dev); + __drm_helper_disable_unused_functions(dev); return 0; } @@ -560,17 +424,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) * drm_crtc_helper_set_config - set a new config from userspace * @set: mode set configuration * - * LOCKING: - * Caller must hold mode config lock. - * * Setup a new configuration, provided by the upper layers (either an ioctl call - * from userspace or internally e.g. from the fbdev suppport code) in @set, and + * from userspace or internally e.g. from the fbdev support code) in @set, and * enable it. This is the main helper functions for drivers that implement * kernel mode setting with the crtc helper functions and the assorted * ->prepare(), ->modeset() and ->commit() helper callbacks. * - * RETURNS: - * Returns 0 on success, -ERRNO on failure. + * Returns: + * Returns 0 on success, negative errno numbers on failure. */ int drm_crtc_helper_set_config(struct drm_mode_set *set) { @@ -612,6 +473,8 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) dev = set->crtc->dev; + drm_warn_on_modeset_not_all_locked(dev); + /* * Allocate space for the backup of all (non-pointer) encoder and * connector data. @@ -647,19 +510,19 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) save_set.mode = &set->crtc->mode; save_set.x = set->crtc->x; save_set.y = set->crtc->y; - save_set.fb = set->crtc->fb; + save_set.fb = set->crtc->primary->fb; /* We should be able to check here if the fb has the same properties * and then just flip_or_move it */ - if (set->crtc->fb != set->fb) { + if (set->crtc->primary->fb != set->fb) { /* If we have no fb then treat it as a full mode set */ - if (set->crtc->fb == NULL) { + if (set->crtc->primary->fb == NULL) { DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); mode_changed = true; } else if (set->fb == NULL) { mode_changed = true; } else if (set->fb->pixel_format != - set->crtc->fb->pixel_format) { + set->crtc->primary->fb->pixel_format) { mode_changed = true; } else fb_changed = true; @@ -689,12 +552,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) if (new_encoder == NULL) /* don't break so fail path works correct */ fail = 1; - break; if (connector->dpms != DRM_MODE_DPMS_ON) { DRM_DEBUG_KMS("connector dpms not on, full mode switch\n"); mode_changed = true; } + + break; } } @@ -760,13 +624,13 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) DRM_DEBUG_KMS("attempting to set mode from" " userspace\n"); drm_mode_debug_printmodeline(set->mode); - set->crtc->fb = set->fb; + set->crtc->primary->fb = set->fb; if (!drm_crtc_helper_set_mode(set->crtc, set->mode, set->x, set->y, save_set.fb)) { DRM_ERROR("failed to set mode on [CRTC:%d]\n", set->crtc->base.id); - set->crtc->fb = save_set.fb; + set->crtc->primary->fb = save_set.fb; ret = -EINVAL; goto fail; } @@ -777,17 +641,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); } } - drm_helper_disable_unused_functions(dev); + __drm_helper_disable_unused_functions(dev); } else if (fb_changed) { set->crtc->x = set->x; set->crtc->y = set->y; - set->crtc->fb = set->fb; + set->crtc->primary->fb = set->fb; ret = crtc_funcs->mode_set_base(set->crtc, set->x, set->y, save_set.fb); if (ret != 0) { set->crtc->x = save_set.x; set->crtc->y = save_set.y; - set->crtc->fb = save_set.fb; + set->crtc->primary->fb = save_set.fb; goto fail; } } @@ -924,8 +788,16 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode) } EXPORT_SYMBOL(drm_helper_connector_dpms); -int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, - struct drm_mode_fb_cmd2 *mode_cmd) +/** + * drm_helper_mode_fill_fb_struct - fill out framebuffer metadata + * @fb: drm_framebuffer object to fill out + * @mode_cmd: metadata from the userspace fb creation request + * + * This helper can be used in a drivers fb_create callback to pre-fill the fb's + * metadata fields. + */ +void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, + struct drm_mode_fb_cmd2 *mode_cmd) { int i; @@ -938,26 +810,47 @@ int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, &fb->bits_per_pixel); fb->pixel_format = mode_cmd->pixel_format; - - return 0; } EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); -int drm_helper_resume_force_mode(struct drm_device *dev) +/** + * drm_helper_resume_force_mode - force-restore mode setting configuration + * @dev: drm_device which should be restored + * + * Drivers which use the mode setting helpers can use this function to + * force-restore the mode setting configuration e.g. on resume or when something + * else might have trampled over the hw state (like some overzealous old BIOSen + * tended to do). + * + * This helper doesn't provide a error return value since restoring the old + * config should never fail due to resource allocation issues since the driver + * has successfully set the restored configuration already. Hence this should + * boil down to the equivalent of a few dpms on calls, which also don't provide + * an error code. + * + * Drivers where simply restoring an old configuration again might fail (e.g. + * due to slight differences in allocating shared resources when the + * configuration is restored in a different order than when userspace set it up) + * need to use their own restore logic. + */ +void drm_helper_resume_force_mode(struct drm_device *dev) { struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_crtc_helper_funcs *crtc_funcs; - int ret, encoder_dpms; + int encoder_dpms; + bool ret; + drm_modeset_lock_all(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (!crtc->enabled) continue; ret = drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + crtc->x, crtc->y, crtc->primary->fb); + /* Restoring the old config should never fail! */ if (ret == false) DRM_ERROR("failed to set mode on crtc %p\n", crtc); @@ -980,155 +873,9 @@ int drm_helper_resume_force_mode(struct drm_device *dev) drm_helper_choose_crtc_dpms(crtc)); } } + /* disable the unused connectors while restoring the modesetting */ - drm_helper_disable_unused_functions(dev); - return 0; + __drm_helper_disable_unused_functions(dev); + drm_modeset_unlock_all(dev); } EXPORT_SYMBOL(drm_helper_resume_force_mode); - -void drm_kms_helper_hotplug_event(struct drm_device *dev) -{ - /* send a uevent + call fbdev */ - drm_sysfs_hotplug_event(dev); - if (dev->mode_config.funcs->output_poll_changed) - dev->mode_config.funcs->output_poll_changed(dev); -} -EXPORT_SYMBOL(drm_kms_helper_hotplug_event); - -#define DRM_OUTPUT_POLL_PERIOD (10*HZ) -static void output_poll_execute(struct work_struct *work) -{ - struct delayed_work *delayed_work = to_delayed_work(work); - struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_work); - struct drm_connector *connector; - enum drm_connector_status old_status; - bool repoll = false, changed = false; - - if (!drm_kms_helper_poll) - return; - - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - - /* Ignore forced connectors. */ - if (connector->force) - continue; - - /* Ignore HPD capable connectors and connectors where we don't - * want any hotplug detection at all for polling. */ - if (!connector->polled || connector->polled == DRM_CONNECTOR_POLL_HPD) - continue; - - repoll = true; - - old_status = connector->status; - /* if we are connected and don't want to poll for disconnect - skip it */ - if (old_status == connector_status_connected && - !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT)) - continue; - - connector->status = connector->funcs->detect(connector, false); - if (old_status != connector->status) { - const char *old, *new; - - old = drm_get_connector_status_name(old_status); - new = drm_get_connector_status_name(connector->status); - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] " - "status updated from %s to %s\n", - connector->base.id, - drm_get_connector_name(connector), - old, new); - - changed = true; - } - } - - mutex_unlock(&dev->mode_config.mutex); - - if (changed) - drm_kms_helper_hotplug_event(dev); - - if (repoll) - schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD); -} - -void drm_kms_helper_poll_disable(struct drm_device *dev) -{ - if (!dev->mode_config.poll_enabled) - return; - cancel_delayed_work_sync(&dev->mode_config.output_poll_work); -} -EXPORT_SYMBOL(drm_kms_helper_poll_disable); - -void drm_kms_helper_poll_enable(struct drm_device *dev) -{ - bool poll = false; - struct drm_connector *connector; - - if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) - return; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT)) - poll = true; - } - - if (poll) - schedule_delayed_work(&dev->mode_config.output_poll_work, DRM_OUTPUT_POLL_PERIOD); -} -EXPORT_SYMBOL(drm_kms_helper_poll_enable); - -void drm_kms_helper_poll_init(struct drm_device *dev) -{ - INIT_DELAYED_WORK(&dev->mode_config.output_poll_work, output_poll_execute); - dev->mode_config.poll_enabled = true; - - drm_kms_helper_poll_enable(dev); -} -EXPORT_SYMBOL(drm_kms_helper_poll_init); - -void drm_kms_helper_poll_fini(struct drm_device *dev) -{ - drm_kms_helper_poll_disable(dev); -} -EXPORT_SYMBOL(drm_kms_helper_poll_fini); - -bool drm_helper_hpd_irq_event(struct drm_device *dev) -{ - struct drm_connector *connector; - enum drm_connector_status old_status; - bool changed = false; - - if (!dev->mode_config.poll_enabled) - return false; - - mutex_lock(&dev->mode_config.mutex); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - - /* Only handle HPD capable connectors. */ - if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) - continue; - - old_status = connector->status; - - connector->status = connector->funcs->detect(connector, false); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", - connector->base.id, - drm_get_connector_name(connector), - drm_get_connector_status_name(old_status), - drm_get_connector_status_name(connector->status)); - if (old_status != connector->status) - changed = true; - } - - mutex_unlock(&dev->mode_config.mutex); - - if (changed) - drm_kms_helper_hotplug_event(dev); - - return changed; -} -EXPORT_SYMBOL(drm_helper_hpd_irq_event); diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h new file mode 100644 index 0000000..a2945ee --- /dev/null +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2006 Keith Packard + * Copyright © 2007-2008 Dave Airlie + * Copyright © 2007-2008 Intel Corporation + * Jesse Barnes + * Copyright © 2014 Intel Corporation + * Daniel Vetter + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * This header file contains mode setting related functions and definitions + * which are only used within the drm module as internal implementation details + * and are not exported to drivers. + */ + +int drm_mode_object_get(struct drm_device *dev, + struct drm_mode_object *obj, uint32_t obj_type); +void drm_mode_object_put(struct drm_device *dev, + struct drm_mode_object *object); + diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 9e978aa..4b6e6f3 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -346,3 +346,414 @@ int drm_dp_bw_code_to_link_rate(u8 link_bw) } } EXPORT_SYMBOL(drm_dp_bw_code_to_link_rate); + +/** + * DOC: dp helpers + * + * The DisplayPort AUX channel is an abstraction to allow generic, driver- + * independent access to AUX functionality. Drivers can take advantage of + * this by filling in the fields of the drm_dp_aux structure. + * + * Transactions are described using a hardware-independent drm_dp_aux_msg + * structure, which is passed into a driver's .transfer() implementation. + * Both native and I2C-over-AUX transactions are supported. + */ + +static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request, + unsigned int offset, void *buffer, size_t size) +{ + struct drm_dp_aux_msg msg; + unsigned int retry; + int err; + + memset(&msg, 0, sizeof(msg)); + msg.address = offset; + msg.request = request; + msg.buffer = buffer; + msg.size = size; + + /* + * The specification doesn't give any recommendation on how often to + * retry native transactions, so retry 7 times like for I2C-over-AUX + * transactions. + */ + for (retry = 0; retry < 7; retry++) { + err = aux->transfer(aux, &msg); + if (err < 0) { + if (err == -EBUSY) + continue; + + return err; + } + + + switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) { + case DP_AUX_NATIVE_REPLY_ACK: + if (err < size) + return -EPROTO; + return err; + + case DP_AUX_NATIVE_REPLY_NACK: + return -EIO; + + case DP_AUX_NATIVE_REPLY_DEFER: + usleep_range(400, 500); + break; + } + } + + DRM_DEBUG_KMS("too many retries, giving up\n"); + return -EIO; +} + +/** + * drm_dp_dpcd_read() - read a series of bytes from the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the (first) register to read + * @buffer: buffer to store the register values + * @size: number of bytes in @buffer + * + * Returns the number of bytes transferred on success, or a negative error + * code on failure. -EIO is returned if the request was NAKed by the sink or + * if the retry count was exceeded. If not all bytes were transferred, this + * function returns -EPROTO. Errors from the underlying AUX channel transfer + * function, with the exception of -EBUSY (which causes the transaction to + * be retried), are propagated to the caller. + */ +ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size) +{ + return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer, + size); +} +EXPORT_SYMBOL(drm_dp_dpcd_read); + +/** + * drm_dp_dpcd_write() - write a series of bytes to the DPCD + * @aux: DisplayPort AUX channel + * @offset: address of the (first) register to write + * @buffer: buffer containing the values to write + * @size: number of bytes in @buffer + * + * Returns the number of bytes transferred on success, or a negative error + * code on failure. -EIO is returned if the request was NAKed by the sink or + * if the retry count was exceeded. If not all bytes were transferred, this + * function returns -EPROTO. Errors from the underlying AUX channel transfer + * function, with the exception of -EBUSY (which causes the transaction to + * be retried), are propagated to the caller. + */ +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset, + void *buffer, size_t size) +{ + return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_WRITE, offset, buffer, + size); +} +EXPORT_SYMBOL(drm_dp_dpcd_write); + +/** + * drm_dp_dpcd_read_link_status() - read DPCD link status (bytes 0x202-0x207) + * @aux: DisplayPort AUX channel + * @status: buffer to store the link status in (must be at least 6 bytes) + * + * Returns the number of bytes transferred on success or a negative error + * code on failure. + */ +int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux, + u8 status[DP_LINK_STATUS_SIZE]) +{ + return drm_dp_dpcd_read(aux, DP_LANE0_1_STATUS, status, + DP_LINK_STATUS_SIZE); +} +EXPORT_SYMBOL(drm_dp_dpcd_read_link_status); + +/** + * drm_dp_link_probe() - probe a DisplayPort link for capabilities + * @aux: DisplayPort AUX channel + * @link: pointer to structure in which to return link capabilities + * + * The structure filled in by this function can usually be passed directly + * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and + * configure the link based on the link's capabilities. + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link) +{ + u8 values[3]; + int err; + + memset(link, 0, sizeof(*link)); + + err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values)); + if (err < 0) + return err; + + link->revision = values[0]; + link->rate = drm_dp_bw_code_to_link_rate(values[1]); + link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK; + + if (values[2] & DP_ENHANCED_FRAME_CAP) + link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; + + return 0; +} +EXPORT_SYMBOL(drm_dp_link_probe); + +/** + * drm_dp_link_power_up() - power up a DisplayPort link + * @aux: DisplayPort AUX channel + * @link: pointer to a structure containing the link configuration + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link) +{ + u8 value; + int err; + + /* DP_SET_POWER register is only available on DPCD v1.1 and later */ + if (link->revision < 0x11) + return 0; + + err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value); + if (err < 0) + return err; + + value &= ~DP_SET_POWER_MASK; + value |= DP_SET_POWER_D0; + + err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value); + if (err < 0) + return err; + + /* + * According to the DP 1.1 specification, a "Sink Device must exit the + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink + * Control Field" (register 0x600). + */ + usleep_range(1000, 2000); + + return 0; +} +EXPORT_SYMBOL(drm_dp_link_power_up); + +/** + * drm_dp_link_configure() - configure a DisplayPort link + * @aux: DisplayPort AUX channel + * @link: pointer to a structure containing the link configuration + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link) +{ + u8 values[2]; + int err; + + values[0] = drm_dp_link_rate_to_bw_code(link->rate); + values[1] = link->num_lanes; + + if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING) + values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + + err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values)); + if (err < 0) + return err; + + return 0; +} +EXPORT_SYMBOL(drm_dp_link_configure); + +/* + * I2C-over-AUX implementation + */ + +static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_10BIT_ADDR; +} + +/* + * Transfer a single I2C-over-AUX message and handle various error conditions, + * retrying the transaction as appropriate. It is assumed that the + * aux->transfer function does not modify anything in the msg other than the + * reply field. + */ +static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + unsigned int retry; + int err; + + /* + * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device + * is required to retry at least seven times upon receiving AUX_DEFER + * before giving up the AUX transaction. + */ + for (retry = 0; retry < 7; retry++) { + err = aux->transfer(aux, msg); + if (err < 0) { + if (err == -EBUSY) + continue; + + DRM_DEBUG_KMS("transaction failed: %d\n", err); + return err; + } + + + switch (msg->reply & DP_AUX_NATIVE_REPLY_MASK) { + case DP_AUX_NATIVE_REPLY_ACK: + /* + * For I2C-over-AUX transactions this isn't enough, we + * need to check for the I2C ACK reply. + */ + break; + + case DP_AUX_NATIVE_REPLY_NACK: + DRM_DEBUG_KMS("native nack\n"); + return -EREMOTEIO; + + case DP_AUX_NATIVE_REPLY_DEFER: + DRM_DEBUG_KMS("native defer"); + /* + * We could check for I2C bit rate capabilities and if + * available adjust this interval. We could also be + * more careful with DP-to-legacy adapters where a + * long legacy cable may force very low I2C bit rates. + * + * For now just defer for long enough to hopefully be + * safe for all use-cases. + */ + usleep_range(500, 600); + continue; + + default: + DRM_ERROR("invalid native reply %#04x\n", msg->reply); + return -EREMOTEIO; + } + + switch (msg->reply & DP_AUX_I2C_REPLY_MASK) { + case DP_AUX_I2C_REPLY_ACK: + /* + * Both native ACK and I2C ACK replies received. We + * can assume the transfer was successful. + */ + if (err < msg->size) + return -EPROTO; + return 0; + + case DP_AUX_I2C_REPLY_NACK: + DRM_DEBUG_KMS("I2C nack\n"); + return -EREMOTEIO; + + case DP_AUX_I2C_REPLY_DEFER: + DRM_DEBUG_KMS("I2C defer\n"); + usleep_range(400, 500); + continue; + + default: + DRM_ERROR("invalid I2C reply %#04x\n", msg->reply); + return -EREMOTEIO; + } + } + + DRM_DEBUG_KMS("too many retries, giving up\n"); + return -EREMOTEIO; +} + +static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, + int num) +{ + struct drm_dp_aux *aux = adapter->algo_data; + unsigned int i, j; + struct drm_dp_aux_msg msg; + int err = 0; + + memset(&msg, 0, sizeof(msg)); + + for (i = 0; i < num; i++) { + msg.address = msgs[i].addr; + msg.request = (msgs[i].flags & I2C_M_RD) ? + DP_AUX_I2C_READ : + DP_AUX_I2C_WRITE; + msg.request |= DP_AUX_I2C_MOT; + /* Send a bare address packet to start the transaction. + * Zero sized messages specify an address only (bare + * address) transaction. + */ + msg.buffer = NULL; + msg.size = 0; + err = drm_dp_i2c_do_msg(aux, &msg); + if (err < 0) + break; + /* + * Many hardware implementations support FIFOs larger than a + * single byte, but it has been empirically determined that + * transferring data in larger chunks can actually lead to + * decreased performance. Therefore each message is simply + * transferred byte-by-byte. + */ + for (j = 0; j < msgs[i].len; j++) { + msg.buffer = msgs[i].buf + j; + msg.size = 1; + + err = drm_dp_i2c_do_msg(aux, &msg); + if (err < 0) + break; + } + if (err < 0) + break; + } + if (err >= 0) + err = num; + /* Send a bare address packet to close out the transaction. + * Zero sized messages specify an address only (bare + * address) transaction. + */ + msg.request &= ~DP_AUX_I2C_MOT; + msg.buffer = NULL; + msg.size = 0; + (void)drm_dp_i2c_do_msg(aux, &msg); + + return err; +} + +static const struct i2c_algorithm drm_dp_i2c_algo = { + .functionality = drm_dp_i2c_functionality, + .master_xfer = drm_dp_i2c_xfer, +}; + +/** + * drm_dp_aux_register_i2c_bus() - register an I2C adapter for I2C-over-AUX + * @aux: DisplayPort AUX channel + * + * Returns 0 on success or a negative error code on failure. + */ +int drm_dp_aux_register_i2c_bus(struct drm_dp_aux *aux) +{ + aux->ddc.algo = &drm_dp_i2c_algo; + aux->ddc.algo_data = aux; + aux->ddc.retries = 3; + + aux->ddc.class = I2C_CLASS_DDC; + aux->ddc.owner = THIS_MODULE; + aux->ddc.dev.parent = aux->dev; + aux->ddc.dev.of_node = aux->dev->of_node; + + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + sizeof(aux->ddc.name)); + + return i2c_add_adapter(&aux->ddc); +} +EXPORT_SYMBOL(drm_dp_aux_register_i2c_bus); + +/** + * drm_dp_aux_unregister_i2c_bus() - unregister an I2C-over-AUX adapter + * @aux: DisplayPort AUX channel + */ +void drm_dp_aux_unregister_i2c_bus(struct drm_dp_aux *aux) +{ + i2c_del_adapter(&aux->ddc); +} +EXPORT_SYMBOL(drm_dp_aux_unregister_i2c_bus); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 345be03..03711d0 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -286,6 +286,45 @@ static int drm_version(struct drm_device *dev, void *data, } /** + * drm_ioctl_permit - Check ioctl permissions against caller + * + * @flags: ioctl permission flags. + * @file_priv: Pointer to struct drm_file identifying the caller. + * + * Checks whether the caller is allowed to run an ioctl with the + * indicated permissions. If so, returns zero. Otherwise returns an + * error code suitable for ioctl return. + */ +static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) +{ + /* ROOT_ONLY is only for CAP_SYS_ADMIN */ + if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) + return -EACCES; + + /* AUTH is only for authenticated or render client */ + if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) && + !file_priv->authenticated)) + return -EACCES; + + /* MASTER is only for master or control clients */ + if (unlikely((flags & DRM_MASTER) && !file_priv->is_master && + !drm_is_control_client(file_priv))) + return -EACCES; + + /* Control clients must be explicitly allowed */ + if (unlikely(!(flags & DRM_CONTROL_ALLOW) && + drm_is_control_client(file_priv))) + return -EACCES; + + /* Render clients must be explicitly allowed */ + if (unlikely(!(flags & DRM_RENDER_ALLOW) && + drm_is_render_client(file_priv))) + return -EACCES; + + return 0; +} + +/** * Called whenever a process performs an ioctl on /dev/drm. * * \param inode device inode. @@ -344,65 +383,64 @@ long drm_ioctl(struct file *filp, DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, ioctl->name); /* Do not trust userspace, use our own definition */ func = ioctl->func; - if (!func) { + if (unlikely(!func)) { DRM_DEBUG("no function\n"); retcode = -EINVAL; - } else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) || - ((ioctl->flags & DRM_AUTH) && !drm_is_render_client(file_priv) && !file_priv->authenticated) || - ((ioctl->flags & DRM_MASTER) && !file_priv->is_master) || - (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL)) || - (!(ioctl->flags & DRM_RENDER_ALLOW) && drm_is_render_client(file_priv))) { - retcode = -EACCES; - } else { - if (cmd & (IOC_IN | IOC_OUT)) { - if (asize <= sizeof(stack_kdata)) { - kdata = stack_kdata; - } else { - kdata = kmalloc(asize, GFP_KERNEL); - if (!kdata) { - retcode = -ENOMEM; - goto err_i1; - } - } - if (asize > usize) - memset(kdata + usize, 0, asize - usize); - } + goto err_i1; + } - if (cmd & IOC_IN) { - if (copy_from_user(kdata, (void __user *)arg, - usize) != 0) { - retcode = -EFAULT; + retcode = drm_ioctl_permit(ioctl->flags, file_priv); + if (unlikely(retcode)) + goto err_i1; + + if (cmd & (IOC_IN | IOC_OUT)) { + if (asize <= sizeof(stack_kdata)) { + kdata = stack_kdata; + } else { + kdata = kmalloc(asize, GFP_KERNEL); + if (!kdata) { + retcode = -ENOMEM; goto err_i1; } - } else - memset(kdata, 0, usize); - - if (ioctl->flags & DRM_UNLOCKED) - retcode = func(dev, kdata, file_priv); - else { - mutex_lock(&drm_global_mutex); - retcode = func(dev, kdata, file_priv); - mutex_unlock(&drm_global_mutex); } + if (asize > usize) + memset(kdata + usize, 0, asize - usize); + } - if (cmd & IOC_OUT) { - if (copy_to_user((void __user *)arg, kdata, - usize) != 0) - retcode = -EFAULT; + if (cmd & IOC_IN) { + if (copy_from_user(kdata, (void __user *)arg, + usize) != 0) { + retcode = -EFAULT; + goto err_i1; } + } else + memset(kdata, 0, usize); + + if (ioctl->flags & DRM_UNLOCKED) + retcode = func(dev, kdata, file_priv); + else { + mutex_lock(&drm_global_mutex); + retcode = func(dev, kdata, file_priv); + mutex_unlock(&drm_global_mutex); + } + + if (cmd & IOC_OUT) { + if (copy_to_user((void __user *)arg, kdata, + usize) != 0) + retcode = -EFAULT; } err_i1: if (!ioctl) DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), file_priv->authenticated, cmd, nr); if (kdata != stack_kdata) @@ -412,3 +450,21 @@ long drm_ioctl(struct file *filp, return retcode; } EXPORT_SYMBOL(drm_ioctl); + +/** + * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags + * + * @nr: Ioctl number. + * @flags: Where to return the ioctl permission flags + */ +bool drm_ioctl_flags(unsigned int nr, unsigned int *flags) +{ + if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) || + (nr < DRM_COMMAND_BASE)) { + *flags = drm_ioctls[nr].flags; + return true; + } + + return false; +} +EXPORT_SYMBOL(drm_ioctl_flags); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b924306..d4e3f9d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1098,10 +1098,14 @@ EXPORT_SYMBOL(drm_edid_is_valid); /** * Get EDID information via I2C. * - * \param adapter : i2c device adaptor - * \param buf : EDID data buffer to be filled - * \param len : EDID data buffer length - * \return 0 on success or -1 on failure. + * @adapter : i2c device adaptor + * @buf: EDID data buffer to be filled + * @block: 128 byte EDID block to start fetching from + * @len: EDID data buffer length to fetch + * + * Returns: + * + * 0 on success or -1 on failure. * * Try to fetch EDID information by calling i2c driver function. */ @@ -1243,9 +1247,11 @@ out: /** * Probe DDC presence. + * @adapter: i2c adapter to probe + * + * Returns: * - * \param adapter : i2c device adaptor - * \return 1 on success + * 1 on success */ bool drm_probe_ddc(struct i2c_adapter *adapter) @@ -1586,8 +1592,10 @@ bad_std_timing(u8 a, u8 b) /** * drm_mode_std - convert standard mode info (width, height, refresh) into mode + * @connector: connector of for the EDID block + * @edid: EDID block to scan * @t: standard timing params - * @timing_level: standard timing level + * @revision: standard timing level * * Take the standard timing params (in this case width, aspect, and refresh) * and convert them into a real mode using CVT/GTF/DMT. @@ -2132,6 +2140,7 @@ do_established_modes(struct detailed_timing *timing, void *c) /** * add_established_modes - get est. modes from EDID and add them + * @connector: connector of for the EDID block * @edid: EDID block to scan * * Each EDID block contains a bitmap of the supported "established modes" list @@ -2194,6 +2203,7 @@ do_standard_modes(struct detailed_timing *timing, void *c) /** * add_standard_modes - get std. modes from EDID and add them + * @connector: connector of for the EDID block * @edid: EDID block to scan * * Standard modes can be calculated using the appropriate standard (DMT, @@ -2580,6 +2590,9 @@ drm_display_mode_from_vic_index(struct drm_connector *connector, return NULL; newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]); + if (!newmode) + return NULL; + newmode->vrefresh = 0; return newmode; @@ -3300,6 +3313,7 @@ EXPORT_SYMBOL(drm_detect_hdmi_monitor); /** * drm_detect_monitor_audio - check monitor audio capability + * @edid: EDID block to scan * * Monitor should have CEA extension block. * If monitor has 'basic audio', but no CEA audio blocks, it's 'basic @@ -3345,6 +3359,7 @@ EXPORT_SYMBOL(drm_detect_monitor_audio); /** * drm_rgb_quant_range_selectable - is RGB quantization range selectable? + * @edid: EDID block to scan * * Check whether the monitor reports the RGB quantization range selection * as supported. The AVI infoframe can then be used to inform the monitor @@ -3564,8 +3579,8 @@ void drm_set_preferred_mode(struct drm_connector *connector, struct drm_display_mode *mode; list_for_each_entry(mode, &connector->probed_modes, head) { - if (drm_mode_width(mode) == hpref && - drm_mode_height(mode) == vpref) + if (mode->hdisplay == hpref && + mode->vdisplay == vpref) mode->type |= DRM_MODE_TYPE_PREFERRED; } } @@ -3599,6 +3614,7 @@ drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; return 0; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 98a0363..04d3fd3 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -232,7 +232,7 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) list_for_each_entry(c, &dev->mode_config.crtc_list, head) { if (crtc->base.id == c->base.id) - return c->fb; + return c->primary->fb; } return NULL; @@ -291,7 +291,8 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) drm_warn_on_modeset_not_all_locked(dev); list_for_each_entry(plane, &dev->mode_config.plane_list, head) - drm_plane_force_disable(plane); + if (plane->type != DRM_PLANE_TYPE_PRIMARY) + drm_plane_force_disable(plane); for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; @@ -365,9 +366,9 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) return false; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb) + if (crtc->primary->fb) crtcs_bound++; - if (crtc->fb == fb_helper->fb) + if (crtc->primary->fb == fb_helper->fb) bound++; } @@ -516,6 +517,9 @@ int drm_fb_helper_init(struct drm_device *dev, struct drm_crtc *crtc; int i; + if (!max_conn_count) + return -EINVAL; + fb_helper->dev = dev; INIT_LIST_HEAD(&fb_helper->kernel_fb_list); @@ -809,8 +813,6 @@ int drm_fb_helper_set_par(struct fb_info *info) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; - int ret; - int i; if (var->pixclock != 0) { DRM_ERROR("PIXEL CLOCK SET\n"); @@ -818,13 +820,7 @@ int drm_fb_helper_set_par(struct fb_info *info) } drm_modeset_lock_all(dev); - for (i = 0; i < fb_helper->crtc_count; i++) { - ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); - if (ret) { - drm_modeset_unlock_all(dev); - return ret; - } - } + drm_fb_helper_restore_fbdev_mode(fb_helper); drm_modeset_unlock_all(dev); if (fb_helper->delayed_hotplug) { @@ -1136,19 +1132,20 @@ static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper, return count; } -static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) +struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height) { struct drm_display_mode *mode; list_for_each_entry(mode, &fb_connector->connector->modes, head) { - if (drm_mode_width(mode) > width || - drm_mode_height(mode) > height) + if (mode->hdisplay > width || + mode->vdisplay > height) continue; if (mode->type & DRM_MODE_TYPE_PREFERRED) return mode; } return NULL; } +EXPORT_SYMBOL(drm_has_preferred_mode); static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) { @@ -1157,11 +1154,12 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) return cmdline_mode->specified; } -static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, +struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, int width, int height) { struct drm_cmdline_mode *cmdline_mode; struct drm_display_mode *mode = NULL; + bool prefer_non_interlace; cmdline_mode = &fb_helper_conn->cmdline_mode; if (cmdline_mode->specified == false) @@ -1173,6 +1171,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne if (cmdline_mode->rb || cmdline_mode->margins) goto create_mode; + prefer_non_interlace = !cmdline_mode->interlace; + again: list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) { /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || @@ -1187,16 +1187,25 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne if (cmdline_mode->interlace) { if (!(mode->flags & DRM_MODE_FLAG_INTERLACE)) continue; + } else if (prefer_non_interlace) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + continue; } return mode; } + if (prefer_non_interlace) { + prefer_non_interlace = false; + goto again; + } + create_mode: mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, cmdline_mode); list_add(&mode->head, &fb_helper_conn->connector->modes); return mode; } +EXPORT_SYMBOL(drm_pick_cmdline_mode); static bool drm_connector_enabled(struct drm_connector *connector, bool strict) { @@ -1539,9 +1548,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) drm_fb_helper_parse_command_line(fb_helper); + mutex_lock(&dev->mode_config.mutex); count = drm_fb_helper_probe_connector_modes(fb_helper, dev->mode_config.max_width, dev->mode_config.max_height); + mutex_unlock(&dev->mode_config.mutex); /* * we shouldn't end up with no modes here. */ diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c index 7f2af9a..e1eba0b 100644 --- a/drivers/gpu/drm/drm_fops.c +++ b/drivers/gpu/drm/drm_fops.c @@ -39,12 +39,12 @@ #include #include -/* from BKL pushdown: note that nothing else serializes idr_find() */ +/* from BKL pushdown */ DEFINE_MUTEX(drm_global_mutex); EXPORT_SYMBOL(drm_global_mutex); static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_device * dev); + struct drm_minor *minor); static int drm_setup(struct drm_device * dev) { @@ -79,38 +79,23 @@ static int drm_setup(struct drm_device * dev) */ int drm_open(struct inode *inode, struct file *filp) { - struct drm_device *dev = NULL; - int minor_id = iminor(inode); + struct drm_device *dev; struct drm_minor *minor; - int retcode = 0; + int retcode; int need_setup = 0; - struct address_space *old_mapping; - struct address_space *old_imapping; - - minor = idr_find(&drm_minors_idr, minor_id); - if (!minor) - return -ENODEV; - if (!(dev = minor->dev)) - return -ENODEV; - - if (drm_device_is_unplugged(dev)) - return -ENODEV; + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + return PTR_ERR(minor); + dev = minor->dev; if (!dev->open_count++) need_setup = 1; - mutex_lock(&dev->struct_mutex); - old_imapping = inode->i_mapping; - old_mapping = dev->dev_mapping; - if (old_mapping == NULL) - dev->dev_mapping = &inode->i_data; - /* ihold ensures nobody can remove inode with our i_data */ - ihold(container_of(dev->dev_mapping, struct inode, i_data)); - inode->i_mapping = dev->dev_mapping; - filp->f_mapping = dev->dev_mapping; - mutex_unlock(&dev->struct_mutex); - retcode = drm_open_helper(inode, filp, dev); + /* share address_space across all char-devs of a single device */ + filp->f_mapping = dev->anon_inode->i_mapping; + + retcode = drm_open_helper(inode, filp, minor); if (retcode) goto err_undo; if (need_setup) { @@ -121,13 +106,8 @@ int drm_open(struct inode *inode, struct file *filp) return 0; err_undo: - mutex_lock(&dev->struct_mutex); - filp->f_mapping = old_imapping; - inode->i_mapping = old_imapping; - iput(container_of(dev->dev_mapping, struct inode, i_data)); - dev->dev_mapping = old_mapping; - mutex_unlock(&dev->struct_mutex); dev->open_count--; + drm_minor_release(minor); return retcode; } EXPORT_SYMBOL(drm_open); @@ -143,33 +123,30 @@ EXPORT_SYMBOL(drm_open); */ int drm_stub_open(struct inode *inode, struct file *filp) { - struct drm_device *dev = NULL; + struct drm_device *dev; struct drm_minor *minor; - int minor_id = iminor(inode); int err = -ENODEV; const struct file_operations *new_fops; DRM_DEBUG("\n"); mutex_lock(&drm_global_mutex); - minor = idr_find(&drm_minors_idr, minor_id); - if (!minor) - goto out; - - if (!(dev = minor->dev)) - goto out; - - if (drm_device_is_unplugged(dev)) - goto out; + minor = drm_minor_acquire(iminor(inode)); + if (IS_ERR(minor)) + goto out_unlock; + dev = minor->dev; new_fops = fops_get(dev->driver->fops); if (!new_fops) - goto out; + goto out_release; replace_fops(filp, new_fops); if (filp->f_op->open) err = filp->f_op->open(inode, filp); -out: + +out_release: + drm_minor_release(minor); +out_unlock: mutex_unlock(&drm_global_mutex); return err; } @@ -196,16 +173,16 @@ static int drm_cpu_valid(void) * * \param inode device inode. * \param filp file pointer. - * \param dev device. + * \param minor acquired minor-object. * \return zero on success or a negative number on failure. * * Creates and initializes a drm_file structure for the file private data in \p * filp and add it into the double linked list in \p dev. */ static int drm_open_helper(struct inode *inode, struct file *filp, - struct drm_device * dev) + struct drm_minor *minor) { - int minor_id = iminor(inode); + struct drm_device *dev = minor->dev; struct drm_file *priv; int ret; @@ -216,7 +193,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, if (dev->switch_power_state != DRM_SWITCH_POWER_ON && dev->switch_power_state != DRM_SWITCH_POWER_DYNAMIC_OFF) return -EINVAL; - DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id); + DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor->index); priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) @@ -226,11 +203,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->filp = filp; priv->uid = current_euid(); priv->pid = get_pid(task_pid(current)); - priv->minor = idr_find(&drm_minors_idr, minor_id); - if (!priv->minor) { - ret = -ENODEV; - goto out_put_pid; - } + priv->minor = minor; /* for compatibility root is always authenticated */ priv->always_authenticated = capable(CAP_SYS_ADMIN); @@ -258,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp, /* if there is no current master make this fd it, but do not create * any master object for render clients */ - mutex_lock(&dev->struct_mutex); - if (!priv->minor->master && !drm_is_render_client(priv)) { + mutex_lock(&dev->master_mutex); + if (drm_is_primary_client(priv) && !priv->minor->master) { /* create a new master */ priv->minor->master = drm_master_create(priv->minor); if (!priv->minor->master) { - mutex_unlock(&dev->struct_mutex); ret = -ENOMEM; goto out_close; } @@ -271,37 +243,31 @@ static int drm_open_helper(struct inode *inode, struct file *filp, priv->is_master = 1; /* take another reference for the copy in the local file priv */ priv->master = drm_master_get(priv->minor->master); - priv->authenticated = 1; - mutex_unlock(&dev->struct_mutex); if (dev->driver->master_create) { ret = dev->driver->master_create(dev, priv->master); if (ret) { - mutex_lock(&dev->struct_mutex); /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); - mutex_unlock(&dev->struct_mutex); goto out_close; } } - mutex_lock(&dev->struct_mutex); if (dev->driver->master_set) { ret = dev->driver->master_set(dev, priv, true); if (ret) { /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); - mutex_unlock(&dev->struct_mutex); goto out_close; } } - } else if (!drm_is_render_client(priv)) { + } else if (drm_is_primary_client(priv)) { /* get a reference to the master */ priv->master = drm_master_get(priv->minor->master); } - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&dev->master_mutex); mutex_lock(&dev->struct_mutex); list_add(&priv->lhead, &dev->filelist); @@ -319,7 +285,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, pci_dev_put(pci_dev); } if (!dev->hose) { - struct pci_bus *b = pci_bus_b(pci_root_buses.next); + struct pci_bus *b = list_entry(pci_root_buses.next, + struct pci_bus, node); if (b) dev->hose = b->sysdata; } @@ -329,6 +296,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp, return 0; out_close: + mutex_unlock(&dev->master_mutex); if (dev->driver->postclose) dev->driver->postclose(dev, priv); out_prime_destroy: @@ -336,7 +304,6 @@ out_prime_destroy: drm_prime_destroy_file_private(&priv->prime); if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, priv); -out_put_pid: put_pid(priv->pid); kfree(priv); filp->private_data = NULL; @@ -434,7 +401,6 @@ int drm_lastclose(struct drm_device * dev) drm_legacy_dma_takedown(dev); - dev->dev_mapping = NULL; mutex_unlock(&dev->struct_mutex); drm_legacy_dev_reinit(dev); @@ -458,7 +424,8 @@ int drm_lastclose(struct drm_device * dev) int drm_release(struct inode *inode, struct file *filp) { struct drm_file *file_priv = filp->private_data; - struct drm_device *dev = file_priv->minor->dev; + struct drm_minor *minor = file_priv->minor; + struct drm_device *dev = minor->dev; int retcode = 0; mutex_lock(&drm_global_mutex); @@ -474,7 +441,7 @@ int drm_release(struct inode *inode, struct file *filp) DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", task_pid_nr(current), - (long)old_encode_dev(file_priv->minor->device), + (long)old_encode_dev(file_priv->minor->kdev->devt), dev->open_count); /* Release any auth tokens that might point to this file_priv, @@ -517,11 +484,13 @@ int drm_release(struct inode *inode, struct file *filp) } mutex_unlock(&dev->ctxlist_mutex); - mutex_lock(&dev->struct_mutex); + mutex_lock(&dev->master_mutex); if (file_priv->is_master) { struct drm_master *master = file_priv->master; struct drm_file *temp; + + mutex_lock(&dev->struct_mutex); list_for_each_entry(temp, &dev->filelist, lhead) { if ((temp->master == file_priv->master) && (temp != file_priv)) @@ -540,6 +509,7 @@ int drm_release(struct inode *inode, struct file *filp) master->lock.file_priv = NULL; wake_up_interruptible_all(&master->lock.lock_queue); } + mutex_unlock(&dev->struct_mutex); if (file_priv->minor->master == file_priv->master) { /* drop the reference held my the minor */ @@ -549,13 +519,13 @@ int drm_release(struct inode *inode, struct file *filp) } } - BUG_ON(dev->dev_mapping == NULL); - iput(container_of(dev->dev_mapping, struct inode, i_data)); - - /* drop the reference held my the file priv */ + /* drop the master reference held by the file priv */ if (file_priv->master) drm_master_put(&file_priv->master); file_priv->is_master = 0; + mutex_unlock(&dev->master_mutex); + + mutex_lock(&dev->struct_mutex); list_del(&file_priv->lhead); mutex_unlock(&dev->struct_mutex); @@ -580,6 +550,8 @@ int drm_release(struct inode *inode, struct file *filp) } mutex_unlock(&drm_global_mutex); + drm_minor_release(minor); + return retcode; } EXPORT_SYMBOL(drm_release); diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 5bbad87..9909bef 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -85,9 +85,9 @@ #endif /** - * Initialize the GEM device fields + * drm_gem_init - Initialize the GEM device fields + * @dev: drm_devic structure to initialize */ - int drm_gem_init(struct drm_device *dev) { @@ -120,6 +120,11 @@ drm_gem_destroy(struct drm_device *dev) } /** + * drm_gem_object_init - initialize an allocated shmem-backed GEM object + * @dev: drm_device the object should be initialized for + * @obj: drm_gem_object to initialize + * @size: object size + * * Initialize an already allocated GEM object of the specified size with * shmfs backing store. */ @@ -141,6 +146,11 @@ int drm_gem_object_init(struct drm_device *dev, EXPORT_SYMBOL(drm_gem_object_init); /** + * drm_gem_object_init - initialize an allocated private GEM object + * @dev: drm_device the object should be initialized for + * @obj: drm_gem_object to initialize + * @size: object size + * * Initialize an already allocated GEM object of the specified size with * no GEM provided backing store. Instead the caller is responsible for * backing the object and handling it. @@ -176,6 +186,9 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) } /** + * drm_gem_object_free - release resources bound to userspace handles + * @obj: GEM object to clean up. + * * Called after the last handle to the object has been closed * * Removes any name for the object. Note that this must be @@ -225,7 +238,12 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) } /** - * Removes the mapping from handle to filp for this object. + * drm_gem_handle_delete - deletes the given file-private handle + * @filp: drm file-private structure to use for the handle look up + * @handle: userspace handle to delete + * + * Removes the GEM handle from the @filp lookup table and if this is the last + * handle also cleans up linked resources like GEM names. */ int drm_gem_handle_delete(struct drm_file *filp, u32 handle) @@ -270,6 +288,9 @@ EXPORT_SYMBOL(drm_gem_handle_delete); /** * drm_gem_dumb_destroy - dumb fb callback helper for gem based drivers + * @file: drm file-private structure to remove the dumb handle from + * @dev: corresponding drm_device + * @handle: the dumb handle to remove * * This implements the ->dumb_destroy kms driver callback for drivers which use * gem to manage their backing storage. @@ -284,6 +305,9 @@ EXPORT_SYMBOL(drm_gem_dumb_destroy); /** * drm_gem_handle_create_tail - internal functions to create a handle + * @file_priv: drm file-private structure to register the handle for + * @obj: object to register + * @handlep: pionter to return the created handle to the caller * * This expects the dev->object_name_lock to be held already and will drop it * before returning. Used to avoid races in establishing new handles when @@ -336,6 +360,11 @@ drm_gem_handle_create_tail(struct drm_file *file_priv, } /** + * gem_handle_create - create a gem handle for an object + * @file_priv: drm file-private structure to register the handle for + * @obj: object to register + * @handlep: pionter to return the created handle to the caller + * * Create a handle for this object. This adds a handle reference * to the object, which includes a regular reference count. Callers * will likely want to dereference the object afterwards. @@ -536,6 +565,11 @@ drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, EXPORT_SYMBOL(drm_gem_object_lookup); /** + * drm_gem_close_ioctl - implementation of the GEM_CLOSE ioctl + * @dev: drm_device + * @data: ioctl data + * @file_priv: drm file-private structure + * * Releases the handle to an mm object. */ int @@ -554,6 +588,11 @@ drm_gem_close_ioctl(struct drm_device *dev, void *data, } /** + * drm_gem_flink_ioctl - implementation of the GEM_FLINK ioctl + * @dev: drm_device + * @data: ioctl data + * @file_priv: drm file-private structure + * * Create a global name for an object, returning the name. * * Note that the name does not hold a reference; when the object @@ -601,6 +640,11 @@ err: } /** + * drm_gem_open - implementation of the GEM_OPEN ioctl + * @dev: drm_device + * @data: ioctl data + * @file_priv: drm file-private structure + * * Open an object using the global name, returning a handle and the size. * * This handle (of course) holds a reference to the object, so the object @@ -640,6 +684,10 @@ drm_gem_open_ioctl(struct drm_device *dev, void *data, } /** + * gem_gem_open - initalizes GEM file-private structures at devnode open time + * @dev: drm_device which is being opened by userspace + * @file_private: drm file-private structure to set up + * * Called at device open time, sets up the structure for handling refcounting * of mm objects. */ @@ -650,7 +698,7 @@ drm_gem_open(struct drm_device *dev, struct drm_file *file_private) spin_lock_init(&file_private->table_lock); } -/** +/* * Called at device close to release the file's * handle references on objects. */ @@ -674,6 +722,10 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) } /** + * drm_gem_release - release file-private GEM resources + * @dev: drm_device which is being closed by userspace + * @file_private: drm file-private structure to clean up + * * Called at close time when the filp is going away. * * Releases any remaining references on objects by this filp. @@ -692,11 +744,16 @@ drm_gem_object_release(struct drm_gem_object *obj) WARN_ON(obj->dma_buf); if (obj->filp) - fput(obj->filp); + fput(obj->filp); + + drm_gem_free_mmap_offset(obj); } EXPORT_SYMBOL(drm_gem_object_release); /** + * drm_gem_object_free - free a GEM object + * @kref: kref of the object to free + * * Called after the last reference to the object has been lost. * Must be called holding struct_ mutex * @@ -782,7 +839,7 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP; vma->vm_ops = dev->driver->gem_vm_ops; vma->vm_private_data = obj; - vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); + vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); /* Take a ref for this mapping of the object, so that the fault * handler can dereference the mmap offset's pointer to the object. @@ -818,7 +875,7 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) struct drm_device *dev = priv->minor->dev; struct drm_gem_object *obj; struct drm_vma_offset_node *node; - int ret = 0; + int ret; if (drm_device_is_unplugged(dev)) return -ENODEV; diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 6b51bf9..05c97c5 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -79,7 +79,6 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, unsigned int size) { struct drm_gem_cma_object *cma_obj; - struct sg_table *sgt = NULL; int ret; size = round_up(size, PAGE_SIZE); @@ -97,23 +96,9 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, goto error; } - sgt = kzalloc(sizeof(*cma_obj->sgt), GFP_KERNEL); - if (sgt == NULL) { - ret = -ENOMEM; - goto error; - } - - ret = dma_get_sgtable(drm->dev, sgt, cma_obj->vaddr, - cma_obj->paddr, size); - if (ret < 0) - goto error; - - cma_obj->sgt = sgt; - return cma_obj; error: - kfree(sgt); drm_gem_cma_free_object(&cma_obj->base); return ERR_PTR(ret); } @@ -175,10 +160,6 @@ void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) if (cma_obj->vaddr) { dma_free_writecombine(gem_obj->dev->dev, cma_obj->base.size, cma_obj->vaddr, cma_obj->paddr); - if (cma_obj->sgt) { - sg_free_table(cma_obj->sgt); - kfree(cma_obj->sgt); - } } else if (gem_obj->import_attach) { drm_prime_gem_destroy(gem_obj, cma_obj->sgt); } @@ -253,8 +234,17 @@ static int drm_gem_cma_mmap_obj(struct drm_gem_cma_object *cma_obj, { int ret; - ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); + /* + * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the + * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map + * the whole buffer. + */ + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_pgoff = 0; + + ret = dma_mmap_writecombine(cma_obj->base.dev->dev, vma, + cma_obj->vaddr, cma_obj->paddr, + vma->vm_end - vma->vm_start); if (ret) drm_gem_vm_close(vma); @@ -292,9 +282,9 @@ void drm_gem_cma_describe(struct drm_gem_cma_object *cma_obj, struct seq_file *m off = drm_vma_node_start(&obj->vma_node); - seq_printf(m, "%2d (%2d) %08llx %08Zx %p %d", + seq_printf(m, "%2d (%2d) %08llx %pad %p %d", obj->name, obj->refcount.refcount.counter, - off, cma_obj->paddr, cma_obj->vaddr, obj->size); + off, &cma_obj->paddr, cma_obj->vaddr, obj->size); seq_printf(m, "\n"); } @@ -342,7 +332,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, cma_obj->paddr = sg_dma_address(sgt->sgl); cma_obj->sgt = sgt; - DRM_DEBUG_PRIME("dma_addr = 0x%x, size = %zu\n", cma_obj->paddr, size); + DRM_DEBUG_PRIME("dma_addr = %pad, size = %zu\n", &cma_obj->paddr, size); return &cma_obj->base; } diff --git a/drivers/gpu/drm/drm_info.c b/drivers/gpu/drm/drm_info.c index 7473035..86feedd 100644 --- a/drivers/gpu/drm/drm_info.c +++ b/drivers/gpu/drm/drm_info.c @@ -47,18 +47,16 @@ int drm_name_info(struct seq_file *m, void *data) struct drm_minor *minor = node->minor; struct drm_device *dev = minor->dev; struct drm_master *master = minor->master; - const char *bus_name; if (!master) return 0; - bus_name = dev->driver->bus->get_name(dev); if (master->unique) { seq_printf(m, "%s %s %s\n", - bus_name, + dev->driver->name, dev_name(dev->dev), master->unique); } else { seq_printf(m, "%s %s\n", - bus_name, dev_name(dev->dev)); + dev->driver->name, dev_name(dev->dev)); } return 0; } diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index f4dc9b7..38269d5 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -72,9 +72,6 @@ static void drm_unset_busid(struct drm_device *dev, struct drm_master *master) { - kfree(dev->devname); - dev->devname = NULL; - kfree(master->unique); master->unique = NULL; master->unique_len = 0; @@ -93,7 +90,8 @@ drm_unset_busid(struct drm_device *dev, * Copies the bus id from userspace into drm_device::unique, and verifies that * it matches the device this DRM is attached to (EINVAL otherwise). Deprecated * in interface version 1.1 and will return EBUSY when setversion has requested - * version 1.1 or greater. + * version 1.1 or greater. Also note that KMS is all version 1.1 and later and + * UMS was only ever supported on pci devices. */ int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -108,10 +106,13 @@ int drm_setunique(struct drm_device *dev, void *data, if (!u->unique_len || u->unique_len > 1024) return -EINVAL; - if (!dev->driver->bus->set_unique) + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + if (WARN_ON(!dev->pdev)) return -EINVAL; - ret = dev->driver->bus->set_unique(dev, master, u); + ret = drm_pci_set_unique(dev, master, u); if (ret) goto err; @@ -328,6 +329,13 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) return -EINVAL; file_priv->stereo_allowed = req->value; break; + case DRM_CLIENT_CAP_UNIVERSAL_PLANES: + if (!drm_universal_planes) + return -EINVAL; + if (req->value > 1) + return -EINVAL; + file_priv->universal_planes = req->value; + break; default: return -EINVAL; } diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c2676b5..a0f2982 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -56,33 +56,6 @@ */ #define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 -/** - * Get interrupt from bus id. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument, pointing to a drm_irq_busid structure. - * \return zero on success or a negative number on failure. - * - * Finds the PCI device with the specified bus id and gets its IRQ number. - * This IOCTL is deprecated, and will now return EINVAL for any busid not equal - * to that of the device that this DRM instance attached to. - */ -int drm_irq_by_busid(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_irq_busid *p = data; - - if (!dev->driver->bus->irq_by_busid) - return -EINVAL; - - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return -EINVAL; - - return dev->driver->bus->irq_by_busid(dev, p); -} - /* * Clear vblank timestamp buffer for a crtc. */ @@ -269,34 +242,26 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state) * \c irq_preinstall() and \c irq_postinstall() functions * before and after the installation. */ -int drm_irq_install(struct drm_device *dev) +int drm_irq_install(struct drm_device *dev, int irq) { int ret; unsigned long sh_flags = 0; - char *irqname; if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; - if (drm_dev_to_irq(dev) == 0) + if (irq == 0) return -EINVAL; - mutex_lock(&dev->struct_mutex); - /* Driver must have been initialized */ - if (!dev->dev_private) { - mutex_unlock(&dev->struct_mutex); + if (!dev->dev_private) return -EINVAL; - } - if (dev->irq_enabled) { - mutex_unlock(&dev->struct_mutex); + if (dev->irq_enabled) return -EBUSY; - } dev->irq_enabled = true; - mutex_unlock(&dev->struct_mutex); - DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev)); + DRM_DEBUG("irq=%d\n", irq); /* Before installing handler */ if (dev->driver->irq_preinstall) @@ -306,18 +271,11 @@ int drm_irq_install(struct drm_device *dev) if (drm_core_check_feature(dev, DRIVER_IRQ_SHARED)) sh_flags = IRQF_SHARED; - if (dev->devname) - irqname = dev->devname; - else - irqname = dev->driver->name; - - ret = request_irq(drm_dev_to_irq(dev), dev->driver->irq_handler, - sh_flags, irqname, dev); + ret = request_irq(irq, dev->driver->irq_handler, + sh_flags, dev->driver->name, dev); if (ret < 0) { - mutex_lock(&dev->struct_mutex); dev->irq_enabled = false; - mutex_unlock(&dev->struct_mutex); return ret; } @@ -329,12 +287,12 @@ int drm_irq_install(struct drm_device *dev) ret = dev->driver->irq_postinstall(dev); if (ret < 0) { - mutex_lock(&dev->struct_mutex); dev->irq_enabled = false; - mutex_unlock(&dev->struct_mutex); if (!drm_core_check_feature(dev, DRIVER_MODESET)) vga_client_register(dev->pdev, NULL, NULL, NULL); - free_irq(drm_dev_to_irq(dev), dev); + free_irq(irq, dev); + } else { + dev->irq = irq; } return ret; @@ -357,10 +315,8 @@ int drm_irq_uninstall(struct drm_device *dev) if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) return -EINVAL; - mutex_lock(&dev->struct_mutex); irq_enabled = dev->irq_enabled; dev->irq_enabled = false; - mutex_unlock(&dev->struct_mutex); /* * Wake up any waiters so they don't hang. @@ -379,7 +335,7 @@ int drm_irq_uninstall(struct drm_device *dev) if (!irq_enabled) return -EINVAL; - DRM_DEBUG("irq=%d\n", drm_dev_to_irq(dev)); + DRM_DEBUG("irq=%d\n", dev->irq); if (!drm_core_check_feature(dev, DRIVER_MODESET)) vga_client_register(dev->pdev, NULL, NULL, NULL); @@ -387,7 +343,7 @@ int drm_irq_uninstall(struct drm_device *dev) if (dev->driver->irq_uninstall) dev->driver->irq_uninstall(dev); - free_irq(drm_dev_to_irq(dev), dev); + free_irq(dev->irq, dev); return 0; } @@ -408,28 +364,38 @@ int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_control *ctl = data; + int ret = 0, irq; /* if we haven't irq we fallback for compatibility reasons - * this used to be a separate function in drm_dma.h */ + if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) + return 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + /* UMS was only ever support on pci devices. */ + if (WARN_ON(!dev->pdev)) + return -EINVAL; switch (ctl->func) { case DRM_INST_HANDLER: - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; + irq = dev->pdev->irq; + if (dev->if_version < DRM_IF_VERSION(1, 2) && - ctl->irq != drm_dev_to_irq(dev)) + ctl->irq != irq) return -EINVAL; - return drm_irq_install(dev); + mutex_lock(&dev->struct_mutex); + ret = drm_irq_install(dev, irq); + mutex_unlock(&dev->struct_mutex); + + return ret; case DRM_UNINST_HANDLER: - if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - return 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) - return 0; - return drm_irq_uninstall(dev); + mutex_lock(&dev->struct_mutex); + ret = drm_irq_uninstall(dev); + mutex_unlock(&dev->struct_mutex); + + return ret; default: return -EINVAL; } @@ -1160,9 +1126,8 @@ int drm_wait_vblank(struct drm_device *dev, void *data, int ret; unsigned int flags, seq, crtc, high_crtc; - if (drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) - if ((!drm_dev_to_irq(dev)) || (!dev->irq_enabled)) - return -EINVAL; + if (!dev->irq_enabled) + return -EINVAL; if (vblwait->request.type & _DRM_VBLANK_SIGNAL) return -EINVAL; diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c index b155ee2..09821f4 100644 --- a/drivers/gpu/drm/drm_mipi_dsi.c +++ b/drivers/gpu/drm/drm_mipi_dsi.c @@ -142,8 +142,12 @@ int mipi_dsi_host_register(struct mipi_dsi_host *host) { struct device_node *node; - for_each_available_child_of_node(host->dev->of_node, node) + for_each_available_child_of_node(host->dev->of_node, node) { + /* skip nodes without reg property */ + if (!of_find_property(node, "reg", NULL)) + continue; of_mipi_dsi_device_add(host, node); + } return 0; } diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c index af93cc5..04a209e 100644 --- a/drivers/gpu/drm/drm_mm.c +++ b/drivers/gpu/drm/drm_mm.c @@ -47,7 +47,48 @@ #include #include -#define MM_UNUSED_TARGET 4 +/** + * DOC: Overview + * + * drm_mm provides a simple range allocator. The drivers are free to use the + * resource allocator from the linux core if it suits them, the upside of drm_mm + * is that it's in the DRM core. Which means that it's easier to extend for + * some of the crazier special purpose needs of gpus. + * + * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node. + * Drivers are free to embed either of them into their own suitable + * datastructures. drm_mm itself will not do any allocations of its own, so if + * drivers choose not to embed nodes they need to still allocate them + * themselves. + * + * The range allocator also supports reservation of preallocated blocks. This is + * useful for taking over initial mode setting configurations from the firmware, + * where an object needs to be created which exactly matches the firmware's + * scanout target. As long as the range is still free it can be inserted anytime + * after the allocator is initialized, which helps with avoiding looped + * depencies in the driver load sequence. + * + * drm_mm maintains a stack of most recently freed holes, which of all + * simplistic datastructures seems to be a fairly decent approach to clustering + * allocations and avoiding too much fragmentation. This means free space + * searches are O(num_holes). Given that all the fancy features drm_mm supports + * something better would be fairly complex and since gfx thrashing is a fairly + * steep cliff not a real concern. Removing a node again is O(1). + * + * drm_mm supports a few features: Alignment and range restrictions can be + * supplied. Further more every &drm_mm_node has a color value (which is just an + * opaqua unsigned long) which in conjunction with a driver callback can be used + * to implement sophisticated placement restrictions. The i915 DRM driver uses + * this to implement guard pages between incompatible caching domains in the + * graphics TT. + * + * Two behaviors are supported for searching and allocating: bottom-up and top-down. + * The default is bottom-up. Top-down allocation can be used if the memory area + * has different restrictions, or just to reduce fragmentation. + * + * Finally iteration helpers to walk all nodes and all holes are provided as are + * some basic allocator dumpers for debugging. + */ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, unsigned long size, @@ -65,7 +106,8 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, - unsigned long color) + unsigned long color, + enum drm_mm_allocator_flags flags) { struct drm_mm *mm = hole_node->mm; unsigned long hole_start = drm_mm_hole_node_start(hole_node); @@ -78,12 +120,22 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, if (mm->color_adjust) mm->color_adjust(hole_node, color, &adj_start, &adj_end); + if (flags & DRM_MM_CREATE_TOP) + adj_start = adj_end - size; + if (alignment) { unsigned tmp = adj_start % alignment; - if (tmp) - adj_start += alignment - tmp; + if (tmp) { + if (flags & DRM_MM_CREATE_TOP) + adj_start -= tmp; + else + adj_start += alignment - tmp; + } } + BUG_ON(adj_start < hole_start); + BUG_ON(adj_end > hole_end); + if (adj_start == hole_start) { hole_node->hole_follows = 0; list_del(&hole_node->hole_stack); @@ -107,6 +159,20 @@ static void drm_mm_insert_helper(struct drm_mm_node *hole_node, } } +/** + * drm_mm_reserve_node - insert an pre-initialized node + * @mm: drm_mm allocator to insert @node into + * @node: drm_mm_node to insert + * + * This functions inserts an already set-up drm_mm_node into the allocator, + * meaning that start, size and color must be set by the caller. This is useful + * to initialize the allocator with preallocated objects which must be set-up + * before the range allocator can be set-up, e.g. when taking over a firmware + * framebuffer. + * + * Returns: + * 0 on success, -ENOSPC if there's no hole where @node is. + */ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) { struct drm_mm_node *hole; @@ -141,30 +207,39 @@ int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) return 0; } - WARN(1, "no hole found for node 0x%lx + 0x%lx\n", - node->start, node->size); return -ENOSPC; } EXPORT_SYMBOL(drm_mm_reserve_node); /** - * Search for free space and insert a preallocated memory node. Returns - * -ENOSPC if no suitable free area is available. The preallocated memory node - * must be cleared. + * drm_mm_insert_node_generic - search for space and insert @node + * @mm: drm_mm to allocate from + * @node: preallocate node to insert + * @size: size of the allocation + * @alignment: alignment of the allocation + * @color: opaque tag value to use for this node + * @sflags: flags to fine-tune the allocation search + * @aflags: flags to fine-tune the allocation behavior + * + * The preallocated node must be cleared to 0. + * + * Returns: + * 0 on success, -ENOSPC if there's no suitable hole. */ int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, unsigned long size, unsigned alignment, unsigned long color, - enum drm_mm_search_flags flags) + enum drm_mm_search_flags sflags, + enum drm_mm_allocator_flags aflags) { struct drm_mm_node *hole_node; hole_node = drm_mm_search_free_generic(mm, size, alignment, - color, flags); + color, sflags); if (!hole_node) return -ENOSPC; - drm_mm_insert_helper(hole_node, node, size, alignment, color); + drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags); return 0; } EXPORT_SYMBOL(drm_mm_insert_node_generic); @@ -173,7 +248,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, struct drm_mm_node *node, unsigned long size, unsigned alignment, unsigned long color, - unsigned long start, unsigned long end) + unsigned long start, unsigned long end, + enum drm_mm_allocator_flags flags) { struct drm_mm *mm = hole_node->mm; unsigned long hole_start = drm_mm_hole_node_start(hole_node); @@ -188,13 +264,20 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, if (adj_end > end) adj_end = end; + if (flags & DRM_MM_CREATE_TOP) + adj_start = adj_end - size; + if (mm->color_adjust) mm->color_adjust(hole_node, color, &adj_start, &adj_end); if (alignment) { unsigned tmp = adj_start % alignment; - if (tmp) - adj_start += alignment - tmp; + if (tmp) { + if (flags & DRM_MM_CREATE_TOP) + adj_start -= tmp; + else + adj_start += alignment - tmp; + } } if (adj_start == hole_start) { @@ -211,6 +294,8 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, INIT_LIST_HEAD(&node->hole_stack); list_add(&node->node_list, &hole_node->node_list); + BUG_ON(node->start < start); + BUG_ON(node->start < adj_start); BUG_ON(node->start + node->size > adj_end); BUG_ON(node->start + node->size > end); @@ -222,32 +307,51 @@ static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node, } /** - * Search for free space and insert a preallocated memory node. Returns - * -ENOSPC if no suitable free area is available. This is for range - * restricted allocations. The preallocated memory node must be cleared. + * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node + * @mm: drm_mm to allocate from + * @node: preallocate node to insert + * @size: size of the allocation + * @alignment: alignment of the allocation + * @color: opaque tag value to use for this node + * @start: start of the allowed range for this node + * @end: end of the allowed range for this node + * @sflags: flags to fine-tune the allocation search + * @aflags: flags to fine-tune the allocation behavior + * + * The preallocated node must be cleared to 0. + * + * Returns: + * 0 on success, -ENOSPC if there's no suitable hole. */ int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, - unsigned long size, unsigned alignment, unsigned long color, + unsigned long size, unsigned alignment, + unsigned long color, unsigned long start, unsigned long end, - enum drm_mm_search_flags flags) + enum drm_mm_search_flags sflags, + enum drm_mm_allocator_flags aflags) { struct drm_mm_node *hole_node; hole_node = drm_mm_search_free_in_range_generic(mm, size, alignment, color, - start, end, flags); + start, end, sflags); if (!hole_node) return -ENOSPC; drm_mm_insert_helper_range(hole_node, node, size, alignment, color, - start, end); + start, end, aflags); return 0; } EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); /** - * Remove a memory node from the allocator. + * drm_mm_remove_node - Remove a memory node from the allocator. + * @node: drm_mm_node to remove + * + * This just removes a node from its drm_mm allocator. The node does not need to + * be cleared again before it can be re-inserted into this or any other drm_mm + * allocator. It is a bug to call this function on a un-allocated node. */ void drm_mm_remove_node(struct drm_mm_node *node) { @@ -315,7 +419,10 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, best = NULL; best_size = ~0UL; - drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { + __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, + flags & DRM_MM_SEARCH_BELOW) { + unsigned long hole_size = adj_end - adj_start; + if (mm->color_adjust) { mm->color_adjust(entry, color, &adj_start, &adj_end); if (adj_end <= adj_start) @@ -328,9 +435,9 @@ static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, if (!(flags & DRM_MM_SEARCH_BEST)) return entry; - if (entry->size < best_size) { + if (hole_size < best_size) { best = entry; - best_size = entry->size; + best_size = hole_size; } } @@ -356,7 +463,10 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ best = NULL; best_size = ~0UL; - drm_mm_for_each_hole(entry, mm, adj_start, adj_end) { + __drm_mm_for_each_hole(entry, mm, adj_start, adj_end, + flags & DRM_MM_SEARCH_BELOW) { + unsigned long hole_size = adj_end - adj_start; + if (adj_start < start) adj_start = start; if (adj_end > end) @@ -374,9 +484,9 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ if (!(flags & DRM_MM_SEARCH_BEST)) return entry; - if (entry->size < best_size) { + if (hole_size < best_size) { best = entry; - best_size = entry->size; + best_size = hole_size; } } @@ -384,7 +494,13 @@ static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_ } /** - * Moves an allocation. To be used with embedded struct drm_mm_node. + * drm_mm_replace_node - move an allocation from @old to @new + * @old: drm_mm_node to remove from the allocator + * @new: drm_mm_node which should inherit @old's allocation + * + * This is useful for when drivers embed the drm_mm_node structure and hence + * can't move allocations by reassigning pointers. It's a combination of remove + * and insert with the guarantee that the allocation start will match. */ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) { @@ -402,12 +518,46 @@ void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) EXPORT_SYMBOL(drm_mm_replace_node); /** - * Initializa lru scanning. + * DOC: lru scan roaster + * + * Very often GPUs need to have continuous allocations for a given object. When + * evicting objects to make space for a new one it is therefore not most + * efficient when we simply start to select all objects from the tail of an LRU + * until there's a suitable hole: Especially for big objects or nodes that + * otherwise have special allocation constraints there's a good chance we evict + * lots of (smaller) objects unecessarily. + * + * The DRM range allocator supports this use-case through the scanning + * interfaces. First a scan operation needs to be initialized with + * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds + * objects to the roaster (probably by walking an LRU list, but this can be + * freely implemented) until a suitable hole is found or there's no further + * evitable object. + * + * The the driver must walk through all objects again in exactly the reverse + * order to restore the allocator state. Note that while the allocator is used + * in the scan mode no other operation is allowed. + * + * Finally the driver evicts all objects selected in the scan. Adding and + * removing an object is O(1), and since freeing a node is also O(1) the overall + * complexity is O(scanned_objects). So like the free stack which needs to be + * walked before a scan operation even begins this is linear in the number of + * objects. It doesn't seem to hurt badly. + */ + +/** + * drm_mm_init_scan - initialize lru scanning + * @mm: drm_mm to scan + * @size: size of the allocation + * @alignment: alignment of the allocation + * @color: opaque tag value to use for the allocation * * This simply sets up the scanning routines with the parameters for the desired - * hole. + * hole. Note that there's no need to specify allocation flags, since they only + * change the place a node is allocated from within a suitable hole. * - * Warning: As long as the scan list is non-empty, no other operations than + * Warning: + * As long as the scan list is non-empty, no other operations than * adding/removing nodes to/from the scan list are allowed. */ void drm_mm_init_scan(struct drm_mm *mm, @@ -427,12 +577,20 @@ void drm_mm_init_scan(struct drm_mm *mm, EXPORT_SYMBOL(drm_mm_init_scan); /** - * Initializa lru scanning. + * drm_mm_init_scan - initialize range-restricted lru scanning + * @mm: drm_mm to scan + * @size: size of the allocation + * @alignment: alignment of the allocation + * @color: opaque tag value to use for the allocation + * @start: start of the allowed range for the allocation + * @end: end of the allowed range for the allocation * * This simply sets up the scanning routines with the parameters for the desired - * hole. This version is for range-restricted scans. + * hole. Note that there's no need to specify allocation flags, since they only + * change the place a node is allocated from within a suitable hole. * - * Warning: As long as the scan list is non-empty, no other operations than + * Warning: + * As long as the scan list is non-empty, no other operations than * adding/removing nodes to/from the scan list are allowed. */ void drm_mm_init_scan_with_range(struct drm_mm *mm, @@ -456,12 +614,16 @@ void drm_mm_init_scan_with_range(struct drm_mm *mm, EXPORT_SYMBOL(drm_mm_init_scan_with_range); /** + * drm_mm_scan_add_block - add a node to the scan list + * @node: drm_mm_node to add + * * Add a node to the scan list that might be freed to make space for the desired * hole. * - * Returns non-zero, if a hole has been found, zero otherwise. + * Returns: + * True if a hole has been found, false otherwise. */ -int drm_mm_scan_add_block(struct drm_mm_node *node) +bool drm_mm_scan_add_block(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; @@ -501,15 +663,16 @@ int drm_mm_scan_add_block(struct drm_mm_node *node) mm->scan_size, mm->scan_alignment)) { mm->scan_hit_start = hole_start; mm->scan_hit_end = hole_end; - return 1; + return true; } - return 0; + return false; } EXPORT_SYMBOL(drm_mm_scan_add_block); /** - * Remove a node from the scan list. + * drm_mm_scan_remove_block - remove a node from the scan list + * @node: drm_mm_node to remove * * Nodes _must_ be removed in the exact same order from the scan list as they * have been added, otherwise the internal state of the memory manager will be @@ -519,10 +682,11 @@ EXPORT_SYMBOL(drm_mm_scan_add_block); * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then * return the just freed block (because its at the top of the free_stack list). * - * Returns one if this block should be evicted, zero otherwise. Will always - * return zero when no hole has been found. + * Returns: + * True if this block should be evicted, false otherwise. Will always + * return false when no hole has been found. */ -int drm_mm_scan_remove_block(struct drm_mm_node *node) +bool drm_mm_scan_remove_block(struct drm_mm_node *node) { struct drm_mm *mm = node->mm; struct drm_mm_node *prev_node; @@ -543,7 +707,15 @@ int drm_mm_scan_remove_block(struct drm_mm_node *node) } EXPORT_SYMBOL(drm_mm_scan_remove_block); -int drm_mm_clean(struct drm_mm * mm) +/** + * drm_mm_clean - checks whether an allocator is clean + * @mm: drm_mm allocator to check + * + * Returns: + * True if the allocator is completely free, false if there's still a node + * allocated in it. + */ +bool drm_mm_clean(struct drm_mm * mm) { struct list_head *head = &mm->head_node.node_list; @@ -551,6 +723,14 @@ int drm_mm_clean(struct drm_mm * mm) } EXPORT_SYMBOL(drm_mm_clean); +/** + * drm_mm_init - initialize a drm-mm allocator + * @mm: the drm_mm structure to initialize + * @start: start of the range managed by @mm + * @size: end of the range managed by @mm + * + * Note that @mm must be cleared to 0 before calling this function. + */ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) { INIT_LIST_HEAD(&mm->hole_stack); @@ -572,6 +752,13 @@ void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) } EXPORT_SYMBOL(drm_mm_init); +/** + * drm_mm_takedown - clean up a drm_mm allocator + * @mm: drm_mm allocator to clean up + * + * Note that it is a bug to call this function on an allocator which is not + * clean. + */ void drm_mm_takedown(struct drm_mm * mm) { WARN(!list_empty(&mm->head_node.node_list), @@ -597,6 +784,11 @@ static unsigned long drm_mm_debug_hole(struct drm_mm_node *entry, return 0; } +/** + * drm_mm_debug_table - dump allocator state to dmesg + * @mm: drm_mm allocator to dump + * @prefix: prefix to use for dumping to dmesg + */ void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) { struct drm_mm_node *entry; @@ -635,6 +827,11 @@ static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *en return 0; } +/** + * drm_mm_dump_table - dump allocator state to a seq_file + * @m: seq_file to dump to + * @mm: drm_mm allocator to dump + */ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) { struct drm_mm_node *entry; diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index b073315..bedf189 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -37,15 +37,14 @@ #include #include