OLD | NEW |
1 /* | 1 /* |
2 * drivers/video/tegra/fb.c | 2 * drivers/video/tegra/fb.c |
3 * | 3 * |
4 * Copyright (C) 2010 Google, Inc. | 4 * Copyright (C) 2010 Google, Inc. |
5 * Author: Erik Gilling <konkers@android.com> | 5 * Author: Erik Gilling <konkers@android.com> |
6 * Colin Cross <ccross@android.com> | 6 * Colin Cross <ccross@android.com> |
7 * Travis Geiselbrecht <travis@palm.com> | 7 * Travis Geiselbrecht <travis@palm.com> |
8 * | 8 * |
9 * This software is licensed under the terms of the GNU General Public | 9 * This software is licensed under the terms of the GNU General Public |
10 * License version 2, as published by the Free Software Foundation, and | 10 * License version 2, as published by the Free Software Foundation, and |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 struct tegra_fb_info { | 43 struct tegra_fb_info { |
44 struct tegra_dc_win *win; | 44 struct tegra_dc_win *win; |
45 struct nvhost_device *ndev; | 45 struct nvhost_device *ndev; |
46 struct fb_info *info; | 46 struct fb_info *info; |
47 bool valid; | 47 bool valid; |
48 | 48 |
49 struct resource *fb_mem; | 49 struct resource *fb_mem; |
50 | 50 |
51 int xres; | 51 int xres; |
52 int yres; | 52 int yres; |
53 | |
54 atomic_t in_use; | |
55 struct nvmap_client *user_nvmap; | |
56 struct nvmap_client *fb_nvmap; | |
57 | |
58 struct workqueue_struct *flip_wq; | |
59 }; | |
60 | |
61 struct tegra_fb_flip_win { | |
62 struct tegra_fb_windowattr attr; | |
63 struct nvmap_handle_ref *handle; | |
64 dma_addr_t phys_addr; | |
65 }; | |
66 | |
67 struct tegra_fb_flip_data { | |
68 struct work_struct work; | |
69 struct tegra_fb_info *fb; | |
70 struct tegra_fb_flip_win win[TEGRA_FB_FLIP_N_WINDOWS]; | |
71 u32 syncpt_max; | |
72 }; | 53 }; |
73 | 54 |
74 /* palette array used by the fbcon */ | 55 /* palette array used by the fbcon */ |
75 static u32 pseudo_palette[16]; | 56 static u32 pseudo_palette[16]; |
76 | 57 |
77 static int tegra_fb_open(struct fb_info *info, int user) | |
78 { | |
79 struct tegra_fb_info *tegra_fb = info->par; | |
80 | |
81 if (atomic_xchg(&tegra_fb->in_use, 1)) | |
82 return -EBUSY; | |
83 | |
84 tegra_fb->user_nvmap = NULL; | |
85 | |
86 return 0; | |
87 } | |
88 | |
89 static int tegra_fb_release(struct fb_info *info, int user) | |
90 { | |
91 struct tegra_fb_info *tegra_fb = info->par; | |
92 struct fb_var_screeninfo *var = &info->var; | |
93 | |
94 flush_workqueue(tegra_fb->flip_wq); | |
95 | |
96 if (tegra_fb->win->cur_handle) { | |
97 nvmap_unpin(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); | |
98 nvmap_free(tegra_fb->fb_nvmap, tegra_fb->win->cur_handle); | |
99 | |
100 tegra_fb->win->cur_handle = NULL; | |
101 | |
102 tegra_fb->win->x = 0; | |
103 tegra_fb->win->y = 0; | |
104 tegra_fb->win->w = var->xres; | |
105 tegra_fb->win->h = var->yres; | |
106 tegra_fb->win->out_x = 0; | |
107 tegra_fb->win->out_y = 0; | |
108 tegra_fb->win->out_w = var->xres; | |
109 tegra_fb->win->out_h = var->yres; | |
110 tegra_fb->win->flags = TEGRA_WIN_FLAG_ENABLED; | |
111 } | |
112 | |
113 if (tegra_fb->user_nvmap) { | |
114 nvmap_client_put(tegra_fb->user_nvmap); | |
115 tegra_fb->user_nvmap = NULL; | |
116 } | |
117 | |
118 WARN_ON(!atomic_xchg(&tegra_fb->in_use, 0)); | |
119 | |
120 return 0; | |
121 } | |
122 | |
123 static int tegra_fb_check_var(struct fb_var_screeninfo *var, | 58 static int tegra_fb_check_var(struct fb_var_screeninfo *var, |
124 struct fb_info *info) | 59 struct fb_info *info) |
125 { | 60 { |
126 if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) > | 61 if ((var->yres * var->xres * var->bits_per_pixel / 8 * 2) > |
127 info->screen_size) | 62 info->screen_size) |
128 return -EINVAL; | 63 return -EINVAL; |
129 | 64 |
130 /* double yres_virtual to allow double buffering through pan_display */ | 65 /* double yres_virtual to allow double buffering through pan_display */ |
131 var->yres_virtual = var->yres * 2; | 66 var->yres_virtual = var->yres * 2; |
132 | 67 |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
238 case FB_BLANK_UNBLANK: | 173 case FB_BLANK_UNBLANK: |
239 dev_dbg(&tegra_fb->ndev->dev, "unblank\n"); | 174 dev_dbg(&tegra_fb->ndev->dev, "unblank\n"); |
240 tegra_dc_enable(tegra_fb->win->dc); | 175 tegra_dc_enable(tegra_fb->win->dc); |
241 return 0; | 176 return 0; |
242 | 177 |
243 case FB_BLANK_NORMAL: | 178 case FB_BLANK_NORMAL: |
244 case FB_BLANK_VSYNC_SUSPEND: | 179 case FB_BLANK_VSYNC_SUSPEND: |
245 case FB_BLANK_HSYNC_SUSPEND: | 180 case FB_BLANK_HSYNC_SUSPEND: |
246 case FB_BLANK_POWERDOWN: | 181 case FB_BLANK_POWERDOWN: |
247 dev_dbg(&tegra_fb->ndev->dev, "blank\n"); | 182 dev_dbg(&tegra_fb->ndev->dev, "blank\n"); |
248 flush_workqueue(tegra_fb->flip_wq); | |
249 tegra_dc_disable(tegra_fb->win->dc); | 183 tegra_dc_disable(tegra_fb->win->dc); |
250 return 0; | 184 return 0; |
251 | 185 |
252 default: | 186 default: |
253 return -ENOTTY; | 187 return -ENOTTY; |
254 } | 188 } |
255 } | 189 } |
256 | 190 |
257 void tegra_fb_suspend(struct tegra_fb_info *tegra_fb) | |
258 { | |
259 flush_workqueue(tegra_fb->flip_wq); | |
260 } | |
261 | |
262 | |
263 static int tegra_fb_pan_display(struct fb_var_screeninfo *var, | 191 static int tegra_fb_pan_display(struct fb_var_screeninfo *var, |
264 struct fb_info *info) | 192 struct fb_info *info) |
265 { | 193 { |
266 struct tegra_fb_info *tegra_fb = info->par; | 194 struct tegra_fb_info *tegra_fb = info->par; |
267 char __iomem *flush_start; | 195 char __iomem *flush_start; |
268 char __iomem *flush_end; | 196 char __iomem *flush_end; |
269 u32 addr; | 197 u32 addr; |
270 | 198 |
271 if (!tegra_fb->win->cur_handle) { | 199 if (!tegra_fb->win->cur_handle) { |
272 flush_start = info->screen_base + (var->yoffset * info->fix.line
_length); | 200 flush_start = info->screen_base + (var->yoffset * info->fix.line
_length); |
(...skipping 26 matching lines...) Expand all Loading... |
299 { | 227 { |
300 cfb_copyarea(info, region); | 228 cfb_copyarea(info, region); |
301 } | 229 } |
302 | 230 |
303 static void tegra_fb_imageblit(struct fb_info *info, | 231 static void tegra_fb_imageblit(struct fb_info *info, |
304 const struct fb_image *image) | 232 const struct fb_image *image) |
305 { | 233 { |
306 cfb_imageblit(info, image); | 234 cfb_imageblit(info, image); |
307 } | 235 } |
308 | 236 |
309 /* TODO: implement ALLOC, FREE, BLANK ioctls */ | |
310 | |
311 static int tegra_fb_set_nvmap_fd(struct tegra_fb_info *tegra_fb, int fd) | |
312 { | |
313 struct nvmap_client *nvmap = NULL; | |
314 | |
315 if (fd < 0) | |
316 return -EINVAL; | |
317 | |
318 nvmap = nvmap_client_get_file(fd); | |
319 if (IS_ERR(nvmap)) | |
320 return PTR_ERR(nvmap); | |
321 | |
322 if (tegra_fb->user_nvmap) | |
323 nvmap_client_put(tegra_fb->user_nvmap); | |
324 | |
325 tegra_fb->user_nvmap = nvmap; | |
326 | |
327 return 0; | |
328 } | |
329 | |
330 static int tegra_fb_pin_window(struct tegra_fb_info *tegra_fb, | |
331 struct tegra_fb_flip_win *flip_win) | |
332 { | |
333 struct nvmap_handle_ref *win_dupe; | |
334 struct nvmap_handle *win_handle; | |
335 unsigned long buff_id = flip_win->attr.buff_id; | |
336 | |
337 if (!buff_id) | |
338 return 0; | |
339 | |
340 win_handle = nvmap_get_handle_id(tegra_fb->user_nvmap, buff_id); | |
341 if (win_handle == NULL) { | |
342 dev_err(&tegra_fb->ndev->dev, "%s: flip invalid " | |
343 "handle %08lx\n", current->comm, buff_id); | |
344 return -EPERM; | |
345 } | |
346 | |
347 /* duplicate the new framebuffer's handle into the fb driver's | |
348 * nvmap context, to ensure that the handle won't be freed as | |
349 * long as it is in-use by the fb driver */ | |
350 win_dupe = nvmap_duplicate_handle_id(tegra_fb->fb_nvmap, buff_id); | |
351 nvmap_handle_put(win_handle); | |
352 | |
353 if (IS_ERR(win_dupe)) { | |
354 dev_err(&tegra_fb->ndev->dev, "couldn't duplicate handle\n"); | |
355 return PTR_ERR(win_dupe); | |
356 } | |
357 | |
358 flip_win->handle = win_dupe; | |
359 | |
360 flip_win->phys_addr = nvmap_pin(tegra_fb->fb_nvmap, win_dupe); | |
361 if (IS_ERR((void *)flip_win->phys_addr)) { | |
362 dev_err(&tegra_fb->ndev->dev, "couldn't pin handle\n"); | |
363 nvmap_free(tegra_fb->fb_nvmap, win_dupe); | |
364 return PTR_ERR((void *)flip_win->phys_addr); | |
365 } | |
366 | |
367 return 0; | |
368 } | |
369 | |
370 static int tegra_fb_set_windowattr(struct tegra_fb_info *tegra_fb, | |
371 struct tegra_dc_win *win, | |
372 const struct tegra_fb_flip_win *flip_win) | |
373 { | |
374 if (flip_win->handle == NULL) { | |
375 win->flags = 0; | |
376 win->cur_handle = NULL; | |
377 return 0; | |
378 } | |
379 | |
380 win->flags = TEGRA_WIN_FLAG_ENABLED; | |
381 if (flip_win->attr.blend == TEGRA_FB_WIN_BLEND_PREMULT) | |
382 win->flags |= TEGRA_WIN_FLAG_BLEND_PREMULT; | |
383 else if (flip_win->attr.blend == TEGRA_FB_WIN_BLEND_COVERAGE) | |
384 win->flags |= TEGRA_WIN_FLAG_BLEND_COVERAGE; | |
385 win->fmt = flip_win->attr.pixformat; | |
386 win->x = flip_win->attr.x; | |
387 win->y = flip_win->attr.y; | |
388 win->w = flip_win->attr.w; | |
389 win->h = flip_win->attr.h; | |
390 win->out_x = flip_win->attr.out_x; | |
391 win->out_y = flip_win->attr.out_y; | |
392 win->out_w = flip_win->attr.out_w; | |
393 win->out_h = flip_win->attr.out_h; | |
394 win->z = flip_win->attr.z; | |
395 win->cur_handle = flip_win->handle; | |
396 | |
397 /* STOPSHIP verify that this won't read outside of the surface */ | |
398 win->phys_addr = flip_win->phys_addr + flip_win->attr.offset; | |
399 win->offset_u = flip_win->attr.offset_u + flip_win->attr.offset; | |
400 win->offset_v = flip_win->attr.offset_v + flip_win->attr.offset; | |
401 win->stride = flip_win->attr.stride; | |
402 win->stride_uv = flip_win->attr.stride_uv; | |
403 | |
404 if ((s32)flip_win->attr.pre_syncpt_id >= 0) { | |
405 nvhost_syncpt_wait_timeout(&tegra_fb->ndev->host->syncpt, | |
406 flip_win->attr.pre_syncpt_id, | |
407 flip_win->attr.pre_syncpt_val, | |
408 msecs_to_jiffies(500)); | |
409 } | |
410 | |
411 | |
412 return 0; | |
413 } | |
414 | |
415 static void tegra_fb_flip_worker(struct work_struct *work) | |
416 { | |
417 struct tegra_fb_flip_data *data = | |
418 container_of(work, struct tegra_fb_flip_data, work); | |
419 struct tegra_fb_info *tegra_fb = data->fb; | |
420 struct tegra_dc_win *win; | |
421 struct tegra_dc_win *wins[TEGRA_FB_FLIP_N_WINDOWS]; | |
422 struct nvmap_handle_ref *unpin_handles[TEGRA_FB_FLIP_N_WINDOWS]; | |
423 int i, nr_win = 0, nr_unpin = 0; | |
424 | |
425 data = container_of(work, struct tegra_fb_flip_data, work); | |
426 | |
427 for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) { | |
428 struct tegra_fb_flip_win *flip_win = &data->win[i]; | |
429 int idx = flip_win->attr.index; | |
430 win = tegra_dc_get_window(tegra_fb->win->dc, idx); | |
431 | |
432 if (!win) | |
433 continue; | |
434 | |
435 if (win->flags && win->cur_handle) | |
436 unpin_handles[nr_unpin++] = win->cur_handle; | |
437 | |
438 tegra_fb_set_windowattr(tegra_fb, win, &data->win[i]); | |
439 | |
440 wins[nr_win++] = win; | |
441 | |
442 #if 0 | |
443 if (flip_win->attr.pre_syncpt_id < 0) | |
444 continue; | |
445 printk("%08x %08x\n", | |
446 flip_win->attr.pre_syncpt_id, | |
447 flip_win->attr.pre_syncpt_val); | |
448 | |
449 nvhost_syncpt_wait_timeout(&tegra_fb->ndev->host->syncpt, | |
450 flip_win->attr.pre_syncpt_id, | |
451 flip_win->attr.pre_syncpt_val, | |
452 msecs_to_jiffies(500)); | |
453 #endif | |
454 } | |
455 | |
456 tegra_dc_update_windows(wins, nr_win); | |
457 /* TODO: implement swapinterval here */ | |
458 tegra_dc_sync_windows(wins, nr_win); | |
459 | |
460 tegra_dc_incr_syncpt_min(tegra_fb->win->dc, data->syncpt_max); | |
461 | |
462 /* unpin and deref previous front buffers */ | |
463 for (i = 0; i < nr_unpin; i++) { | |
464 nvmap_unpin(tegra_fb->fb_nvmap, unpin_handles[i]); | |
465 nvmap_free(tegra_fb->fb_nvmap, unpin_handles[i]); | |
466 } | |
467 | |
468 kfree(data); | |
469 } | |
470 | |
471 static int tegra_fb_flip(struct tegra_fb_info *tegra_fb, | |
472 struct tegra_fb_flip_args *args) | |
473 { | |
474 struct tegra_fb_flip_data *data; | |
475 struct tegra_fb_flip_win *flip_win; | |
476 u32 syncpt_max; | |
477 int i, err; | |
478 | |
479 if (WARN_ON(!tegra_fb->user_nvmap)) | |
480 return -EFAULT; | |
481 | |
482 if (WARN_ON(!tegra_fb->ndev)) | |
483 return -EFAULT; | |
484 | |
485 data = kzalloc(sizeof(*data), GFP_KERNEL); | |
486 if (data == NULL) { | |
487 dev_err(&tegra_fb->ndev->dev, | |
488 "can't allocate memory for flip\n"); | |
489 return -ENOMEM; | |
490 } | |
491 | |
492 INIT_WORK(&data->work, tegra_fb_flip_worker); | |
493 data->fb = tegra_fb; | |
494 | |
495 for (i = 0; i < TEGRA_FB_FLIP_N_WINDOWS; i++) { | |
496 flip_win = &data->win[i]; | |
497 | |
498 memcpy(&flip_win->attr, &args->win[i], sizeof(flip_win->attr)); | |
499 | |
500 err = tegra_fb_pin_window(tegra_fb, flip_win); | |
501 if (err < 0) { | |
502 dev_err(&tegra_fb->ndev->dev, | |
503 "error setting window attributes\n"); | |
504 goto surf_err; | |
505 } | |
506 } | |
507 | |
508 syncpt_max = tegra_dc_incr_syncpt_max(tegra_fb->win->dc); | |
509 data->syncpt_max = syncpt_max; | |
510 | |
511 queue_work(tegra_fb->flip_wq, &data->work); | |
512 | |
513 args->post_syncpt_val = syncpt_max; | |
514 args->post_syncpt_id = tegra_dc_get_syncpt_id(tegra_fb->win->dc); | |
515 | |
516 return 0; | |
517 | |
518 surf_err: | |
519 while (i--) { | |
520 if (data->win[i].handle) { | |
521 nvmap_unpin(tegra_fb->fb_nvmap, | |
522 data->win[i].handle); | |
523 nvmap_free(tegra_fb->fb_nvmap, | |
524 data->win[i].handle); | |
525 } | |
526 } | |
527 kfree(data); | |
528 return err; | |
529 } | |
530 | |
531 /* TODO: implement private window ioctls to set overlay x,y */ | |
532 | |
533 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long
arg) | 237 static int tegra_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long
arg) |
534 { | 238 { |
535 struct tegra_fb_info *tegra_fb = info->par; | |
536 struct tegra_fb_flip_args flip_args; | |
537 struct tegra_fb_modedb modedb; | 239 struct tegra_fb_modedb modedb; |
538 struct fb_modelist *modelist; | 240 struct fb_modelist *modelist; |
539 int i; | 241 int i; |
540 int fd; | |
541 int ret; | |
542 | 242 |
543 switch (cmd) { | 243 switch (cmd) { |
544 case FBIO_TEGRA_SET_NVMAP_FD: | |
545 if (copy_from_user(&fd, (void __user *)arg, sizeof(fd))) | |
546 return -EFAULT; | |
547 | |
548 return tegra_fb_set_nvmap_fd(tegra_fb, fd); | |
549 | |
550 case FBIO_TEGRA_FLIP: | |
551 if (copy_from_user(&flip_args, (void __user *)arg, sizeof(flip_a
rgs))) | |
552 return -EFAULT; | |
553 | |
554 ret = tegra_fb_flip(tegra_fb, &flip_args); | |
555 | |
556 if (copy_to_user((void __user *)arg, &flip_args, sizeof(flip_arg
s))) | |
557 return -EFAULT; | |
558 | |
559 return ret; | |
560 | |
561 case FBIO_TEGRA_GET_MODEDB: | 244 case FBIO_TEGRA_GET_MODEDB: |
562 if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb))) | 245 if (copy_from_user(&modedb, (void __user *)arg, sizeof(modedb))) |
563 return -EFAULT; | 246 return -EFAULT; |
564 | 247 |
565 i = 0; | 248 i = 0; |
566 list_for_each_entry(modelist, &info->modelist, list) { | 249 list_for_each_entry(modelist, &info->modelist, list) { |
567 struct fb_var_screeninfo var; | 250 struct fb_var_screeninfo var; |
568 | 251 |
569 if (i >= modedb.modedb_len) | 252 if (i >= modedb.modedb_len) |
570 break; | 253 break; |
(...skipping 12 matching lines...) Expand all Loading... |
583 | 266 |
584 default: | 267 default: |
585 return -ENOTTY; | 268 return -ENOTTY; |
586 } | 269 } |
587 | 270 |
588 return 0; | 271 return 0; |
589 } | 272 } |
590 | 273 |
591 static struct fb_ops tegra_fb_ops = { | 274 static struct fb_ops tegra_fb_ops = { |
592 .owner = THIS_MODULE, | 275 .owner = THIS_MODULE, |
593 .fb_open = tegra_fb_open, | |
594 .fb_release = tegra_fb_release, | |
595 .fb_check_var = tegra_fb_check_var, | 276 .fb_check_var = tegra_fb_check_var, |
596 .fb_set_par = tegra_fb_set_par, | 277 .fb_set_par = tegra_fb_set_par, |
597 .fb_setcolreg = tegra_fb_setcolreg, | 278 .fb_setcolreg = tegra_fb_setcolreg, |
598 .fb_blank = tegra_fb_blank, | 279 .fb_blank = tegra_fb_blank, |
599 .fb_pan_display = tegra_fb_pan_display, | 280 .fb_pan_display = tegra_fb_pan_display, |
600 .fb_fillrect = tegra_fb_fillrect, | 281 .fb_fillrect = tegra_fb_fillrect, |
601 .fb_copyarea = tegra_fb_copyarea, | 282 .fb_copyarea = tegra_fb_copyarea, |
602 .fb_imageblit = tegra_fb_imageblit, | 283 .fb_imageblit = tegra_fb_imageblit, |
603 .fb_ioctl = tegra_fb_ioctl, | 284 .fb_ioctl = tegra_fb_ioctl, |
604 }; | 285 }; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
686 ret = -ENOMEM; | 367 ret = -ENOMEM; |
687 goto err; | 368 goto err; |
688 } | 369 } |
689 | 370 |
690 tegra_fb = info->par; | 371 tegra_fb = info->par; |
691 tegra_fb->win = win; | 372 tegra_fb->win = win; |
692 tegra_fb->ndev = ndev; | 373 tegra_fb->ndev = ndev; |
693 tegra_fb->fb_mem = fb_mem; | 374 tegra_fb->fb_mem = fb_mem; |
694 tegra_fb->xres = fb_data->xres; | 375 tegra_fb->xres = fb_data->xres; |
695 tegra_fb->yres = fb_data->yres; | 376 tegra_fb->yres = fb_data->yres; |
696 tegra_fb->fb_nvmap = nvmap_create_client(nvmap_dev, "tegra-fb"); | |
697 if (!tegra_fb->fb_nvmap) { | |
698 dev_err(&ndev->dev, "couldn't create nvmap client\n"); | |
699 ret = -ENOMEM; | |
700 goto err_free; | |
701 } | |
702 atomic_set(&tegra_fb->in_use, 0); | |
703 | |
704 tegra_fb->flip_wq = create_singlethread_workqueue(dev_name(&ndev->dev)); | |
705 if (!tegra_fb->flip_wq) { | |
706 dev_err(&ndev->dev, "couldn't create flip work-queue\n"); | |
707 ret = -ENOMEM; | |
708 goto err_delete_wq; | |
709 } | |
710 | 377 |
711 if (fb_mem) { | 378 if (fb_mem) { |
712 fb_size = resource_size(fb_mem); | 379 fb_size = resource_size(fb_mem); |
713 fb_phys = fb_mem->start; | 380 fb_phys = fb_mem->start; |
714 fb_base = ioremap_nocache(fb_phys, fb_size); | 381 fb_base = ioremap_nocache(fb_phys, fb_size); |
715 if (!fb_base) { | 382 if (!fb_base) { |
716 dev_err(&ndev->dev, "fb can't be mapped\n"); | 383 dev_err(&ndev->dev, "fb can't be mapped\n"); |
717 ret = -EBUSY; | 384 ret = -EBUSY; |
718 » » » goto err_put_client; | 385 » » » goto err_free; |
719 } | 386 } |
720 tegra_fb->valid = true; | 387 tegra_fb->valid = true; |
721 } | 388 } |
722 | 389 |
723 info->fbops = &tegra_fb_ops; | 390 info->fbops = &tegra_fb_ops; |
724 info->pseudo_palette = pseudo_palette; | 391 info->pseudo_palette = pseudo_palette; |
725 info->screen_base = fb_base; | 392 info->screen_base = fb_base; |
726 info->screen_size = fb_size; | 393 info->screen_size = fb_size; |
727 | 394 |
728 strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id)); | 395 strlcpy(info->fix.id, "tegra_fb", sizeof(info->fix.id)); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
784 dev_info(&ndev->dev, "probed\n"); | 451 dev_info(&ndev->dev, "probed\n"); |
785 | 452 |
786 if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) { | 453 if (fb_data->flags & TEGRA_FB_FLIP_ON_PROBE) { |
787 tegra_dc_update_windows(&tegra_fb->win, 1); | 454 tegra_dc_update_windows(&tegra_fb->win, 1); |
788 tegra_dc_sync_windows(&tegra_fb->win, 1); | 455 tegra_dc_sync_windows(&tegra_fb->win, 1); |
789 } | 456 } |
790 | 457 |
791 return tegra_fb; | 458 return tegra_fb; |
792 | 459 |
793 err_iounmap_fb: | 460 err_iounmap_fb: |
794 » iounmap(fb_base); | 461 » if (fb_base) |
795 err_put_client: | 462 » » iounmap(fb_base); |
796 » nvmap_client_put(tegra_fb->fb_nvmap); | |
797 err_delete_wq: | |
798 » destroy_workqueue(tegra_fb->flip_wq); | |
799 err_free: | 463 err_free: |
800 framebuffer_release(info); | 464 framebuffer_release(info); |
801 err: | 465 err: |
802 return ERR_PTR(ret); | 466 return ERR_PTR(ret); |
803 } | 467 } |
804 | 468 |
805 void tegra_fb_unregister(struct tegra_fb_info *fb_info) | 469 void tegra_fb_unregister(struct tegra_fb_info *fb_info) |
806 { | 470 { |
807 struct fb_info *info = fb_info->info; | 471 struct fb_info *info = fb_info->info; |
808 | 472 |
809 if (fb_info->win->cur_handle) { | |
810 nvmap_unpin(fb_info->fb_nvmap, fb_info->win->cur_handle); | |
811 nvmap_free(fb_info->fb_nvmap, fb_info->win->cur_handle); | |
812 } | |
813 | |
814 if (fb_info->fb_nvmap) | |
815 nvmap_client_put(fb_info->fb_nvmap); | |
816 | |
817 unregister_framebuffer(info); | 473 unregister_framebuffer(info); |
818 | 474 |
819 flush_workqueue(fb_info->flip_wq); | |
820 destroy_workqueue(fb_info->flip_wq); | |
821 | |
822 iounmap(info->screen_base); | 475 iounmap(info->screen_base); |
823 framebuffer_release(info); | 476 framebuffer_release(info); |
824 } | 477 } |
OLD | NEW |