blob: 5737bc5e6ed23669fa2269be76c2d9c1b0fb513e [file] [log] [blame]
Inki Dae1c248b72011-10-04 19:19:01 +09001/* exynos_drm_fbdev.c
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * Authors:
5 * Inki Dae <inki.dae@samsung.com>
6 * Joonyoung Shim <jy0922.shim@samsung.com>
7 * Seung-Woo Kim <sw0312.kim@samsung.com>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the next
17 * paragraph) shall be included in all copies or substantial portions of the
18 * Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 * OTHER DEALINGS IN THE SOFTWARE.
27 */
28
29#include "drmP.h"
30#include "drm_crtc.h"
31#include "drm_fb_helper.h"
32#include "drm_crtc_helper.h"
33
34#include "exynos_drm_drv.h"
35#include "exynos_drm_fb.h"
Inki Dae2c871122011-11-12 15:23:32 +090036#include "exynos_drm_gem.h"
Inki Dae1c248b72011-10-04 19:19:01 +090037
38#define MAX_CONNECTOR 4
39#define PREFERRED_BPP 32
40
41#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
42 drm_fb_helper)
43
44struct exynos_drm_fbdev {
Joonyoung Shime1533c02011-12-13 14:46:57 +090045 struct drm_fb_helper drm_fb_helper;
46 struct exynos_drm_gem_obj *exynos_gem_obj;
Inki Dae1c248b72011-10-04 19:19:01 +090047};
48
Inki Dae1c248b72011-10-04 19:19:01 +090049static struct fb_ops exynos_drm_fb_ops = {
50 .owner = THIS_MODULE,
51 .fb_fillrect = cfb_fillrect,
52 .fb_copyarea = cfb_copyarea,
53 .fb_imageblit = cfb_imageblit,
54 .fb_check_var = drm_fb_helper_check_var,
Sascha Hauer83b316f2012-02-01 11:38:37 +010055 .fb_set_par = drm_fb_helper_set_par,
Inki Dae1c248b72011-10-04 19:19:01 +090056 .fb_blank = drm_fb_helper_blank,
57 .fb_pan_display = drm_fb_helper_pan_display,
58 .fb_setcmap = drm_fb_helper_setcmap,
59};
60
Inki Dae19c8b832011-10-14 13:29:46 +090061static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
Seung-Woo Kimaa6b2b62011-11-04 13:44:38 +090062 struct drm_framebuffer *fb)
Inki Dae1c248b72011-10-04 19:19:01 +090063{
64 struct fb_info *fbi = helper->fbdev;
65 struct drm_device *dev = helper->dev;
Inki Dae2c871122011-11-12 15:23:32 +090066 struct exynos_drm_gem_buf *buffer;
Seung-Woo Kimaa6b2b62011-11-04 13:44:38 +090067 unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
Inki Dae19c8b832011-10-14 13:29:46 +090068 unsigned long offset;
Inki Dae1c248b72011-10-04 19:19:01 +090069
70 DRM_DEBUG_KMS("%s\n", __FILE__);
71
Ville Syrjälä01f2c772011-12-20 00:06:49 +020072 drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
Seung-Woo Kimaa6b2b62011-11-04 13:44:38 +090073 drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
Inki Dae1c248b72011-10-04 19:19:01 +090074
Seung-Woo Kim229d3532011-12-15 14:36:22 +090075 /* RGB formats use only one buffer */
76 buffer = exynos_drm_fb_buffer(fb, 0);
Inki Dae2c871122011-11-12 15:23:32 +090077 if (!buffer) {
78 DRM_LOG_KMS("buffer is null.\n");
Inki Dae19c8b832011-10-14 13:29:46 +090079 return -EFAULT;
80 }
Inki Dae1c248b72011-10-04 19:19:01 +090081
Inki Dae19c8b832011-10-14 13:29:46 +090082 offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
Ville Syrjälä01f2c772011-12-20 00:06:49 +020083 offset += fbi->var.yoffset * fb->pitches[0];
Inki Dae1c248b72011-10-04 19:19:01 +090084
Inki Dae2c871122011-11-12 15:23:32 +090085 dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
86 fbi->screen_base = buffer->kvaddr + offset;
87 fbi->fix.smem_start = (unsigned long)(buffer->dma_addr + offset);
Inki Dae1c248b72011-10-04 19:19:01 +090088 fbi->screen_size = size;
Inki Dae1c248b72011-10-04 19:19:01 +090089 fbi->fix.smem_len = size;
Inki Dae19c8b832011-10-14 13:29:46 +090090
91 return 0;
Inki Dae1c248b72011-10-04 19:19:01 +090092}
93
94static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
95 struct drm_fb_helper_surface_size *sizes)
96{
97 struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
Joonyoung Shime1533c02011-12-13 14:46:57 +090098 struct exynos_drm_gem_obj *exynos_gem_obj;
Inki Dae1c248b72011-10-04 19:19:01 +090099 struct drm_device *dev = helper->dev;
100 struct fb_info *fbi;
Joonyoung Shima794d572011-12-08 15:05:19 +0900101 struct drm_mode_fb_cmd2 mode_cmd = { 0 };
Inki Dae1c248b72011-10-04 19:19:01 +0900102 struct platform_device *pdev = dev->platformdev;
Joonyoung Shime1533c02011-12-13 14:46:57 +0900103 unsigned long size;
Inki Dae1c248b72011-10-04 19:19:01 +0900104 int ret;
105
106 DRM_DEBUG_KMS("%s\n", __FILE__);
107
108 DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
109 sizes->surface_width, sizes->surface_height,
110 sizes->surface_bpp);
111
112 mode_cmd.width = sizes->surface_width;
113 mode_cmd.height = sizes->surface_height;
Joonyoung Shima794d572011-12-08 15:05:19 +0900114 mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
115 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
116 sizes->surface_depth);
Inki Dae1c248b72011-10-04 19:19:01 +0900117
118 mutex_lock(&dev->struct_mutex);
119
120 fbi = framebuffer_alloc(0, &pdev->dev);
121 if (!fbi) {
122 DRM_ERROR("failed to allocate fb info.\n");
123 ret = -ENOMEM;
124 goto out;
125 }
126
Joonyoung Shime1533c02011-12-13 14:46:57 +0900127 size = mode_cmd.pitches[0] * mode_cmd.height;
128 exynos_gem_obj = exynos_drm_gem_create(dev, size);
129 if (IS_ERR(exynos_gem_obj)) {
130 ret = PTR_ERR(exynos_gem_obj);
Inki Dae1c248b72011-10-04 19:19:01 +0900131 goto out;
132 }
133
Joonyoung Shime1533c02011-12-13 14:46:57 +0900134 exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
135
136 helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
137 &exynos_gem_obj->base);
138 if (IS_ERR_OR_NULL(helper->fb)) {
139 DRM_ERROR("failed to create drm framebuffer.\n");
140 ret = PTR_ERR(helper->fb);
141 goto out;
142 }
143
Inki Dae1c248b72011-10-04 19:19:01 +0900144 helper->fbdev = fbi;
145
146 fbi->par = helper;
147 fbi->flags = FBINFO_FLAG_DEFAULT;
148 fbi->fbops = &exynos_drm_fb_ops;
149
150 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
151 if (ret) {
152 DRM_ERROR("failed to allocate cmap.\n");
153 goto out;
154 }
155
Seung-Woo Kimaa6b2b62011-11-04 13:44:38 +0900156 ret = exynos_drm_fbdev_update(helper, helper->fb);
Joonyoung Shime1533c02011-12-13 14:46:57 +0900157 if (ret < 0) {
Inki Dae19c8b832011-10-14 13:29:46 +0900158 fb_dealloc_cmap(&fbi->cmap);
Joonyoung Shime1533c02011-12-13 14:46:57 +0900159 goto out;
160 }
Inki Dae1c248b72011-10-04 19:19:01 +0900161
162/*
163 * if failed, all resources allocated above would be released by
164 * drm_mode_config_cleanup() when drm_load() had been called prior
165 * to any specific driver such as fimd or hdmi driver.
166 */
167out:
168 mutex_unlock(&dev->struct_mutex);
169 return ret;
170}
171
172static bool
173exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
174 struct drm_fb_helper_surface_size *sizes)
175{
176 if (fb->width != sizes->surface_width)
177 return false;
178 if (fb->height != sizes->surface_height)
179 return false;
180 if (fb->bits_per_pixel != sizes->surface_bpp)
181 return false;
182 if (fb->depth != sizes->surface_depth)
183 return false;
184
185 return true;
186}
187
188static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
189 struct drm_fb_helper_surface_size *sizes)
190{
191 struct drm_device *dev = helper->dev;
192 struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
Joonyoung Shime1533c02011-12-13 14:46:57 +0900193 struct exynos_drm_gem_obj *exynos_gem_obj;
194 struct drm_framebuffer *fb = helper->fb;
Joonyoung Shima794d572011-12-08 15:05:19 +0900195 struct drm_mode_fb_cmd2 mode_cmd = { 0 };
Joonyoung Shime1533c02011-12-13 14:46:57 +0900196 unsigned long size;
Inki Dae1c248b72011-10-04 19:19:01 +0900197
198 DRM_DEBUG_KMS("%s\n", __FILE__);
199
Inki Dae1c248b72011-10-04 19:19:01 +0900200 if (exynos_drm_fbdev_is_samefb(fb, sizes))
201 return 0;
202
203 mode_cmd.width = sizes->surface_width;
204 mode_cmd.height = sizes->surface_height;
Joonyoung Shima794d572011-12-08 15:05:19 +0900205 mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
206 mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
207 sizes->surface_depth);
Inki Dae1c248b72011-10-04 19:19:01 +0900208
Joonyoung Shime1533c02011-12-13 14:46:57 +0900209 if (exynos_fbdev->exynos_gem_obj)
210 exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj);
211
Inki Dae1c248b72011-10-04 19:19:01 +0900212 if (fb->funcs->destroy)
213 fb->funcs->destroy(fb);
214
Joonyoung Shime1533c02011-12-13 14:46:57 +0900215 size = mode_cmd.pitches[0] * mode_cmd.height;
216 exynos_gem_obj = exynos_drm_gem_create(dev, size);
217 if (IS_ERR(exynos_gem_obj))
218 return PTR_ERR(exynos_gem_obj);
219
220 exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
221
222 helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
223 &exynos_gem_obj->base);
224 if (IS_ERR_OR_NULL(helper->fb)) {
225 DRM_ERROR("failed to create drm framebuffer.\n");
226 return PTR_ERR(helper->fb);
Inki Dae1c248b72011-10-04 19:19:01 +0900227 }
228
Seung-Woo Kimaa6b2b62011-11-04 13:44:38 +0900229 return exynos_drm_fbdev_update(helper, helper->fb);
Inki Dae1c248b72011-10-04 19:19:01 +0900230}
231
232static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
233 struct drm_fb_helper_surface_size *sizes)
234{
235 int ret = 0;
236
237 DRM_DEBUG_KMS("%s\n", __FILE__);
238
239 if (!helper->fb) {
240 ret = exynos_drm_fbdev_create(helper, sizes);
241 if (ret < 0) {
242 DRM_ERROR("failed to create fbdev.\n");
243 return ret;
244 }
245
246 /*
247 * fb_helper expects a value more than 1 if succeed
248 * because register_framebuffer() should be called.
249 */
250 ret = 1;
251 } else {
252 ret = exynos_drm_fbdev_recreate(helper, sizes);
253 if (ret < 0) {
254 DRM_ERROR("failed to reconfigure fbdev\n");
255 return ret;
256 }
257 }
258
259 return ret;
260}
261
262static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
263 .fb_probe = exynos_drm_fbdev_probe,
264};
265
266int exynos_drm_fbdev_init(struct drm_device *dev)
267{
268 struct exynos_drm_fbdev *fbdev;
269 struct exynos_drm_private *private = dev->dev_private;
270 struct drm_fb_helper *helper;
271 unsigned int num_crtc;
272 int ret;
273
274 DRM_DEBUG_KMS("%s\n", __FILE__);
275
276 if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
277 return 0;
278
279 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
280 if (!fbdev) {
281 DRM_ERROR("failed to allocate drm fbdev.\n");
282 return -ENOMEM;
283 }
284
285 private->fb_helper = helper = &fbdev->drm_fb_helper;
286 helper->funcs = &exynos_drm_fb_helper_funcs;
287
288 num_crtc = dev->mode_config.num_crtc;
289
290 ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
291 if (ret < 0) {
292 DRM_ERROR("failed to initialize drm fb helper.\n");
293 goto err_init;
294 }
295
296 ret = drm_fb_helper_single_add_all_connectors(helper);
297 if (ret < 0) {
298 DRM_ERROR("failed to register drm_fb_helper_connector.\n");
299 goto err_setup;
300
301 }
302
303 ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
304 if (ret < 0) {
305 DRM_ERROR("failed to set up hw configuration.\n");
306 goto err_setup;
307 }
308
309 return 0;
310
311err_setup:
312 drm_fb_helper_fini(helper);
313
314err_init:
315 private->fb_helper = NULL;
316 kfree(fbdev);
317
318 return ret;
319}
320
321static void exynos_drm_fbdev_destroy(struct drm_device *dev,
322 struct drm_fb_helper *fb_helper)
323{
324 struct drm_framebuffer *fb;
325
326 /* release drm framebuffer and real buffer */
327 if (fb_helper->fb && fb_helper->fb->funcs) {
328 fb = fb_helper->fb;
329 if (fb && fb->funcs->destroy)
330 fb->funcs->destroy(fb);
331 }
332
333 /* release linux framebuffer */
334 if (fb_helper->fbdev) {
335 struct fb_info *info;
336 int ret;
337
338 info = fb_helper->fbdev;
339 ret = unregister_framebuffer(info);
340 if (ret < 0)
341 DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
342
343 if (info->cmap.len)
344 fb_dealloc_cmap(&info->cmap);
345
346 framebuffer_release(info);
347 }
348
349 drm_fb_helper_fini(fb_helper);
350}
351
352void exynos_drm_fbdev_fini(struct drm_device *dev)
353{
354 struct exynos_drm_private *private = dev->dev_private;
355 struct exynos_drm_fbdev *fbdev;
356
357 if (!private || !private->fb_helper)
358 return;
359
360 fbdev = to_exynos_fbdev(private->fb_helper);
361
Joonyoung Shime1533c02011-12-13 14:46:57 +0900362 if (fbdev->exynos_gem_obj)
363 exynos_drm_gem_destroy(fbdev->exynos_gem_obj);
364
Inki Dae1c248b72011-10-04 19:19:01 +0900365 exynos_drm_fbdev_destroy(dev, private->fb_helper);
366 kfree(fbdev);
367 private->fb_helper = NULL;
368}
369
370void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
371{
372 struct exynos_drm_private *private = dev->dev_private;
373
374 if (!private || !private->fb_helper)
375 return;
376
377 drm_fb_helper_restore_fbdev_mode(private->fb_helper);
378}
379
380int exynos_drm_fbdev_reinit(struct drm_device *dev)
381{
382 struct exynos_drm_private *private = dev->dev_private;
383 struct drm_fb_helper *fb_helper;
384 int ret;
385
386 if (!private)
387 return -EINVAL;
388
Inki Daef6b98252011-10-14 13:29:50 +0900389 /*
390 * if all sub drivers were unloaded then num_connector is 0
391 * so at this time, the framebuffers also should be destroyed.
392 */
Inki Dae1c248b72011-10-04 19:19:01 +0900393 if (!dev->mode_config.num_connector) {
394 exynos_drm_fbdev_fini(dev);
395 return 0;
396 }
397
398 fb_helper = private->fb_helper;
399
400 if (fb_helper) {
Joonyoung Shim84b46992011-11-04 13:41:46 +0900401 struct list_head temp_list;
402
403 INIT_LIST_HEAD(&temp_list);
404
405 /*
406 * fb_helper is reintialized but kernel fb is reused
407 * so kernel_fb_list need to be backuped and restored
408 */
409 if (!list_empty(&fb_helper->kernel_fb_list))
410 list_replace_init(&fb_helper->kernel_fb_list,
411 &temp_list);
412
Inki Dae1c248b72011-10-04 19:19:01 +0900413 drm_fb_helper_fini(fb_helper);
414
415 ret = drm_fb_helper_init(dev, fb_helper,
416 dev->mode_config.num_crtc, MAX_CONNECTOR);
417 if (ret < 0) {
418 DRM_ERROR("failed to initialize drm fb helper\n");
419 return ret;
420 }
421
Joonyoung Shim84b46992011-11-04 13:41:46 +0900422 if (!list_empty(&temp_list))
423 list_replace(&temp_list, &fb_helper->kernel_fb_list);
424
Inki Dae1c248b72011-10-04 19:19:01 +0900425 ret = drm_fb_helper_single_add_all_connectors(fb_helper);
426 if (ret < 0) {
427 DRM_ERROR("failed to add fb helper to connectors\n");
428 goto err;
429 }
430
431 ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
432 if (ret < 0) {
433 DRM_ERROR("failed to set up hw configuration.\n");
434 goto err;
435 }
436 } else {
437 /*
438 * if drm_load() failed whem drm load() was called prior
439 * to specific drivers, fb_helper must be NULL and so
440 * this fuction should be called again to re-initialize and
441 * re-configure the fb helper. it means that this function
442 * has been called by the specific drivers.
443 */
Inki Daef6b98252011-10-14 13:29:50 +0900444 ret = exynos_drm_fbdev_init(dev);
Inki Dae1c248b72011-10-04 19:19:01 +0900445 }
446
Inki Dae601b44e2011-10-14 13:29:49 +0900447 return ret;
448
Inki Dae1c248b72011-10-04 19:19:01 +0900449err:
450 /*
451 * if drm_load() failed when drm load() was called prior
452 * to specific drivers, the fb_helper must be NULL and so check it.
453 */
454 if (fb_helper)
455 drm_fb_helper_fini(fb_helper);
456
457 return ret;
458}
459
460MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
461MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
462MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
463MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
464MODULE_LICENSE("GPL");