| 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 |