1/* 2 * Copyright (C) 2012 Russell King 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8#include <drm/drmP.h> 9#include <drm/drm_crtc_helper.h> 10#include <drm/drm_fb_helper.h> 11#include "armada_drm.h" 12#include "armada_fb.h" 13#include "armada_gem.h" 14#include "armada_hw.h" 15 16static void armada_fb_destroy(struct drm_framebuffer *fb) 17{ 18 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); 19 20 drm_framebuffer_cleanup(&dfb->fb); 21 drm_gem_object_unreference_unlocked(&dfb->obj->obj); 22 kfree(dfb); 23} 24 25static int armada_fb_create_handle(struct drm_framebuffer *fb, 26 struct drm_file *dfile, unsigned int *handle) 27{ 28 struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb); 29 return drm_gem_handle_create(dfile, &dfb->obj->obj, handle); 30} 31 32static const struct drm_framebuffer_funcs armada_fb_funcs = { 33 .destroy = armada_fb_destroy, 34 .create_handle = armada_fb_create_handle, 35}; 36 37struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev, 38 struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj) 39{ 40 struct armada_framebuffer *dfb; 41 uint8_t format, config; 42 int ret; 43 44 switch (mode->pixel_format) { 45#define FMT(drm, fmt, mod) \ 46 case DRM_FORMAT_##drm: \ 47 format = CFG_##fmt; \ 48 config = mod; \ 49 break 50 FMT(RGB565, 565, CFG_SWAPRB); 51 FMT(BGR565, 565, 0); 52 FMT(ARGB1555, 1555, CFG_SWAPRB); 53 FMT(ABGR1555, 1555, 0); 54 FMT(RGB888, 888PACK, CFG_SWAPRB); 55 FMT(BGR888, 888PACK, 0); 56 FMT(XRGB8888, X888, CFG_SWAPRB); 57 FMT(XBGR8888, X888, 0); 58 FMT(ARGB8888, 8888, CFG_SWAPRB); 59 FMT(ABGR8888, 8888, 0); 60 FMT(YUYV, 422PACK, CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV); 61 FMT(UYVY, 422PACK, CFG_YUV2RGB); 62 FMT(VYUY, 422PACK, CFG_YUV2RGB | CFG_SWAPUV); 63 FMT(YVYU, 422PACK, CFG_YUV2RGB | CFG_SWAPYU); 64 FMT(YUV422, 422, CFG_YUV2RGB); 65 FMT(YVU422, 422, CFG_YUV2RGB | CFG_SWAPUV); 66 FMT(YUV420, 420, CFG_YUV2RGB); 67 FMT(YVU420, 420, CFG_YUV2RGB | CFG_SWAPUV); 68 FMT(C8, PSEUDO8, 0); 69#undef FMT 70 default: 71 return ERR_PTR(-EINVAL); 72 } 73 74 dfb = kzalloc(sizeof(*dfb), GFP_KERNEL); 75 if (!dfb) { 76 DRM_ERROR("failed to allocate Armada fb object\n"); 77 return ERR_PTR(-ENOMEM); 78 } 79 80 dfb->fmt = format; 81 dfb->mod = config; 82 dfb->obj = obj; 83 84 drm_helper_mode_fill_fb_struct(&dfb->fb, mode); 85 86 ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs); 87 if (ret) { 88 kfree(dfb); 89 return ERR_PTR(ret); 90 } 91 92 /* 93 * Take a reference on our object as we're successful - the 94 * caller already holds a reference, which keeps us safe for 95 * the above call, but the caller will drop their reference 96 * to it. Hence we need to take our own reference. 97 */ 98 drm_gem_object_reference(&obj->obj); 99 100 return dfb; 101} 102 103static struct drm_framebuffer *armada_fb_create(struct drm_device *dev, 104 struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode) 105{ 106 struct armada_gem_object *obj; 107 struct armada_framebuffer *dfb; 108 int ret; 109 110 DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n", 111 mode->width, mode->height, mode->pixel_format, 112 mode->flags, mode->pitches[0], mode->pitches[1], 113 mode->pitches[2]); 114 115 /* We can only handle a single plane at the moment */ 116 if (drm_format_num_planes(mode->pixel_format) > 1 && 117 (mode->handles[0] != mode->handles[1] || 118 mode->handles[0] != mode->handles[2])) { 119 ret = -EINVAL; 120 goto err; 121 } 122 123 obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]); 124 if (!obj) { 125 ret = -ENOENT; 126 goto err; 127 } 128 129 if (obj->obj.import_attach && !obj->sgt) { 130 ret = armada_gem_map_import(obj); 131 if (ret) 132 goto err_unref; 133 } 134 135 /* Framebuffer objects must have a valid device address for scanout */ 136 if (obj->dev_addr == DMA_ERROR_CODE) { 137 ret = -EINVAL; 138 goto err_unref; 139 } 140 141 dfb = armada_framebuffer_create(dev, mode, obj); 142 if (IS_ERR(dfb)) { 143 ret = PTR_ERR(dfb); 144 goto err; 145 } 146 147 drm_gem_object_unreference_unlocked(&obj->obj); 148 149 return &dfb->fb; 150 151 err_unref: 152 drm_gem_object_unreference_unlocked(&obj->obj); 153 err: 154 DRM_ERROR("failed to initialize framebuffer: %d\n", ret); 155 return ERR_PTR(ret); 156} 157 158static void armada_output_poll_changed(struct drm_device *dev) 159{ 160 struct armada_private *priv = dev->dev_private; 161 struct drm_fb_helper *fbh = priv->fbdev; 162 163 if (fbh) 164 drm_fb_helper_hotplug_event(fbh); 165} 166 167const struct drm_mode_config_funcs armada_drm_mode_config_funcs = { 168 .fb_create = armada_fb_create, 169 .output_poll_changed = armada_output_poll_changed, 170}; 171