1926 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Framebuffer driver for TI OMAP boards4 *5 * Copyright (C) 2004 Nokia Corporation6 * Author: Imre Deak <imre.deak@nokia.com>7 *8 * Acknowledgements:9 * Alex McMains <aam@ridgerun.com> - Original driver10 * Juha Yrjola <juha.yrjola@nokia.com> - Original driver and improvements11 * Dirk Behme <dirk.behme@de.bosch.com> - changes for 2.6 kernel API12 * Texas Instruments - H3 support13 */14#include <linux/platform_device.h>15#include <linux/mm.h>16#include <linux/slab.h>17#include <linux/uaccess.h>18#include <linux/module.h>19#include <linux/sysfs.h>20 21#include <linux/omap-dma.h>22 23#include <linux/soc/ti/omap1-soc.h>24#include "omapfb.h"25#include "lcdc.h"26 27#define MODULE_NAME "omapfb"28 29static unsigned int def_accel;30static unsigned long def_vram[OMAPFB_PLANE_NUM];31static unsigned int def_vram_cnt;32static unsigned long def_vxres;33static unsigned long def_vyres;34static unsigned int def_rotate;35static unsigned int def_mirror;36 37static bool manual_update = IS_BUILTIN(CONFIG_FB_OMAP_MANUAL_UPDATE);38 39static struct platform_device *fbdev_pdev;40static struct lcd_panel *fbdev_panel;41static struct omapfb_device *omapfb_dev;42 43struct caps_table_struct {44 unsigned long flag;45 const char *name;46};47 48static const struct caps_table_struct ctrl_caps[] = {49 { OMAPFB_CAPS_MANUAL_UPDATE, "manual update" },50 { OMAPFB_CAPS_TEARSYNC, "tearing synchronization" },51 { OMAPFB_CAPS_PLANE_RELOCATE_MEM, "relocate plane memory" },52 { OMAPFB_CAPS_PLANE_SCALE, "scale plane" },53 { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },54 { OMAPFB_CAPS_WINDOW_SCALE, "scale window" },55 { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },56 { OMAPFB_CAPS_WINDOW_ROTATE, "rotate window" },57 { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" },58};59 60static const struct caps_table_struct color_caps[] = {61 { 1 << OMAPFB_COLOR_RGB565, "RGB565", },62 { 1 << OMAPFB_COLOR_YUV422, "YUV422", },63 { 1 << OMAPFB_COLOR_YUV420, "YUV420", },64 { 1 << OMAPFB_COLOR_CLUT_8BPP, "CLUT8", },65 { 1 << OMAPFB_COLOR_CLUT_4BPP, "CLUT4", },66 { 1 << OMAPFB_COLOR_CLUT_2BPP, "CLUT2", },67 { 1 << OMAPFB_COLOR_CLUT_1BPP, "CLUT1", },68 { 1 << OMAPFB_COLOR_RGB444, "RGB444", },69 { 1 << OMAPFB_COLOR_YUY422, "YUY422", },70};71 72static void omapdss_release(struct device *dev)73{74}75 76/* dummy device for clocks */77static struct platform_device omapdss_device = {78 .name = "omapdss_dss",79 .id = -1,80 .dev = {81 .release = omapdss_release,82 },83};84 85/*86 * ---------------------------------------------------------------------------87 * LCD panel88 * ---------------------------------------------------------------------------89 */90extern struct lcd_ctrl hwa742_ctrl;91 92static const struct lcd_ctrl *ctrls[] = {93 &omap1_int_ctrl,94 95#ifdef CONFIG_FB_OMAP_LCDC_HWA74296 &hwa742_ctrl,97#endif98};99 100#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL101extern struct lcd_ctrl_extif omap1_ext_if;102#endif103 104static void omapfb_rqueue_lock(struct omapfb_device *fbdev)105{106 mutex_lock(&fbdev->rqueue_mutex);107}108 109static void omapfb_rqueue_unlock(struct omapfb_device *fbdev)110{111 mutex_unlock(&fbdev->rqueue_mutex);112}113 114/*115 * ---------------------------------------------------------------------------116 * LCD controller and LCD DMA117 * ---------------------------------------------------------------------------118 */119/*120 * Allocate resources needed for LCD controller and LCD DMA operations. Video121 * memory is allocated from system memory according to the virtual display122 * size, except if a bigger memory size is specified explicitly as a kernel123 * parameter.124 */125static int ctrl_init(struct omapfb_device *fbdev)126{127 int r;128 int i;129 130 /* kernel/module vram parameters override boot tags/board config */131 if (def_vram_cnt) {132 for (i = 0; i < def_vram_cnt; i++)133 fbdev->mem_desc.region[i].size =134 PAGE_ALIGN(def_vram[i]);135 fbdev->mem_desc.region_cnt = i;136 }137 138 if (!fbdev->mem_desc.region_cnt) {139 struct lcd_panel *panel = fbdev->panel;140 int def_size;141 int bpp = panel->bpp;142 143 /* 12 bpp is packed in 16 bits */144 if (bpp == 12)145 bpp = 16;146 def_size = def_vxres * def_vyres * bpp / 8;147 fbdev->mem_desc.region_cnt = 1;148 fbdev->mem_desc.region[0].size = PAGE_ALIGN(def_size);149 }150 r = fbdev->ctrl->init(fbdev, 0, &fbdev->mem_desc);151 if (r < 0) {152 dev_err(fbdev->dev, "controller initialization failed (%d)\n",153 r);154 return r;155 }156 157#ifdef DEBUG158 for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {159 dev_dbg(fbdev->dev, "region%d phys %08x virt %p size=%lu\n",160 i,161 fbdev->mem_desc.region[i].paddr,162 fbdev->mem_desc.region[i].vaddr,163 fbdev->mem_desc.region[i].size);164 }165#endif166 return 0;167}168 169static void ctrl_cleanup(struct omapfb_device *fbdev)170{171 fbdev->ctrl->cleanup();172}173 174/* Must be called with fbdev->rqueue_mutex held. */175static int ctrl_change_mode(struct fb_info *fbi)176{177 int r;178 unsigned long offset;179 struct omapfb_plane_struct *plane = fbi->par;180 struct omapfb_device *fbdev = plane->fbdev;181 struct fb_var_screeninfo *var = &fbi->var;182 183 offset = var->yoffset * fbi->fix.line_length +184 var->xoffset * var->bits_per_pixel / 8;185 186 if (fbdev->ctrl->sync)187 fbdev->ctrl->sync();188 r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,189 offset, var->xres_virtual,190 plane->info.pos_x, plane->info.pos_y,191 var->xres, var->yres, plane->color_mode);192 if (r < 0)193 return r;194 195 if (fbdev->ctrl->set_rotate != NULL) {196 r = fbdev->ctrl->set_rotate(var->rotate);197 if (r < 0)198 return r;199 }200 201 if (fbdev->ctrl->set_scale != NULL)202 r = fbdev->ctrl->set_scale(plane->idx,203 var->xres, var->yres,204 plane->info.out_width,205 plane->info.out_height);206 207 return r;208}209 210/*211 * ---------------------------------------------------------------------------212 * fbdev framework callbacks and the ioctl interface213 * ---------------------------------------------------------------------------214 */215/* Called each time the omapfb device is opened */216static int omapfb_open(struct fb_info *info, int user)217{218 return 0;219}220 221static void omapfb_sync(struct fb_info *info);222 223/* Called when the omapfb device is closed. We make sure that any pending224 * gfx DMA operations are ended, before we return. */225static int omapfb_release(struct fb_info *info, int user)226{227 omapfb_sync(info);228 return 0;229}230 231/* Store a single color palette entry into a pseudo palette or the hardware232 * palette if one is available. For now we support only 16bpp and thus store233 * the entry only to the pseudo palette.234 */235static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,236 u_int blue, u_int transp, int update_hw_pal)237{238 struct omapfb_plane_struct *plane = info->par;239 struct omapfb_device *fbdev = plane->fbdev;240 struct fb_var_screeninfo *var = &info->var;241 int r = 0;242 243 switch (plane->color_mode) {244 case OMAPFB_COLOR_YUV422:245 case OMAPFB_COLOR_YUV420:246 case OMAPFB_COLOR_YUY422:247 r = -EINVAL;248 break;249 case OMAPFB_COLOR_CLUT_8BPP:250 case OMAPFB_COLOR_CLUT_4BPP:251 case OMAPFB_COLOR_CLUT_2BPP:252 case OMAPFB_COLOR_CLUT_1BPP:253 if (fbdev->ctrl->setcolreg)254 r = fbdev->ctrl->setcolreg(regno, red, green, blue,255 transp, update_hw_pal);256 fallthrough;257 case OMAPFB_COLOR_RGB565:258 case OMAPFB_COLOR_RGB444:259 if (r != 0)260 break;261 262 if (regno < 16) {263 u16 pal;264 pal = ((red >> (16 - var->red.length)) <<265 var->red.offset) |266 ((green >> (16 - var->green.length)) <<267 var->green.offset) |268 (blue >> (16 - var->blue.length));269 ((u32 *)(info->pseudo_palette))[regno] = pal;270 }271 break;272 default:273 BUG();274 }275 return r;276}277 278static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,279 u_int transp, struct fb_info *info)280{281 return _setcolreg(info, regno, red, green, blue, transp, 1);282}283 284static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)285{286 int count, index, r;287 u16 *red, *green, *blue, *transp;288 u16 trans = 0xffff;289 290 red = cmap->red;291 green = cmap->green;292 blue = cmap->blue;293 transp = cmap->transp;294 index = cmap->start;295 296 for (count = 0; count < cmap->len; count++) {297 if (transp)298 trans = *transp++;299 r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,300 count == cmap->len - 1);301 if (r != 0)302 return r;303 }304 305 return 0;306}307 308static int omapfb_update_full_screen(struct fb_info *fbi);309 310static int omapfb_blank(int blank, struct fb_info *fbi)311{312 struct omapfb_plane_struct *plane = fbi->par;313 struct omapfb_device *fbdev = plane->fbdev;314 int do_update = 0;315 int r = 0;316 317 omapfb_rqueue_lock(fbdev);318 switch (blank) {319 case FB_BLANK_UNBLANK:320 if (fbdev->state == OMAPFB_SUSPENDED) {321 if (fbdev->ctrl->resume)322 fbdev->ctrl->resume();323 if (fbdev->panel->enable)324 fbdev->panel->enable(fbdev->panel);325 fbdev->state = OMAPFB_ACTIVE;326 if (fbdev->ctrl->get_update_mode() ==327 OMAPFB_MANUAL_UPDATE)328 do_update = 1;329 }330 break;331 case FB_BLANK_POWERDOWN:332 if (fbdev->state == OMAPFB_ACTIVE) {333 if (fbdev->panel->disable)334 fbdev->panel->disable(fbdev->panel);335 if (fbdev->ctrl->suspend)336 fbdev->ctrl->suspend();337 fbdev->state = OMAPFB_SUSPENDED;338 }339 break;340 default:341 r = -EINVAL;342 }343 omapfb_rqueue_unlock(fbdev);344 345 if (r == 0 && do_update)346 r = omapfb_update_full_screen(fbi);347 348 return r;349}350 351static void omapfb_sync(struct fb_info *fbi)352{353 struct omapfb_plane_struct *plane = fbi->par;354 struct omapfb_device *fbdev = plane->fbdev;355 356 omapfb_rqueue_lock(fbdev);357 if (fbdev->ctrl->sync)358 fbdev->ctrl->sync();359 omapfb_rqueue_unlock(fbdev);360}361 362/*363 * Set fb_info.fix fields and also updates fbdev.364 * When calling this fb_info.var must be set up already.365 */366static void set_fb_fix(struct fb_info *fbi, int from_init)367{368 struct fb_fix_screeninfo *fix = &fbi->fix;369 struct fb_var_screeninfo *var = &fbi->var;370 struct omapfb_plane_struct *plane = fbi->par;371 struct omapfb_mem_region *rg;372 int bpp;373 374 rg = &plane->fbdev->mem_desc.region[plane->idx];375 fbi->screen_base = rg->vaddr;376 377 if (!from_init) {378 mutex_lock(&fbi->mm_lock);379 fix->smem_start = rg->paddr;380 fix->smem_len = rg->size;381 mutex_unlock(&fbi->mm_lock);382 } else {383 fix->smem_start = rg->paddr;384 fix->smem_len = rg->size;385 }386 387 fix->type = FB_TYPE_PACKED_PIXELS;388 bpp = var->bits_per_pixel;389 if (var->nonstd)390 fix->visual = FB_VISUAL_PSEUDOCOLOR;391 else switch (var->bits_per_pixel) {392 case 16:393 case 12:394 fix->visual = FB_VISUAL_TRUECOLOR;395 /* 12bpp is stored in 16 bits */396 bpp = 16;397 break;398 case 1:399 case 2:400 case 4:401 case 8:402 fix->visual = FB_VISUAL_PSEUDOCOLOR;403 break;404 }405 fix->accel = FB_ACCEL_OMAP1610;406 fix->line_length = var->xres_virtual * bpp / 8;407}408 409static int set_color_mode(struct omapfb_plane_struct *plane,410 struct fb_var_screeninfo *var)411{412 switch (var->nonstd) {413 case 0:414 break;415 case OMAPFB_COLOR_YUV422:416 var->bits_per_pixel = 16;417 plane->color_mode = var->nonstd;418 return 0;419 case OMAPFB_COLOR_YUV420:420 var->bits_per_pixel = 12;421 plane->color_mode = var->nonstd;422 return 0;423 case OMAPFB_COLOR_YUY422:424 var->bits_per_pixel = 16;425 plane->color_mode = var->nonstd;426 return 0;427 default:428 return -EINVAL;429 }430 431 switch (var->bits_per_pixel) {432 case 1:433 plane->color_mode = OMAPFB_COLOR_CLUT_1BPP;434 return 0;435 case 2:436 plane->color_mode = OMAPFB_COLOR_CLUT_2BPP;437 return 0;438 case 4:439 plane->color_mode = OMAPFB_COLOR_CLUT_4BPP;440 return 0;441 case 8:442 plane->color_mode = OMAPFB_COLOR_CLUT_8BPP;443 return 0;444 case 12:445 var->bits_per_pixel = 16;446 fallthrough;447 case 16:448 if (plane->fbdev->panel->bpp == 12)449 plane->color_mode = OMAPFB_COLOR_RGB444;450 else451 plane->color_mode = OMAPFB_COLOR_RGB565;452 return 0;453 default:454 return -EINVAL;455 }456}457 458/*459 * Check the values in var against our capabilities and in case of out of460 * bound values try to adjust them.461 */462static int set_fb_var(struct fb_info *fbi,463 struct fb_var_screeninfo *var)464{465 int bpp;466 unsigned long max_frame_size;467 unsigned long line_size;468 int xres_min, xres_max;469 int yres_min, yres_max;470 struct omapfb_plane_struct *plane = fbi->par;471 struct omapfb_device *fbdev = plane->fbdev;472 struct lcd_panel *panel = fbdev->panel;473 474 if (set_color_mode(plane, var) < 0)475 return -EINVAL;476 477 bpp = var->bits_per_pixel;478 if (plane->color_mode == OMAPFB_COLOR_RGB444)479 bpp = 16;480 481 switch (var->rotate) {482 case 0:483 case 180:484 xres_min = OMAPFB_PLANE_XRES_MIN;485 xres_max = panel->x_res;486 yres_min = OMAPFB_PLANE_YRES_MIN;487 yres_max = panel->y_res;488 if (cpu_is_omap15xx()) {489 var->xres = panel->x_res;490 var->yres = panel->y_res;491 }492 break;493 case 90:494 case 270:495 xres_min = OMAPFB_PLANE_YRES_MIN;496 xres_max = panel->y_res;497 yres_min = OMAPFB_PLANE_XRES_MIN;498 yres_max = panel->x_res;499 if (cpu_is_omap15xx()) {500 var->xres = panel->y_res;501 var->yres = panel->x_res;502 }503 break;504 default:505 return -EINVAL;506 }507 508 if (var->xres < xres_min)509 var->xres = xres_min;510 if (var->yres < yres_min)511 var->yres = yres_min;512 if (var->xres > xres_max)513 var->xres = xres_max;514 if (var->yres > yres_max)515 var->yres = yres_max;516 517 if (var->xres_virtual < var->xres)518 var->xres_virtual = var->xres;519 if (var->yres_virtual < var->yres)520 var->yres_virtual = var->yres;521 max_frame_size = fbdev->mem_desc.region[plane->idx].size;522 line_size = var->xres_virtual * bpp / 8;523 if (line_size * var->yres_virtual > max_frame_size) {524 /* Try to keep yres_virtual first */525 line_size = max_frame_size / var->yres_virtual;526 var->xres_virtual = line_size * 8 / bpp;527 if (var->xres_virtual < var->xres) {528 /* Still doesn't fit. Shrink yres_virtual too */529 var->xres_virtual = var->xres;530 line_size = var->xres * bpp / 8;531 var->yres_virtual = max_frame_size / line_size;532 }533 /* Recheck this, as the virtual size changed. */534 if (var->xres_virtual < var->xres)535 var->xres = var->xres_virtual;536 if (var->yres_virtual < var->yres)537 var->yres = var->yres_virtual;538 if (var->xres < xres_min || var->yres < yres_min)539 return -EINVAL;540 }541 if (var->xres + var->xoffset > var->xres_virtual)542 var->xoffset = var->xres_virtual - var->xres;543 if (var->yres + var->yoffset > var->yres_virtual)544 var->yoffset = var->yres_virtual - var->yres;545 546 if (plane->color_mode == OMAPFB_COLOR_RGB444) {547 var->red.offset = 8;548 var->red.length = 4;549 var->red.msb_right = 0;550 var->green.offset = 4;551 var->green.length = 4;552 var->green.msb_right = 0;553 var->blue.offset = 0;554 var->blue.length = 4;555 var->blue.msb_right = 0;556 } else {557 var->red.offset = 11;558 var->red.length = 5;559 var->red.msb_right = 0;560 var->green.offset = 5;561 var->green.length = 6;562 var->green.msb_right = 0;563 var->blue.offset = 0;564 var->blue.length = 5;565 var->blue.msb_right = 0;566 }567 568 var->height = -1;569 var->width = -1;570 var->grayscale = 0;571 572 /* pixclock in ps, the rest in pixclock */573 var->pixclock = 10000000 / (panel->pixel_clock / 100);574 var->left_margin = panel->hfp;575 var->right_margin = panel->hbp;576 var->upper_margin = panel->vfp;577 var->lower_margin = panel->vbp;578 var->hsync_len = panel->hsw;579 var->vsync_len = panel->vsw;580 581 /* TODO: get these from panel->config */582 var->vmode = FB_VMODE_NONINTERLACED;583 var->sync = 0;584 585 return 0;586}587 588 589/*590 * Set new x,y offsets in the virtual display for the visible area and switch591 * to the new mode.592 */593static int omapfb_pan_display(struct fb_var_screeninfo *var,594 struct fb_info *fbi)595{596 struct omapfb_plane_struct *plane = fbi->par;597 struct omapfb_device *fbdev = plane->fbdev;598 int r = 0;599 600 omapfb_rqueue_lock(fbdev);601 if (var->xoffset != fbi->var.xoffset ||602 var->yoffset != fbi->var.yoffset) {603 struct fb_var_screeninfo *new_var = &fbdev->new_var;604 605 memcpy(new_var, &fbi->var, sizeof(*new_var));606 new_var->xoffset = var->xoffset;607 new_var->yoffset = var->yoffset;608 if (set_fb_var(fbi, new_var))609 r = -EINVAL;610 else {611 memcpy(&fbi->var, new_var, sizeof(*new_var));612 ctrl_change_mode(fbi);613 }614 }615 omapfb_rqueue_unlock(fbdev);616 617 return r;618}619 620/* Set mirror to vertical axis and switch to the new mode. */621static int omapfb_mirror(struct fb_info *fbi, int mirror)622{623 struct omapfb_plane_struct *plane = fbi->par;624 struct omapfb_device *fbdev = plane->fbdev;625 int r = 0;626 627 omapfb_rqueue_lock(fbdev);628 mirror = mirror ? 1 : 0;629 if (cpu_is_omap15xx())630 r = -EINVAL;631 else if (mirror != plane->info.mirror) {632 plane->info.mirror = mirror;633 r = ctrl_change_mode(fbi);634 }635 omapfb_rqueue_unlock(fbdev);636 637 return r;638}639 640/*641 * Check values in var, try to adjust them in case of out of bound values if642 * possible, or return error.643 */644static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)645{646 struct omapfb_plane_struct *plane = fbi->par;647 struct omapfb_device *fbdev = plane->fbdev;648 int r;649 650 omapfb_rqueue_lock(fbdev);651 if (fbdev->ctrl->sync != NULL)652 fbdev->ctrl->sync();653 r = set_fb_var(fbi, var);654 omapfb_rqueue_unlock(fbdev);655 656 return r;657}658 659/*660 * Switch to a new mode. The parameters for it has been check already by661 * omapfb_check_var.662 */663static int omapfb_set_par(struct fb_info *fbi)664{665 struct omapfb_plane_struct *plane = fbi->par;666 struct omapfb_device *fbdev = plane->fbdev;667 int r = 0;668 669 omapfb_rqueue_lock(fbdev);670 set_fb_fix(fbi, 0);671 r = ctrl_change_mode(fbi);672 omapfb_rqueue_unlock(fbdev);673 674 return r;675}676 677static int omapfb_update_window_async(struct fb_info *fbi,678 struct omapfb_update_window *win,679 void (*callback)(void *),680 void *callback_data)681{682 int xres, yres;683 struct omapfb_plane_struct *plane = fbi->par;684 struct omapfb_device *fbdev = plane->fbdev;685 struct fb_var_screeninfo *var = &fbi->var;686 687 switch (var->rotate) {688 case 0:689 case 180:690 xres = fbdev->panel->x_res;691 yres = fbdev->panel->y_res;692 break;693 case 90:694 case 270:695 xres = fbdev->panel->y_res;696 yres = fbdev->panel->x_res;697 break;698 default:699 return -EINVAL;700 }701 702 if (win->x >= xres || win->y >= yres ||703 win->out_x > xres || win->out_y > yres)704 return -EINVAL;705 706 if (!fbdev->ctrl->update_window ||707 fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)708 return -ENODEV;709 710 if (win->x + win->width > xres)711 win->width = xres - win->x;712 if (win->y + win->height > yres)713 win->height = yres - win->y;714 if (win->out_x + win->out_width > xres)715 win->out_width = xres - win->out_x;716 if (win->out_y + win->out_height > yres)717 win->out_height = yres - win->out_y;718 if (!win->width || !win->height || !win->out_width || !win->out_height)719 return 0;720 721 return fbdev->ctrl->update_window(fbi, win, callback, callback_data);722}723 724static int omapfb_update_win(struct fb_info *fbi,725 struct omapfb_update_window *win)726{727 struct omapfb_plane_struct *plane = fbi->par;728 int ret;729 730 omapfb_rqueue_lock(plane->fbdev);731 ret = omapfb_update_window_async(fbi, win, NULL, NULL);732 omapfb_rqueue_unlock(plane->fbdev);733 734 return ret;735}736 737static int omapfb_update_full_screen(struct fb_info *fbi)738{739 struct omapfb_plane_struct *plane = fbi->par;740 struct omapfb_device *fbdev = plane->fbdev;741 struct omapfb_update_window win;742 int r;743 744 if (!fbdev->ctrl->update_window ||745 fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)746 return -ENODEV;747 748 win.x = 0;749 win.y = 0;750 win.width = fbi->var.xres;751 win.height = fbi->var.yres;752 win.out_x = 0;753 win.out_y = 0;754 win.out_width = fbi->var.xres;755 win.out_height = fbi->var.yres;756 win.format = 0;757 758 omapfb_rqueue_lock(fbdev);759 r = fbdev->ctrl->update_window(fbi, &win, NULL, NULL);760 omapfb_rqueue_unlock(fbdev);761 762 return r;763}764 765static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)766{767 struct omapfb_plane_struct *plane = fbi->par;768 struct omapfb_device *fbdev = plane->fbdev;769 struct lcd_panel *panel = fbdev->panel;770 struct omapfb_plane_info old_info;771 int r = 0;772 773 if (pi->pos_x + pi->out_width > panel->x_res ||774 pi->pos_y + pi->out_height > panel->y_res)775 return -EINVAL;776 777 omapfb_rqueue_lock(fbdev);778 if (pi->enabled && !fbdev->mem_desc.region[plane->idx].size) {779 /*780 * This plane's memory was freed, can't enable it781 * until it's reallocated.782 */783 r = -EINVAL;784 goto out;785 }786 old_info = plane->info;787 plane->info = *pi;788 if (pi->enabled) {789 r = ctrl_change_mode(fbi);790 if (r < 0) {791 plane->info = old_info;792 goto out;793 }794 }795 r = fbdev->ctrl->enable_plane(plane->idx, pi->enabled);796 if (r < 0) {797 plane->info = old_info;798 goto out;799 }800out:801 omapfb_rqueue_unlock(fbdev);802 return r;803}804 805static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)806{807 struct omapfb_plane_struct *plane = fbi->par;808 809 *pi = plane->info;810 return 0;811}812 813static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)814{815 struct omapfb_plane_struct *plane = fbi->par;816 struct omapfb_device *fbdev = plane->fbdev;817 struct omapfb_mem_region *rg = &fbdev->mem_desc.region[plane->idx];818 size_t size;819 int r = 0;820 821 if (fbdev->ctrl->setup_mem == NULL)822 return -ENODEV;823 if (mi->type != OMAPFB_MEMTYPE_SDRAM)824 return -EINVAL;825 826 size = PAGE_ALIGN(mi->size);827 omapfb_rqueue_lock(fbdev);828 if (plane->info.enabled) {829 r = -EBUSY;830 goto out;831 }832 if (rg->size != size || rg->type != mi->type) {833 struct fb_var_screeninfo *new_var = &fbdev->new_var;834 unsigned long old_size = rg->size;835 u8 old_type = rg->type;836 unsigned long paddr;837 838 rg->size = size;839 rg->type = mi->type;840 /*841 * size == 0 is a special case, for which we842 * don't check / adjust the screen parameters.843 * This isn't a problem since the plane can't844 * be reenabled unless its size is > 0.845 */846 if (old_size != size && size) {847 if (size) {848 memcpy(new_var, &fbi->var, sizeof(*new_var));849 r = set_fb_var(fbi, new_var);850 if (r < 0)851 goto out;852 }853 }854 855 if (fbdev->ctrl->sync)856 fbdev->ctrl->sync();857 r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr);858 if (r < 0) {859 /* Revert changes. */860 rg->size = old_size;861 rg->type = old_type;862 goto out;863 }864 rg->paddr = paddr;865 866 if (old_size != size) {867 if (size) {868 memcpy(&fbi->var, new_var, sizeof(fbi->var));869 set_fb_fix(fbi, 0);870 } else {871 /*872 * Set these explicitly to indicate that the873 * plane memory is dealloce'd, the other874 * screen parameters in var / fix are invalid.875 */876 mutex_lock(&fbi->mm_lock);877 fbi->fix.smem_start = 0;878 fbi->fix.smem_len = 0;879 mutex_unlock(&fbi->mm_lock);880 }881 }882 }883out:884 omapfb_rqueue_unlock(fbdev);885 886 return r;887}888 889static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)890{891 struct omapfb_plane_struct *plane = fbi->par;892 struct omapfb_device *fbdev = plane->fbdev;893 struct omapfb_mem_region *rg;894 895 rg = &fbdev->mem_desc.region[plane->idx];896 memset(mi, 0, sizeof(*mi));897 mi->size = rg->size;898 mi->type = rg->type;899 900 return 0;901}902 903static int omapfb_set_color_key(struct omapfb_device *fbdev,904 struct omapfb_color_key *ck)905{906 int r;907 908 if (!fbdev->ctrl->set_color_key)909 return -ENODEV;910 911 omapfb_rqueue_lock(fbdev);912 r = fbdev->ctrl->set_color_key(ck);913 omapfb_rqueue_unlock(fbdev);914 915 return r;916}917 918static int omapfb_get_color_key(struct omapfb_device *fbdev,919 struct omapfb_color_key *ck)920{921 int r;922 923 if (!fbdev->ctrl->get_color_key)924 return -ENODEV;925 926 omapfb_rqueue_lock(fbdev);927 r = fbdev->ctrl->get_color_key(ck);928 omapfb_rqueue_unlock(fbdev);929 930 return r;931}932 933static struct blocking_notifier_head omapfb_client_list[OMAPFB_PLANE_NUM];934static int notifier_inited;935 936static void omapfb_init_notifier(void)937{938 int i;939 940 for (i = 0; i < OMAPFB_PLANE_NUM; i++)941 BLOCKING_INIT_NOTIFIER_HEAD(&omapfb_client_list[i]);942}943 944int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb,945 omapfb_notifier_callback_t callback,946 void *callback_data)947{948 int r;949 950 if ((unsigned)omapfb_nb->plane_idx >= OMAPFB_PLANE_NUM)951 return -EINVAL;952 953 if (!notifier_inited) {954 omapfb_init_notifier();955 notifier_inited = 1;956 }957 958 omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *,959 unsigned long, void *))callback;960 omapfb_nb->data = callback_data;961 r = blocking_notifier_chain_register(962 &omapfb_client_list[omapfb_nb->plane_idx],963 &omapfb_nb->nb);964 if (r)965 return r;966 if (omapfb_dev != NULL &&967 omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) {968 omapfb_dev->ctrl->bind_client(omapfb_nb);969 }970 971 return 0;972}973EXPORT_SYMBOL(omapfb_register_client);974 975int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb)976{977 return blocking_notifier_chain_unregister(978 &omapfb_client_list[omapfb_nb->plane_idx], &omapfb_nb->nb);979}980EXPORT_SYMBOL(omapfb_unregister_client);981 982void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event)983{984 int i;985 986 if (!notifier_inited)987 /* no client registered yet */988 return;989 990 for (i = 0; i < OMAPFB_PLANE_NUM; i++)991 blocking_notifier_call_chain(&omapfb_client_list[i], event,992 fbdev->fb_info[i]);993}994EXPORT_SYMBOL(omapfb_notify_clients);995 996static int omapfb_set_update_mode(struct omapfb_device *fbdev,997 enum omapfb_update_mode mode)998{999 int r;1000 1001 omapfb_rqueue_lock(fbdev);1002 r = fbdev->ctrl->set_update_mode(mode);1003 omapfb_rqueue_unlock(fbdev);1004 1005 return r;1006}1007 1008static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)1009{1010 int r;1011 1012 omapfb_rqueue_lock(fbdev);1013 r = fbdev->ctrl->get_update_mode();1014 omapfb_rqueue_unlock(fbdev);1015 1016 return r;1017}1018 1019static void omapfb_get_caps(struct omapfb_device *fbdev, int plane,1020 struct omapfb_caps *caps)1021{1022 memset(caps, 0, sizeof(*caps));1023 fbdev->ctrl->get_caps(plane, caps);1024 if (fbdev->panel->get_caps)1025 caps->ctrl |= fbdev->panel->get_caps(fbdev->panel);1026}1027 1028/* For lcd testing */1029void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval)1030{1031 omapfb_rqueue_lock(fbdev);1032 *(u16 *)fbdev->mem_desc.region[0].vaddr = pixval;1033 if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) {1034 struct omapfb_update_window win;1035 1036 memset(&win, 0, sizeof(win));1037 win.width = 2;1038 win.height = 2;1039 win.out_width = 2;1040 win.out_height = 2;1041 fbdev->ctrl->update_window(fbdev->fb_info[0], &win, NULL, NULL);1042 }1043 omapfb_rqueue_unlock(fbdev);1044}1045EXPORT_SYMBOL(omapfb_write_first_pixel);1046 1047/*1048 * Ioctl interface. Part of the kernel mode frame buffer API is duplicated1049 * here to be accessible by user mode code.1050 */1051static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd,1052 unsigned long arg)1053{1054 struct omapfb_plane_struct *plane = fbi->par;1055 struct omapfb_device *fbdev = plane->fbdev;1056 const struct fb_ops *ops = fbi->fbops;1057 union {1058 struct omapfb_update_window update_window;1059 struct omapfb_plane_info plane_info;1060 struct omapfb_mem_info mem_info;1061 struct omapfb_color_key color_key;1062 enum omapfb_update_mode update_mode;1063 struct omapfb_caps caps;1064 unsigned int mirror;1065 int plane_out;1066 int enable_plane;1067 } p;1068 int r = 0;1069 1070 BUG_ON(!ops);1071 switch (cmd) {1072 case OMAPFB_MIRROR:1073 if (get_user(p.mirror, (int __user *)arg))1074 r = -EFAULT;1075 else1076 omapfb_mirror(fbi, p.mirror);1077 break;1078 case OMAPFB_SYNC_GFX:1079 omapfb_sync(fbi);1080 break;1081 case OMAPFB_VSYNC:1082 break;1083 case OMAPFB_SET_UPDATE_MODE:1084 if (get_user(p.update_mode, (int __user *)arg))1085 r = -EFAULT;1086 else1087 r = omapfb_set_update_mode(fbdev, p.update_mode);1088 break;1089 case OMAPFB_GET_UPDATE_MODE:1090 p.update_mode = omapfb_get_update_mode(fbdev);1091 if (put_user(p.update_mode,1092 (enum omapfb_update_mode __user *)arg))1093 r = -EFAULT;1094 break;1095 case OMAPFB_UPDATE_WINDOW_OLD:1096 if (copy_from_user(&p.update_window, (void __user *)arg,1097 sizeof(struct omapfb_update_window_old)))1098 r = -EFAULT;1099 else {1100 struct omapfb_update_window *u = &p.update_window;1101 u->out_x = u->x;1102 u->out_y = u->y;1103 u->out_width = u->width;1104 u->out_height = u->height;1105 memset(u->reserved, 0, sizeof(u->reserved));1106 r = omapfb_update_win(fbi, u);1107 }1108 break;1109 case OMAPFB_UPDATE_WINDOW:1110 if (copy_from_user(&p.update_window, (void __user *)arg,1111 sizeof(p.update_window)))1112 r = -EFAULT;1113 else1114 r = omapfb_update_win(fbi, &p.update_window);1115 break;1116 case OMAPFB_SETUP_PLANE:1117 if (copy_from_user(&p.plane_info, (void __user *)arg,1118 sizeof(p.plane_info)))1119 r = -EFAULT;1120 else1121 r = omapfb_setup_plane(fbi, &p.plane_info);1122 break;1123 case OMAPFB_QUERY_PLANE:1124 if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0)1125 break;1126 if (copy_to_user((void __user *)arg, &p.plane_info,1127 sizeof(p.plane_info)))1128 r = -EFAULT;1129 break;1130 case OMAPFB_SETUP_MEM:1131 if (copy_from_user(&p.mem_info, (void __user *)arg,1132 sizeof(p.mem_info)))1133 r = -EFAULT;1134 else1135 r = omapfb_setup_mem(fbi, &p.mem_info);1136 break;1137 case OMAPFB_QUERY_MEM:1138 if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0)1139 break;1140 if (copy_to_user((void __user *)arg, &p.mem_info,1141 sizeof(p.mem_info)))1142 r = -EFAULT;1143 break;1144 case OMAPFB_SET_COLOR_KEY:1145 if (copy_from_user(&p.color_key, (void __user *)arg,1146 sizeof(p.color_key)))1147 r = -EFAULT;1148 else1149 r = omapfb_set_color_key(fbdev, &p.color_key);1150 break;1151 case OMAPFB_GET_COLOR_KEY:1152 if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0)1153 break;1154 if (copy_to_user((void __user *)arg, &p.color_key,1155 sizeof(p.color_key)))1156 r = -EFAULT;1157 break;1158 case OMAPFB_GET_CAPS:1159 omapfb_get_caps(fbdev, plane->idx, &p.caps);1160 if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))1161 r = -EFAULT;1162 break;1163 case OMAPFB_LCD_TEST:1164 {1165 int test_num;1166 1167 if (get_user(test_num, (int __user *)arg)) {1168 r = -EFAULT;1169 break;1170 }1171 if (!fbdev->panel->run_test) {1172 r = -EINVAL;1173 break;1174 }1175 r = fbdev->panel->run_test(fbdev->panel, test_num);1176 break;1177 }1178 case OMAPFB_CTRL_TEST:1179 {1180 int test_num;1181 1182 if (get_user(test_num, (int __user *)arg)) {1183 r = -EFAULT;1184 break;1185 }1186 if (!fbdev->ctrl->run_test) {1187 r = -EINVAL;1188 break;1189 }1190 r = fbdev->ctrl->run_test(test_num);1191 break;1192 }1193 default:1194 r = -EINVAL;1195 }1196 1197 return r;1198}1199 1200static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma)1201{1202 struct omapfb_plane_struct *plane = info->par;1203 struct omapfb_device *fbdev = plane->fbdev;1204 int r;1205 1206 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);1207 1208 omapfb_rqueue_lock(fbdev);1209 r = fbdev->ctrl->mmap(info, vma);1210 omapfb_rqueue_unlock(fbdev);1211 1212 return r;1213}1214 1215/*1216 * Callback table for the frame buffer framework. Some of these pointers1217 * will be changed according to the current setting of fb_info->accel_flags.1218 */1219static struct fb_ops omapfb_ops = {1220 .owner = THIS_MODULE,1221 FB_DEFAULT_IOMEM_OPS,1222 .fb_open = omapfb_open,1223 .fb_release = omapfb_release,1224 .fb_setcolreg = omapfb_setcolreg,1225 .fb_setcmap = omapfb_setcmap,1226 .fb_blank = omapfb_blank,1227 .fb_ioctl = omapfb_ioctl,1228 .fb_check_var = omapfb_check_var,1229 .fb_set_par = omapfb_set_par,1230 .fb_pan_display = omapfb_pan_display,1231};1232 1233/*1234 * ---------------------------------------------------------------------------1235 * Sysfs interface1236 * ---------------------------------------------------------------------------1237 */1238/* omapfbX sysfs entries */1239static ssize_t omapfb_show_caps_num(struct device *dev,1240 struct device_attribute *attr, char *buf)1241{1242 struct omapfb_device *fbdev = dev_get_drvdata(dev);1243 int plane;1244 size_t size = 0;1245 struct omapfb_caps caps;1246 1247 plane = 0;1248 while (plane < OMAPFB_PLANE_NUM) {1249 omapfb_get_caps(fbdev, plane, &caps);1250 size += sysfs_emit_at(buf, size,1251 "plane#%d %#010x %#010x %#010x\n",1252 plane, caps.ctrl, caps.plane_color, caps.wnd_color);1253 plane++;1254 }1255 return size;1256}1257 1258static ssize_t omapfb_show_caps_text(struct device *dev,1259 struct device_attribute *attr, char *buf)1260{1261 struct omapfb_device *fbdev = dev_get_drvdata(dev);1262 int i;1263 struct omapfb_caps caps;1264 int plane;1265 size_t size = 0;1266 1267 plane = 0;1268 while (plane < OMAPFB_PLANE_NUM) {1269 omapfb_get_caps(fbdev, plane, &caps);1270 size += sysfs_emit_at(buf, size, "plane#%d:\n", plane);1271 for (i = 0; i < ARRAY_SIZE(ctrl_caps); i++) {1272 if (ctrl_caps[i].flag & caps.ctrl)1273 size += sysfs_emit_at(buf, size,1274 " %s\n", ctrl_caps[i].name);1275 }1276 size += sysfs_emit_at(buf, size, " plane colors:\n");1277 for (i = 0; i < ARRAY_SIZE(color_caps); i++) {1278 if (color_caps[i].flag & caps.plane_color)1279 size += sysfs_emit_at(buf, size,1280 " %s\n", color_caps[i].name);1281 }1282 size += sysfs_emit_at(buf, size, " window colors:\n");1283 for (i = 0; i < ARRAY_SIZE(color_caps); i++) {1284 if (color_caps[i].flag & caps.wnd_color)1285 size += sysfs_emit_at(buf, size,1286 " %s\n", color_caps[i].name);1287 }1288 1289 plane++;1290 }1291 return size;1292}1293 1294static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL);1295static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL);1296 1297/* panel sysfs entries */1298static ssize_t omapfb_show_panel_name(struct device *dev,1299 struct device_attribute *attr, char *buf)1300{1301 struct omapfb_device *fbdev = dev_get_drvdata(dev);1302 1303 return sysfs_emit(buf, "%s\n", fbdev->panel->name);1304}1305 1306static ssize_t omapfb_show_bklight_level(struct device *dev,1307 struct device_attribute *attr,1308 char *buf)1309{1310 struct omapfb_device *fbdev = dev_get_drvdata(dev);1311 int r;1312 1313 if (fbdev->panel->get_bklight_level) {1314 r = sysfs_emit(buf, "%d\n",1315 fbdev->panel->get_bklight_level(fbdev->panel));1316 } else1317 r = -ENODEV;1318 return r;1319}1320 1321static ssize_t omapfb_store_bklight_level(struct device *dev,1322 struct device_attribute *attr,1323 const char *buf, size_t size)1324{1325 struct omapfb_device *fbdev = dev_get_drvdata(dev);1326 int r;1327 1328 if (fbdev->panel->set_bklight_level) {1329 unsigned int level;1330 1331 if (sscanf(buf, "%10d", &level) == 1) {1332 r = fbdev->panel->set_bklight_level(fbdev->panel,1333 level);1334 } else1335 r = -EINVAL;1336 } else1337 r = -ENODEV;1338 return r ? r : size;1339}1340 1341static ssize_t omapfb_show_bklight_max(struct device *dev,1342 struct device_attribute *attr, char *buf)1343{1344 struct omapfb_device *fbdev = dev_get_drvdata(dev);1345 int r;1346 1347 if (fbdev->panel->get_bklight_level) {1348 r = sysfs_emit(buf, "%d\n",1349 fbdev->panel->get_bklight_max(fbdev->panel));1350 } else1351 r = -ENODEV;1352 return r;1353}1354 1355static struct device_attribute dev_attr_panel_name =1356 __ATTR(name, 0444, omapfb_show_panel_name, NULL);1357static DEVICE_ATTR(backlight_level, 0664,1358 omapfb_show_bklight_level, omapfb_store_bklight_level);1359static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL);1360 1361static struct attribute *panel_attrs[] = {1362 &dev_attr_panel_name.attr,1363 &dev_attr_backlight_level.attr,1364 &dev_attr_backlight_max.attr,1365 NULL,1366};1367 1368static const struct attribute_group panel_attr_grp = {1369 .name = "panel",1370 .attrs = panel_attrs,1371};1372 1373/* ctrl sysfs entries */1374static ssize_t omapfb_show_ctrl_name(struct device *dev,1375 struct device_attribute *attr, char *buf)1376{1377 struct omapfb_device *fbdev = dev_get_drvdata(dev);1378 1379 return sysfs_emit(buf, "%s\n", fbdev->ctrl->name);1380}1381 1382static struct device_attribute dev_attr_ctrl_name =1383 __ATTR(name, 0444, omapfb_show_ctrl_name, NULL);1384 1385static struct attribute *ctrl_attrs[] = {1386 &dev_attr_ctrl_name.attr,1387 NULL,1388};1389 1390static const struct attribute_group ctrl_attr_grp = {1391 .name = "ctrl",1392 .attrs = ctrl_attrs,1393};1394 1395static int omapfb_register_sysfs(struct omapfb_device *fbdev)1396{1397 int r;1398 1399 if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num)))1400 goto fail0;1401 1402 if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text)))1403 goto fail1;1404 1405 if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp)))1406 goto fail2;1407 1408 if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp)))1409 goto fail3;1410 1411 return 0;1412fail3:1413 sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);1414fail2:1415 device_remove_file(fbdev->dev, &dev_attr_caps_text);1416fail1:1417 device_remove_file(fbdev->dev, &dev_attr_caps_num);1418fail0:1419 dev_err(fbdev->dev, "unable to register sysfs interface\n");1420 return r;1421}1422 1423static void omapfb_unregister_sysfs(struct omapfb_device *fbdev)1424{1425 sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp);1426 sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp);1427 device_remove_file(fbdev->dev, &dev_attr_caps_num);1428 device_remove_file(fbdev->dev, &dev_attr_caps_text);1429}1430 1431/*1432 * ---------------------------------------------------------------------------1433 * LDM callbacks1434 * ---------------------------------------------------------------------------1435 */1436/* Initialize system fb_info object and set the default video mode.1437 * The frame buffer memory already allocated by lcddma_init1438 */1439static int fbinfo_init(struct omapfb_device *fbdev, struct fb_info *info)1440{1441 struct fb_var_screeninfo *var = &info->var;1442 struct fb_fix_screeninfo *fix = &info->fix;1443 int r = 0;1444 1445 info->fbops = &omapfb_ops;1446 1447 strscpy(fix->id, MODULE_NAME, sizeof(fix->id));1448 1449 info->pseudo_palette = fbdev->pseudo_palette;1450 1451 var->accel_flags = def_accel ? FB_ACCELF_TEXT : 0;1452 var->xres = def_vxres;1453 var->yres = def_vyres;1454 var->xres_virtual = def_vxres;1455 var->yres_virtual = def_vyres;1456 var->rotate = def_rotate;1457 var->bits_per_pixel = fbdev->panel->bpp;1458 1459 set_fb_var(info, var);1460 set_fb_fix(info, 1);1461 1462 r = fb_alloc_cmap(&info->cmap, 16, 0);1463 if (r != 0)1464 dev_err(fbdev->dev, "unable to allocate color map memory\n");1465 1466 return r;1467}1468 1469/* Release the fb_info object */1470static void fbinfo_cleanup(struct omapfb_device *fbdev, struct fb_info *fbi)1471{1472 fb_dealloc_cmap(&fbi->cmap);1473}1474 1475static void planes_cleanup(struct omapfb_device *fbdev)1476{1477 int i;1478 1479 for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1480 if (fbdev->fb_info[i] == NULL)1481 break;1482 fbinfo_cleanup(fbdev, fbdev->fb_info[i]);1483 framebuffer_release(fbdev->fb_info[i]);1484 }1485}1486 1487static int planes_init(struct omapfb_device *fbdev)1488{1489 struct fb_info *fbi;1490 int i;1491 int r;1492 1493 for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1494 struct omapfb_plane_struct *plane;1495 fbi = framebuffer_alloc(sizeof(struct omapfb_plane_struct),1496 fbdev->dev);1497 if (fbi == NULL) {1498 planes_cleanup(fbdev);1499 return -ENOMEM;1500 }1501 plane = fbi->par;1502 plane->idx = i;1503 plane->fbdev = fbdev;1504 plane->info.mirror = def_mirror;1505 fbdev->fb_info[i] = fbi;1506 1507 if ((r = fbinfo_init(fbdev, fbi)) < 0) {1508 framebuffer_release(fbi);1509 planes_cleanup(fbdev);1510 return r;1511 }1512 plane->info.out_width = fbi->var.xres;1513 plane->info.out_height = fbi->var.yres;1514 }1515 return 0;1516}1517 1518/*1519 * Free driver resources. Can be called to rollback an aborted initialization1520 * sequence.1521 */1522static void omapfb_free_resources(struct omapfb_device *fbdev, int state)1523{1524 int i;1525 1526 switch (state) {1527 case OMAPFB_ACTIVE:1528 for (i = 0; i < fbdev->mem_desc.region_cnt; i++)1529 unregister_framebuffer(fbdev->fb_info[i]);1530 fallthrough;1531 case 7:1532 omapfb_unregister_sysfs(fbdev);1533 fallthrough;1534 case 6:1535 if (fbdev->panel->disable)1536 fbdev->panel->disable(fbdev->panel);1537 fallthrough;1538 case 5:1539 omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);1540 fallthrough;1541 case 4:1542 planes_cleanup(fbdev);1543 fallthrough;1544 case 3:1545 ctrl_cleanup(fbdev);1546 fallthrough;1547 case 2:1548 if (fbdev->panel->cleanup)1549 fbdev->panel->cleanup(fbdev->panel);1550 fallthrough;1551 case 1:1552 dev_set_drvdata(fbdev->dev, NULL);1553 kfree(fbdev);1554 break;1555 case 0:1556 /* nothing to free */1557 break;1558 default:1559 BUG();1560 }1561}1562 1563static int omapfb_find_ctrl(struct omapfb_device *fbdev)1564{1565 struct omapfb_platform_data *conf;1566 char name[17];1567 int i;1568 1569 conf = dev_get_platdata(fbdev->dev);1570 1571 fbdev->ctrl = NULL;1572 1573 strscpy(name, conf->lcd.ctrl_name, sizeof(name));1574 1575 if (strcmp(name, "internal") == 0) {1576 fbdev->ctrl = fbdev->int_ctrl;1577 return 0;1578 }1579 1580 for (i = 0; i < ARRAY_SIZE(ctrls); i++) {1581 dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name);1582 if (strcmp(ctrls[i]->name, name) == 0) {1583 fbdev->ctrl = ctrls[i];1584 break;1585 }1586 }1587 1588 if (fbdev->ctrl == NULL) {1589 dev_dbg(fbdev->dev, "ctrl %s not supported\n", name);1590 return -1;1591 }1592 1593 return 0;1594}1595 1596/*1597 * Called by LDM binding to probe and attach a new device.1598 * Initialization sequence:1599 * 1. allocate system omapfb_device structure1600 * 2. select controller type according to platform configuration1601 * init LCD panel1602 * 3. init LCD controller and LCD DMA1603 * 4. init system fb_info structure for all planes1604 * 5. setup video mode for first plane and enable it1605 * 6. enable LCD panel1606 * 7. register sysfs attributes1607 * OMAPFB_ACTIVE: register system fb_info structure for all planes1608 */1609static int omapfb_do_probe(struct platform_device *pdev,1610 struct lcd_panel *panel)1611{1612 struct omapfb_device *fbdev = NULL;1613 int init_state;1614 unsigned long phz, hhz, vhz;1615 unsigned long vram;1616 int i;1617 int r = 0;1618 1619 init_state = 0;1620 1621 if (pdev->num_resources != 2) {1622 dev_err(&pdev->dev, "probed for an unknown device\n");1623 r = -ENODEV;1624 goto cleanup;1625 }1626 1627 if (dev_get_platdata(&pdev->dev) == NULL) {1628 dev_err(&pdev->dev, "missing platform data\n");1629 r = -ENOENT;1630 goto cleanup;1631 }1632 1633 fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);1634 if (fbdev == NULL) {1635 dev_err(&pdev->dev,1636 "unable to allocate memory for device info\n");1637 r = -ENOMEM;1638 goto cleanup;1639 }1640 1641 r = platform_get_irq(pdev, 0);1642 if (r < 0)1643 goto cleanup;1644 fbdev->int_irq = r;1645 1646 r = platform_get_irq(pdev, 1);1647 if (r < 0)1648 goto cleanup;1649 fbdev->ext_irq = r;1650 1651 init_state++;1652 1653 fbdev->dev = &pdev->dev;1654 fbdev->panel = panel;1655 fbdev->dssdev = &omapdss_device;1656 platform_set_drvdata(pdev, fbdev);1657 1658 mutex_init(&fbdev->rqueue_mutex);1659 1660 fbdev->int_ctrl = &omap1_int_ctrl;1661#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL1662 fbdev->ext_if = &omap1_ext_if;1663#endif1664 if (omapfb_find_ctrl(fbdev) < 0) {1665 dev_err(fbdev->dev,1666 "LCD controller not found, board not supported\n");1667 r = -ENODEV;1668 goto cleanup;1669 }1670 1671 if (fbdev->panel->init) {1672 r = fbdev->panel->init(fbdev->panel, fbdev);1673 if (r)1674 goto cleanup;1675 }1676 1677 pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);1678 1679 def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res;1680 def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res;1681 1682 init_state++;1683 1684 r = ctrl_init(fbdev);1685 if (r)1686 goto cleanup;1687 if (fbdev->ctrl->mmap != NULL)1688 omapfb_ops.fb_mmap = omapfb_mmap;1689 init_state++;1690 1691 r = planes_init(fbdev);1692 if (r)1693 goto cleanup;1694 init_state++;1695 1696#ifdef CONFIG_FB_OMAP_DMA_TUNE1697 /* Set DMA priority for EMIFF access to highest */1698 omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);1699#endif1700 1701 r = ctrl_change_mode(fbdev->fb_info[0]);1702 if (r) {1703 dev_err(fbdev->dev, "mode setting failed\n");1704 goto cleanup;1705 }1706 1707 /* GFX plane is enabled by default */1708 r = fbdev->ctrl->enable_plane(OMAPFB_PLANE_GFX, 1);1709 if (r)1710 goto cleanup;1711 1712 omapfb_set_update_mode(fbdev, manual_update ?1713 OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE);1714 init_state++;1715 1716 if (fbdev->panel->enable) {1717 r = fbdev->panel->enable(fbdev->panel);1718 if (r)1719 goto cleanup;1720 }1721 init_state++;1722 1723 r = omapfb_register_sysfs(fbdev);1724 if (r)1725 goto cleanup;1726 init_state++;1727 1728 vram = 0;1729 for (i = 0; i < fbdev->mem_desc.region_cnt; i++) {1730 r = register_framebuffer(fbdev->fb_info[i]);1731 if (r != 0) {1732 dev_err(fbdev->dev,1733 "registering framebuffer %d failed\n", i);1734 goto cleanup;1735 }1736 vram += fbdev->mem_desc.region[i].size;1737 }1738 1739 fbdev->state = OMAPFB_ACTIVE;1740 1741 panel = fbdev->panel;1742 phz = panel->pixel_clock * 1000;1743 hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw);1744 vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw);1745 1746 omapfb_dev = fbdev;1747 1748 pr_info("omapfb: Framebuffer initialized. Total vram %lu planes %d\n",1749 vram, fbdev->mem_desc.region_cnt);1750 pr_info("omapfb: Pixclock %lu kHz hfreq %lu.%lu kHz "1751 "vfreq %lu.%lu Hz\n",1752 phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10);1753 1754 return 0;1755 1756cleanup:1757 omapfb_free_resources(fbdev, init_state);1758 1759 return r;1760}1761 1762static int omapfb_probe(struct platform_device *pdev)1763{1764 int r;1765 1766 BUG_ON(fbdev_pdev != NULL);1767 1768 r = platform_device_register(&omapdss_device);1769 if (r) {1770 dev_err(&pdev->dev, "can't register omapdss device\n");1771 return r;1772 }1773 1774 /* Delay actual initialization until the LCD is registered */1775 fbdev_pdev = pdev;1776 if (fbdev_panel != NULL)1777 omapfb_do_probe(fbdev_pdev, fbdev_panel);1778 return 0;1779}1780 1781void omapfb_register_panel(struct lcd_panel *panel)1782{1783 BUG_ON(fbdev_panel != NULL);1784 1785 fbdev_panel = panel;1786 if (fbdev_pdev != NULL)1787 omapfb_do_probe(fbdev_pdev, fbdev_panel);1788}1789EXPORT_SYMBOL_GPL(omapfb_register_panel);1790 1791/* Called when the device is being detached from the driver */1792static void omapfb_remove(struct platform_device *pdev)1793{1794 struct omapfb_device *fbdev = platform_get_drvdata(pdev);1795 enum omapfb_state saved_state = fbdev->state;1796 1797 /* FIXME: wait till completion of pending events */1798 1799 fbdev->state = OMAPFB_DISABLED;1800 omapfb_free_resources(fbdev, saved_state);1801 1802 platform_device_unregister(&omapdss_device);1803 fbdev->dssdev = NULL;1804}1805 1806/* PM suspend */1807static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)1808{1809 struct omapfb_device *fbdev = platform_get_drvdata(pdev);1810 1811 if (fbdev != NULL)1812 omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]);1813 return 0;1814}1815 1816/* PM resume */1817static int omapfb_resume(struct platform_device *pdev)1818{1819 struct omapfb_device *fbdev = platform_get_drvdata(pdev);1820 1821 if (fbdev != NULL)1822 omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]);1823 return 0;1824}1825 1826static struct platform_driver omapfb_driver = {1827 .probe = omapfb_probe,1828 .remove = omapfb_remove,1829 .suspend = omapfb_suspend,1830 .resume = omapfb_resume,1831 .driver = {1832 .name = MODULE_NAME,1833 },1834};1835 1836#ifndef MODULE1837 1838/* Process kernel command line parameters */1839static int __init omapfb_setup(char *options)1840{1841 char *this_opt = NULL;1842 int r = 0;1843 1844 pr_debug("omapfb: options %s\n", options);1845 1846 if (!options || !*options)1847 return 0;1848 1849 while (!r && (this_opt = strsep(&options, ",")) != NULL) {1850 if (!strncmp(this_opt, "accel", 5))1851 def_accel = 1;1852 else if (!strncmp(this_opt, "vram:", 5)) {1853 unsigned long long vram;1854 char *suffix;1855 1856 vram = memparse(this_opt + 5, &suffix);1857 switch (suffix[0]) {1858 case '\0':1859 break;1860 default:1861 pr_debug("omapfb: invalid vram suffix %c\n",1862 suffix[0]);1863 r = -1;1864 }1865 def_vram[def_vram_cnt++] = vram;1866 }1867 else if (!strncmp(this_opt, "vxres:", 6))1868 def_vxres = simple_strtoul(this_opt + 6, NULL, 0);1869 else if (!strncmp(this_opt, "vyres:", 6))1870 def_vyres = simple_strtoul(this_opt + 6, NULL, 0);1871 else if (!strncmp(this_opt, "rotate:", 7))1872 def_rotate = (simple_strtoul(this_opt + 7, NULL, 0));1873 else if (!strncmp(this_opt, "mirror:", 7))1874 def_mirror = (simple_strtoul(this_opt + 7, NULL, 0));1875 else if (!strncmp(this_opt, "manual_update", 13))1876 manual_update = 1;1877 else {1878 pr_debug("omapfb: invalid option\n");1879 r = -1;1880 }1881 }1882 1883 return r;1884}1885 1886#endif1887 1888/* Register both the driver and the device */1889static int __init omapfb_init(void)1890{1891#ifndef MODULE1892 char *option;1893 1894 if (fb_get_options("omapfb", &option))1895 return -ENODEV;1896 omapfb_setup(option);1897#endif1898 /* Register the driver with LDM */1899 if (platform_driver_register(&omapfb_driver)) {1900 pr_debug("failed to register omapfb driver\n");1901 return -ENODEV;1902 }1903 1904 return 0;1905}1906 1907static void __exit omapfb_cleanup(void)1908{1909 platform_driver_unregister(&omapfb_driver);1910}1911 1912module_param_named(accel, def_accel, uint, 0664);1913module_param_array_named(vram, def_vram, ulong, &def_vram_cnt, 0664);1914module_param_named(vxres, def_vxres, long, 0664);1915module_param_named(vyres, def_vyres, long, 0664);1916module_param_named(rotate, def_rotate, uint, 0664);1917module_param_named(mirror, def_mirror, uint, 0664);1918module_param_named(manual_update, manual_update, bool, 0664);1919 1920module_init(omapfb_init);1921module_exit(omapfb_cleanup);1922 1923MODULE_DESCRIPTION("TI OMAP framebuffer driver");1924MODULE_AUTHOR("Imre Deak <imre.deak@nokia.com>");1925MODULE_LICENSE("GPL");1926