#include <linux/dma-iommu.h>
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
+#include <drm/drm_sync_helper.h>
+#include <drm/rockchip_drm.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
+#include <linux/memblock.h>
#include <linux/module.h>
+#include <linux/of_address.h>
#include <linux/of_graph.h>
#include <linux/component.h>
-
-#include <drm/rockchip_drm.h>
+#include <linux/fence.h>
+#include <linux/console.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
#include "rockchip_drm_fbdev.h"
#include "rockchip_drm_gem.h"
+#include "rockchip_drm_rga.h"
#define DRIVER_NAME "rockchip"
#define DRIVER_DESC "RockChip Soc DRM"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
+static LIST_HEAD(rockchip_drm_subdrv_list);
+static DEFINE_MUTEX(subdrv_list_mutex);
+
+struct rockchip_drm_mode_set {
+ struct list_head head;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ struct drm_display_mode *mode;
+ int hdisplay;
+ int vdisplay;
+ int vrefresh;
+
+ bool mode_changed;
+ bool ymirror;
+ int ratio;
+};
+
+static struct drm_crtc *find_crtc_by_node(struct drm_device *drm_dev,
+ struct device_node *node)
+{
+ struct device_node *np_crtc;
+ struct drm_crtc *crtc;
+
+ np_crtc = of_get_parent(node);
+ if (!np_crtc || !of_device_is_available(np_crtc))
+ return NULL;
+
+ drm_for_each_crtc(crtc, drm_dev) {
+ if (crtc->port == np_crtc)
+ return crtc;
+ }
+
+ return NULL;
+}
+
+static struct drm_connector *find_connector_by_node(struct drm_device *drm_dev,
+ struct device_node *node)
+{
+ struct device_node *np_connector;
+ struct drm_connector *connector;
+
+ np_connector = of_graph_get_remote_port_parent(node);
+ if (!np_connector || !of_device_is_available(np_connector))
+ return NULL;
+
+ drm_for_each_connector(connector, drm_dev) {
+ if (connector->port == np_connector)
+ return connector;
+ }
+
+ return NULL;
+}
+
+static int init_loader_memory(struct drm_device *drm_dev)
+{
+ struct rockchip_drm_private *private = drm_dev->dev_private;
+ struct rockchip_logo *logo;
+ struct device_node *np = drm_dev->dev->of_node;
+ struct device_node *node;
+ unsigned long nr_pages;
+ struct page **pages;
+ struct sg_table *sgt;
+ DEFINE_DMA_ATTRS(attrs);
+ phys_addr_t start, size;
+ struct resource res;
+ int i, ret;
+
+ logo = devm_kmalloc(drm_dev->dev, sizeof(*logo), GFP_KERNEL);
+ if (!logo)
+ return -ENOMEM;
+
+ node = of_parse_phandle(np, "memory-region", 0);
+ if (!node)
+ return -ENOMEM;
+
+ ret = of_address_to_resource(node, 0, &res);
+ if (ret)
+ return ret;
+ start = res.start;
+ size = resource_size(&res);
+ if (!size)
+ return -ENOMEM;
+
+ nr_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ pages = kmalloc_array(nr_pages, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return -ENOMEM;
+ i = 0;
+ while (i < nr_pages) {
+ pages[i] = phys_to_page(start);
+ start += PAGE_SIZE;
+ i++;
+ }
+ sgt = drm_prime_pages_to_sg(pages, nr_pages);
+ if (IS_ERR(sgt)) {
+ kfree(pages);
+ return PTR_ERR(sgt);
+ }
+
+ dma_set_attr(DMA_ATTR_SKIP_CPU_SYNC, &attrs);
+ dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &attrs);
+ dma_map_sg_attrs(drm_dev->dev, sgt->sgl, sgt->nents,
+ DMA_TO_DEVICE, &attrs);
+ logo->dma_addr = sg_dma_address(sgt->sgl);
+ logo->sgt = sgt;
+ logo->start = res.start;
+ logo->size = size;
+ logo->count = 0;
+ private->logo = logo;
+
+ return 0;
+}
+
+static struct drm_framebuffer *
+get_framebuffer_by_node(struct drm_device *drm_dev, struct device_node *node)
+{
+ struct rockchip_drm_private *private = drm_dev->dev_private;
+ struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+ u32 val;
+ int bpp;
+
+ if (WARN_ON(!private->logo))
+ return NULL;
+
+ if (of_property_read_u32(node, "logo,offset", &val)) {
+ pr_err("%s: failed to get logo,offset\n", __func__);
+ return NULL;
+ }
+ mode_cmd.offsets[0] = val;
+
+ if (of_property_read_u32(node, "logo,width", &val)) {
+ pr_err("%s: failed to get logo,width\n", __func__);
+ return NULL;
+ }
+ mode_cmd.width = val;
+
+ if (of_property_read_u32(node, "logo,height", &val)) {
+ pr_err("%s: failed to get logo,height\n", __func__);
+ return NULL;
+ }
+ mode_cmd.height = val;
+
+ if (of_property_read_u32(node, "logo,bpp", &val)) {
+ pr_err("%s: failed to get logo,bpp\n", __func__);
+ return NULL;
+ }
+ bpp = val;
+
+ mode_cmd.pitches[0] = mode_cmd.width * bpp / 8;
+
+ switch (bpp) {
+ case 16:
+ mode_cmd.pixel_format = DRM_FORMAT_BGR565;
+ break;
+ case 24:
+ mode_cmd.pixel_format = DRM_FORMAT_BGR888;
+ break;
+ case 32:
+ mode_cmd.pixel_format = DRM_FORMAT_XBGR8888;
+ break;
+ default:
+ pr_err("%s: unsupport to logo bpp %d\n", __func__, bpp);
+ return NULL;
+ }
+
+ return rockchip_fb_alloc(drm_dev, &mode_cmd, NULL, private->logo, 1);
+}
+
+static struct rockchip_drm_mode_set *
+of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route)
+{
+ struct rockchip_drm_mode_set *set;
+ struct device_node *connect;
+ struct drm_framebuffer *fb;
+ struct drm_connector *connector;
+ struct drm_crtc *crtc;
+ u32 val;
+
+ connect = of_parse_phandle(route, "connect", 0);
+ if (!connect)
+ return NULL;
+
+ fb = get_framebuffer_by_node(drm_dev, route);
+ if (IS_ERR_OR_NULL(fb))
+ return NULL;
+
+ crtc = find_crtc_by_node(drm_dev, connect);
+ connector = find_connector_by_node(drm_dev, connect);
+ if (!crtc || !connector) {
+ dev_warn(drm_dev->dev,
+ "No available crtc or connector for display");
+ drm_framebuffer_unreference(fb);
+ return NULL;
+ }
+
+ set = kzalloc(sizeof(*set), GFP_KERNEL);
+ if (!set)
+ return NULL;
+
+ if (!of_property_read_u32(route, "video,hdisplay", &val))
+ set->hdisplay = val;
+
+ if (!of_property_read_u32(route, "video,vdisplay", &val))
+ set->vdisplay = val;
+
+ if (!of_property_read_u32(route, "video,vrefresh", &val))
+ set->vrefresh = val;
+
+ if (!of_property_read_u32(route, "logo,ymirror", &val))
+ set->ymirror = val;
+
+ set->fb = fb;
+ set->crtc = crtc;
+ set->connector = connector;
+ /* TODO: set display fullscreen or center */
+ set->ratio = 0;
+
+ return set;
+}
+
+int setup_initial_state(struct drm_device *drm_dev,
+ struct drm_atomic_state *state,
+ struct rockchip_drm_mode_set *set)
+{
+ struct drm_connector *connector = set->connector;
+ struct drm_crtc *crtc = set->crtc;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ struct drm_plane_state *primary_state;
+ struct drm_display_mode *mode = NULL;
+ const struct drm_connector_helper_funcs *funcs;
+ bool is_crtc_enabled = true;
+ int hdisplay, vdisplay;
+ int fb_width, fb_height;
+ int found = 0, match = 0;
+ int num_modes;
+ int ret = 0;
+
+ if (!set->hdisplay || !set->vdisplay || !set->vrefresh)
+ is_crtc_enabled = false;
+
+ conn_state = drm_atomic_get_connector_state(state, connector);
+ if (IS_ERR(conn_state))
+ return PTR_ERR(conn_state);
+
+ funcs = connector->helper_private;
+ conn_state->best_encoder = funcs->best_encoder(connector);
+ num_modes = connector->funcs->fill_modes(connector, 4096, 4096);
+ if (!num_modes) {
+ dev_err(drm_dev->dev, "connector[%s] can't found any modes\n",
+ connector->name);
+ return -EINVAL;
+ }
+
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (mode->hdisplay == set->hdisplay &&
+ mode->vdisplay == set->vdisplay &&
+ drm_mode_vrefresh(mode) == set->vrefresh) {
+ found = 1;
+ match = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ mode = list_first_entry_or_null(&connector->modes,
+ struct drm_display_mode,
+ head);
+ if (!mode) {
+ pr_err("failed to find available modes\n");
+ return -EINVAL;
+ }
+ }
+ }
+
+ set->mode = mode;
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+
+ drm_mode_copy(&crtc_state->adjusted_mode, mode);
+ if (!match || !is_crtc_enabled) {
+ set->mode_changed = true;
+ } else {
+ ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret)
+ return ret;
+
+ crtc_state->active = true;
+ }
+
+ if (!set->fb)
+ return 0;
+ primary_state = drm_atomic_get_plane_state(state, crtc->primary);
+ if (IS_ERR(primary_state))
+ return PTR_ERR(primary_state);
+
+ hdisplay = mode->hdisplay;
+ vdisplay = mode->vdisplay;
+ fb_width = set->fb->width;
+ fb_height = set->fb->height;
+
+ primary_state->crtc = crtc;
+ primary_state->src_x = 0;
+ primary_state->src_y = 0;
+ primary_state->src_w = fb_width << 16;
+ primary_state->src_h = fb_height << 16;
+ if (set->ratio) {
+ if (set->fb->width >= hdisplay) {
+ primary_state->crtc_x = 0;
+ primary_state->crtc_w = hdisplay;
+ } else {
+ primary_state->crtc_x = (hdisplay - fb_width) / 2;
+ primary_state->crtc_w = set->fb->width;
+ }
+
+ if (set->fb->height >= vdisplay) {
+ primary_state->crtc_y = 0;
+ primary_state->crtc_h = vdisplay;
+ } else {
+ primary_state->crtc_y = (vdisplay - fb_height) / 2;
+ primary_state->crtc_h = fb_height;
+ }
+ } else {
+ primary_state->crtc_x = 0;
+ primary_state->crtc_y = 0;
+ primary_state->crtc_w = hdisplay;
+ primary_state->crtc_h = vdisplay;
+ }
+
+ return 0;
+}
+
+static int update_state(struct drm_device *drm_dev,
+ struct drm_atomic_state *state,
+ struct rockchip_drm_mode_set *set,
+ unsigned int *plane_mask)
+{
+ struct drm_mode_config *mode_config = &drm_dev->mode_config;
+ struct drm_crtc *crtc = set->crtc;
+ struct drm_connector *connector = set->connector;
+ struct drm_display_mode *mode = set->mode;
+ struct drm_plane_state *primary_state;
+ struct drm_crtc_state *crtc_state;
+ struct drm_connector_state *conn_state;
+ int ret;
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+ conn_state = drm_atomic_get_connector_state(state, connector);
+ if (IS_ERR(conn_state))
+ return PTR_ERR(conn_state);
+
+ if (set->mode_changed) {
+ ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
+ if (ret)
+ return ret;
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, mode);
+ if (ret)
+ return ret;
+
+ crtc_state->active = true;
+ } else {
+ const struct drm_crtc_helper_funcs *funcs;
+
+ funcs = crtc->helper_private;
+ if (!funcs || !funcs->enable)
+ return -ENXIO;
+ funcs->enable(crtc);
+ }
+
+ primary_state = drm_atomic_get_plane_state(state, crtc->primary);
+ if (IS_ERR(primary_state))
+ return PTR_ERR(primary_state);
+
+ crtc_state->plane_mask = 1 << drm_plane_index(crtc->primary);
+ *plane_mask |= crtc_state->plane_mask;
+
+ drm_atomic_set_fb_for_plane(primary_state, set->fb);
+ drm_framebuffer_unreference(set->fb);
+ ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
+
+ if (set->ymirror)
+ /*
+ * TODO:
+ * some vop maybe not support ymirror, but force use it now.
+ */
+ drm_atomic_plane_set_property(crtc->primary, primary_state,
+ mode_config->rotation_property,
+ BIT(DRM_REFLECT_Y));
+
+ return ret;
+}
+
+static void show_loader_logo(struct drm_device *drm_dev)
+{
+ struct drm_atomic_state *state;
+ struct device_node *np = drm_dev->dev->of_node;
+ struct drm_mode_config *mode_config = &drm_dev->mode_config;
+ struct device_node *root, *route;
+ struct rockchip_drm_mode_set *set, *tmp;
+ struct list_head mode_set_list;
+ unsigned plane_mask = 0;
+ int ret;
+
+ root = of_get_child_by_name(np, "route");
+ if (!root) {
+ dev_warn(drm_dev->dev, "failed to parse display resources\n");
+ return;
+ }
+
+ if (init_loader_memory(drm_dev)) {
+ dev_warn(drm_dev->dev, "failed to parse loader memory\n");
+ return;
+ }
+
+ INIT_LIST_HEAD(&mode_set_list);
+ drm_modeset_lock_all(drm_dev);
+ state = drm_atomic_state_alloc(drm_dev);
+ if (!state) {
+ dev_err(drm_dev->dev, "failed to alloc atomic state\n");
+ ret = -ENOMEM;
+ goto err_unlock;
+ }
+
+ state->acquire_ctx = mode_config->acquire_ctx;
+
+ for_each_child_of_node(root, route) {
+ set = of_parse_display_resource(drm_dev, route);
+ if (!set)
+ continue;
+
+ if (setup_initial_state(drm_dev, state, set)) {
+ drm_framebuffer_unreference(set->fb);
+ kfree(set);
+ continue;
+ }
+ INIT_LIST_HEAD(&set->head);
+ list_add_tail(&set->head, &mode_set_list);
+ }
+
+ if (list_empty(&mode_set_list)) {
+ dev_warn(drm_dev->dev, "can't not find any loader display\n");
+ ret = -ENXIO;
+ goto err_free_state;
+ }
+
+ /*
+ * The state save initial devices status, swap the state into
+ * drm deivces as old state, so if new state come, can compare
+ * with this state to judge which status need to update.
+ */
+ drm_atomic_helper_swap_state(drm_dev, state);
+ drm_atomic_state_free(state);
+ state = drm_atomic_helper_duplicate_state(drm_dev,
+ mode_config->acquire_ctx);
+ if (IS_ERR(state)) {
+ dev_err(drm_dev->dev, "failed to duplicate atomic state\n");
+ ret = PTR_ERR_OR_ZERO(state);
+ goto err_unlock;
+ }
+ state->acquire_ctx = mode_config->acquire_ctx;
+ list_for_each_entry(set, &mode_set_list, head)
+ /*
+ * We don't want to see any fail on update_state.
+ */
+ WARN_ON(update_state(drm_dev, state, set, &plane_mask));
+
+ ret = drm_atomic_commit(state);
+ drm_atomic_clean_old_fb(drm_dev, plane_mask, ret);
+
+ list_for_each_entry_safe(set, tmp, &mode_set_list, head) {
+ struct drm_crtc *crtc = set->crtc;
+
+ list_del(&set->head);
+ kfree(set);
+
+ /* FIXME:
+ * primary plane state rotation is not BIT(0), but we only want
+ * it effect on logo display, userspace may not known to clean
+ * this property, would get unexpect display, so force set
+ * primary rotation to BIT(0).
+ */
+ if (!crtc->primary || !crtc->primary->state)
+ continue;
+
+ drm_atomic_plane_set_property(crtc->primary,
+ crtc->primary->state,
+ mode_config->rotation_property,
+ BIT(0));
+ }
+
+ /*
+ * Is possible get deadlock here?
+ */
+ WARN_ON(ret == -EDEADLK);
+
+ if (ret)
+ goto err_free_state;
+
+ drm_modeset_unlock_all(drm_dev);
+ return;
+
+err_free_state:
+ drm_atomic_state_free(state);
+err_unlock:
+ drm_modeset_unlock_all(drm_dev);
+ if (ret)
+ dev_err(drm_dev->dev, "failed to show loader logo\n");
+}
+
/*
* Attach a (component) device to the shared drm dma mapping from master drm
* device. This is used by the VOPs to map GEM buffers to a common DMA
drm_dev->dev_private = private;
+#ifdef CONFIG_DRM_DMA_SYNC
+ private->cpu_fence_context = fence_context_alloc(1);
+ atomic_set(&private->cpu_fence_seqno, 0);
+#endif
+
drm_mode_config_init(drm_dev);
rockchip_drm_mode_config_init(drm_dev);
drm_mode_config_reset(drm_dev);
+ show_loader_logo(drm_dev);
+
ret = rockchip_drm_fbdev_init(drm_dev);
if (ret)
goto err_vblank_cleanup;
+ drm_dev->mode_config.allow_fb_modifiers = true;
+
return 0;
err_vblank_cleanup:
drm_vblank_cleanup(drm_dev);
priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv);
}
+int rockchip_drm_register_subdrv(struct drm_rockchip_subdrv *subdrv)
+{
+ if (!subdrv)
+ return -EINVAL;
+
+ mutex_lock(&subdrv_list_mutex);
+ list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list);
+ mutex_unlock(&subdrv_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_register_subdrv);
+
+int rockchip_drm_unregister_subdrv(struct drm_rockchip_subdrv *subdrv)
+{
+ if (!subdrv)
+ return -EINVAL;
+
+ mutex_lock(&subdrv_list_mutex);
+ list_del(&subdrv->list);
+ mutex_unlock(&subdrv_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rockchip_drm_unregister_subdrv);
+
+static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct rockchip_drm_file_private *file_priv;
+ struct drm_rockchip_subdrv *subdrv;
+ int ret = 0;
+
+ file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL);
+ if (!file_priv)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&file_priv->gem_cpu_acquire_list);
+
+ file->driver_priv = file_priv;
+
+ mutex_lock(&subdrv_list_mutex);
+ list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) {
+ ret = subdrv->open(dev, subdrv->dev, file);
+ if (ret) {
+ mutex_unlock(&subdrv_list_mutex);
+ goto err_free_file_priv;
+ }
+ }
+ mutex_unlock(&subdrv_list_mutex);
+
+ return 0;
+
+err_free_file_priv:
+ kfree(file_priv);
+ file_priv = NULL;
+
+ return ret;
+}
+
static void rockchip_drm_preclose(struct drm_device *dev,
struct drm_file *file_priv)
{
+ struct rockchip_drm_file_private *file_private = file_priv->driver_priv;
+ struct rockchip_gem_object_node *cur, *d;
+ struct drm_rockchip_subdrv *subdrv;
struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv);
+
+ mutex_lock(&dev->struct_mutex);
+ list_for_each_entry_safe(cur, d,
+ &file_private->gem_cpu_acquire_list, list) {
+#ifdef CONFIG_DRM_DMA_SYNC
+ BUG_ON(!cur->rockchip_gem_obj->acquire_fence);
+ drm_fence_signal_and_put(&cur->rockchip_gem_obj->acquire_fence);
+#endif
+ drm_gem_object_unreference(&cur->rockchip_gem_obj->base);
+ kfree(cur);
+ }
+ /* since we are deleting the whole list, just initialize the header
+ * instead of calling list_del for every element
+ */
+ INIT_LIST_HEAD(&file_private->gem_cpu_acquire_list);
+ mutex_unlock(&dev->struct_mutex);
+
+ mutex_lock(&subdrv_list_mutex);
+ list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list)
+ subdrv->close(dev, subdrv->dev, file_priv);
+ mutex_unlock(&subdrv_list_mutex);
+}
+
+static void rockchip_drm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ kfree(file->driver_priv);
+ file->driver_priv = NULL;
}
void rockchip_drm_lastclose(struct drm_device *dev)
{
struct rockchip_drm_private *priv = dev->dev_private;
- drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper);
+ drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev_helper);
}
static const struct drm_ioctl_desc rockchip_ioctls[] = {
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
+ DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET,
rockchip_gem_map_offset_ioctl,
- DRM_UNLOCKED | DRM_AUTH),
+ DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_ACQUIRE,
+ rockchip_gem_cpu_acquire_ioctl,
+ DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_RELEASE,
+ rockchip_gem_cpu_release_ioctl,
+ DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_GET_VER, rockchip_rga_get_ver_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_SET_CMDLIST,
+ rockchip_rga_set_cmdlist_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
+ DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_EXEC, rockchip_rga_exec_ioctl,
+ DRM_AUTH | DRM_RENDER_ALLOW),
};
static const struct file_operations rockchip_drm_driver_fops = {
static struct drm_driver rockchip_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM |
- DRIVER_PRIME | DRIVER_ATOMIC,
+ DRIVER_PRIME | DRIVER_ATOMIC |
+ DRIVER_RENDER,
.load = rockchip_drm_load,
.unload = rockchip_drm_unload,
.preclose = rockchip_drm_preclose,
.lastclose = rockchip_drm_lastclose,
.get_vblank_counter = drm_vblank_no_hw_counter,
+ .open = rockchip_drm_open,
+ .postclose = rockchip_drm_postclose,
.enable_vblank = rockchip_drm_crtc_enable_vblank,
.disable_vblank = rockchip_drm_crtc_disable_vblank,
.gem_vm_ops = &rockchip_drm_vm_ops,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_import = drm_gem_prime_import,
+ .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table,
.gem_prime_export = drm_gem_prime_export,
.gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table,
.gem_prime_vmap = rockchip_gem_prime_vmap,
};
#ifdef CONFIG_PM_SLEEP
-static int rockchip_drm_sys_suspend(struct device *dev)
+void rockchip_drm_fb_suspend(struct drm_device *drm)
{
- struct drm_device *drm = dev_get_drvdata(dev);
- struct drm_connector *connector;
+ struct rockchip_drm_private *priv = drm->dev_private;
- if (!drm)
- return 0;
+ console_lock();
+ drm_fb_helper_set_suspend(priv->fbdev_helper, 1);
+ console_unlock();
+}
+
+void rockchip_drm_fb_resume(struct drm_device *drm)
+{
+ struct rockchip_drm_private *priv = drm->dev_private;
- drm_modeset_lock_all(drm);
- list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
- int old_dpms = connector->dpms;
+ console_lock();
+ drm_fb_helper_set_suspend(priv->fbdev_helper, 0);
+ console_unlock();
+}
+
+static int rockchip_drm_sys_suspend(struct device *dev)
+{
+ struct drm_device *drm = dev_get_drvdata(dev);
+ struct rockchip_drm_private *priv = drm->dev_private;
- if (connector->funcs->dpms)
- connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+ drm_kms_helper_poll_disable(drm);
+ rockchip_drm_fb_suspend(drm);
- /* Set the old mode back to the connector for resume */
- connector->dpms = old_dpms;
+ priv->state = drm_atomic_helper_suspend(drm);
+ if (IS_ERR(priv->state)) {
+ rockchip_drm_fb_resume(drm);
+ drm_kms_helper_poll_enable(drm);
+ return PTR_ERR(priv->state);
}
- drm_modeset_unlock_all(drm);
return 0;
}
static int rockchip_drm_sys_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
- struct drm_connector *connector;
- enum drm_connector_status status;
- bool changed = false;
-
- if (!drm)
- return 0;
-
- drm_modeset_lock_all(drm);
- list_for_each_entry(connector, &drm->mode_config.connector_list, head) {
- int desired_mode = connector->dpms;
-
- /*
- * at suspend time, we save dpms to connector->dpms,
- * restore the old_dpms, and at current time, the connector
- * dpms status must be DRM_MODE_DPMS_OFF.
- */
- connector->dpms = DRM_MODE_DPMS_OFF;
-
- /*
- * If the connector has been disconnected during suspend,
- * disconnect it from the encoder and leave it off. We'll notify
- * userspace at the end.
- */
- if (desired_mode == DRM_MODE_DPMS_ON) {
- status = connector->funcs->detect(connector, true);
- if (status == connector_status_disconnected) {
- connector->encoder = NULL;
- connector->status = status;
- changed = true;
- continue;
- }
- }
- if (connector->funcs->dpms)
- connector->funcs->dpms(connector, desired_mode);
- }
- drm_modeset_unlock_all(drm);
+ struct rockchip_drm_private *priv = drm->dev_private;
- drm_helper_resume_force_mode(drm);
-
- if (changed)
- drm_kms_helper_hotplug_event(drm);
+ drm_atomic_helper_resume(drm, priv->state);
+ rockchip_drm_fb_resume(drm);
+ drm_kms_helper_poll_enable(drm);
return 0;
}
rockchip_drm_sys_resume)
};
-/*
- * @node: device tree node containing encoder input ports
- * @encoder: drm_encoder
- */
-int rockchip_drm_encoder_get_mux_id(struct device_node *node,
- struct drm_encoder *encoder)
-{
- struct device_node *ep;
- struct drm_crtc *crtc = encoder->crtc;
- struct of_endpoint endpoint;
- struct device_node *port;
- int ret;
-
- if (!node || !crtc)
- return -EINVAL;
-
- for_each_endpoint_of_node(node, ep) {
- port = of_graph_get_remote_port(ep);
- of_node_put(port);
- if (port == crtc->port) {
- ret = of_graph_parse_endpoint(ep, &endpoint);
- of_node_put(ep);
- return ret ?: endpoint.id;
- }
- }
-
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id);
-
static int compare_of(struct device *dev, void *data)
{
struct device_node *np = data;