2635 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/video/omap2/omapfb-main.c4 *5 * Copyright (C) 2008 Nokia Corporation6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>7 *8 * Some code and ideas taken from drivers/video/omap/ driver9 * by Imre Deak.10 */11 12#include <linux/module.h>13#include <linux/delay.h>14#include <linux/slab.h>15#include <linux/fb.h>16#include <linux/dma-mapping.h>17#include <linux/vmalloc.h>18#include <linux/device.h>19#include <linux/platform_device.h>20#include <linux/omapfb.h>21 22#include <video/omapfb_dss.h>23#include <video/omapvrfb.h>24 25#include "omapfb.h"26 27#define MODULE_NAME "omapfb"28 29#define OMAPFB_PLANE_XRES_MIN 830#define OMAPFB_PLANE_YRES_MIN 831 32static char *def_mode;33static char *def_vram;34static bool def_vrfb;35static int def_rotate;36static bool def_mirror;37static bool auto_update;38static unsigned int auto_update_freq;39module_param(auto_update, bool, 0);40module_param(auto_update_freq, uint, 0644);41 42#ifdef DEBUG43bool omapfb_debug;44module_param_named(debug, omapfb_debug, bool, 0644);45static bool omapfb_test_pattern;46module_param_named(test, omapfb_test_pattern, bool, 0644);47#endif48 49static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi);50static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,51 struct omap_dss_device *dssdev);52 53#ifdef DEBUG54static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color)55{56 struct fb_var_screeninfo *var = &fbi->var;57 struct fb_fix_screeninfo *fix = &fbi->fix;58 void __iomem *addr = fbi->screen_base;59 const unsigned bytespp = var->bits_per_pixel >> 3;60 const unsigned line_len = fix->line_length / bytespp;61 62 int r = (color >> 16) & 0xff;63 int g = (color >> 8) & 0xff;64 int b = (color >> 0) & 0xff;65 66 if (var->bits_per_pixel == 16) {67 u16 __iomem *p = (u16 __iomem *)addr;68 p += y * line_len + x;69 70 r = r * 32 / 256;71 g = g * 64 / 256;72 b = b * 32 / 256;73 74 __raw_writew((r << 11) | (g << 5) | (b << 0), p);75 } else if (var->bits_per_pixel == 24) {76 u8 __iomem *p = (u8 __iomem *)addr;77 p += (y * line_len + x) * 3;78 79 __raw_writeb(b, p + 0);80 __raw_writeb(g, p + 1);81 __raw_writeb(r, p + 2);82 } else if (var->bits_per_pixel == 32) {83 u32 __iomem *p = (u32 __iomem *)addr;84 p += y * line_len + x;85 __raw_writel(color, p);86 }87}88 89static void fill_fb(struct fb_info *fbi)90{91 struct fb_var_screeninfo *var = &fbi->var;92 const short w = var->xres_virtual;93 const short h = var->yres_virtual;94 void __iomem *addr = fbi->screen_base;95 int y, x;96 97 if (!addr)98 return;99 100 DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length);101 102 for (y = 0; y < h; y++) {103 for (x = 0; x < w; x++) {104 if (x < 20 && y < 20)105 draw_pixel(fbi, x, y, 0xffffff);106 else if (x < 20 && (y > 20 && y < h - 20))107 draw_pixel(fbi, x, y, 0xff);108 else if (y < 20 && (x > 20 && x < w - 20))109 draw_pixel(fbi, x, y, 0xff00);110 else if (x > w - 20 && (y > 20 && y < h - 20))111 draw_pixel(fbi, x, y, 0xff0000);112 else if (y > h - 20 && (x > 20 && x < w - 20))113 draw_pixel(fbi, x, y, 0xffff00);114 else if (x == 20 || x == w - 20 ||115 y == 20 || y == h - 20)116 draw_pixel(fbi, x, y, 0xffffff);117 else if (x == y || w - x == h - y)118 draw_pixel(fbi, x, y, 0xff00ff);119 else if (w - x == y || x == h - y)120 draw_pixel(fbi, x, y, 0x00ffff);121 else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) {122 int t = x * 3 / w;123 unsigned r = 0, g = 0, b = 0;124 unsigned c;125 if (var->bits_per_pixel == 16) {126 if (t == 0)127 b = (y % 32) * 256 / 32;128 else if (t == 1)129 g = (y % 64) * 256 / 64;130 else if (t == 2)131 r = (y % 32) * 256 / 32;132 } else {133 if (t == 0)134 b = (y % 256);135 else if (t == 1)136 g = (y % 256);137 else if (t == 2)138 r = (y % 256);139 }140 c = (r << 16) | (g << 8) | (b << 0);141 draw_pixel(fbi, x, y, c);142 } else {143 draw_pixel(fbi, x, y, 0);144 }145 }146 }147}148#endif149 150static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot)151{152 const struct vrfb *vrfb = &ofbi->region->vrfb;153 unsigned offset;154 155 switch (rot) {156 case FB_ROTATE_UR:157 offset = 0;158 break;159 case FB_ROTATE_CW:160 offset = vrfb->yoffset;161 break;162 case FB_ROTATE_UD:163 offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset;164 break;165 case FB_ROTATE_CCW:166 offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN;167 break;168 default:169 BUG();170 return 0;171 }172 173 offset *= vrfb->bytespp;174 175 return offset;176}177 178static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot)179{180 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {181 return ofbi->region->vrfb.paddr[rot]182 + omapfb_get_vrfb_offset(ofbi, rot);183 } else {184 return ofbi->region->paddr;185 }186}187 188static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi)189{190 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)191 return ofbi->region->vrfb.paddr[0];192 else193 return ofbi->region->paddr;194}195 196static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi)197{198 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)199 return ofbi->region->vrfb.vaddr[0];200 else201 return ofbi->region->vaddr;202}203 204static struct omapfb_colormode omapfb_colormodes[] = {205 {206 .dssmode = OMAP_DSS_COLOR_UYVY,207 .bits_per_pixel = 16,208 .nonstd = OMAPFB_COLOR_YUV422,209 }, {210 .dssmode = OMAP_DSS_COLOR_YUV2,211 .bits_per_pixel = 16,212 .nonstd = OMAPFB_COLOR_YUY422,213 }, {214 .dssmode = OMAP_DSS_COLOR_ARGB16,215 .bits_per_pixel = 16,216 .red = { .length = 4, .offset = 8, .msb_right = 0 },217 .green = { .length = 4, .offset = 4, .msb_right = 0 },218 .blue = { .length = 4, .offset = 0, .msb_right = 0 },219 .transp = { .length = 4, .offset = 12, .msb_right = 0 },220 }, {221 .dssmode = OMAP_DSS_COLOR_RGB16,222 .bits_per_pixel = 16,223 .red = { .length = 5, .offset = 11, .msb_right = 0 },224 .green = { .length = 6, .offset = 5, .msb_right = 0 },225 .blue = { .length = 5, .offset = 0, .msb_right = 0 },226 .transp = { .length = 0, .offset = 0, .msb_right = 0 },227 }, {228 .dssmode = OMAP_DSS_COLOR_RGB24P,229 .bits_per_pixel = 24,230 .red = { .length = 8, .offset = 16, .msb_right = 0 },231 .green = { .length = 8, .offset = 8, .msb_right = 0 },232 .blue = { .length = 8, .offset = 0, .msb_right = 0 },233 .transp = { .length = 0, .offset = 0, .msb_right = 0 },234 }, {235 .dssmode = OMAP_DSS_COLOR_RGB24U,236 .bits_per_pixel = 32,237 .red = { .length = 8, .offset = 16, .msb_right = 0 },238 .green = { .length = 8, .offset = 8, .msb_right = 0 },239 .blue = { .length = 8, .offset = 0, .msb_right = 0 },240 .transp = { .length = 0, .offset = 0, .msb_right = 0 },241 }, {242 .dssmode = OMAP_DSS_COLOR_ARGB32,243 .bits_per_pixel = 32,244 .red = { .length = 8, .offset = 16, .msb_right = 0 },245 .green = { .length = 8, .offset = 8, .msb_right = 0 },246 .blue = { .length = 8, .offset = 0, .msb_right = 0 },247 .transp = { .length = 8, .offset = 24, .msb_right = 0 },248 }, {249 .dssmode = OMAP_DSS_COLOR_RGBA32,250 .bits_per_pixel = 32,251 .red = { .length = 8, .offset = 24, .msb_right = 0 },252 .green = { .length = 8, .offset = 16, .msb_right = 0 },253 .blue = { .length = 8, .offset = 8, .msb_right = 0 },254 .transp = { .length = 8, .offset = 0, .msb_right = 0 },255 }, {256 .dssmode = OMAP_DSS_COLOR_RGBX32,257 .bits_per_pixel = 32,258 .red = { .length = 8, .offset = 24, .msb_right = 0 },259 .green = { .length = 8, .offset = 16, .msb_right = 0 },260 .blue = { .length = 8, .offset = 8, .msb_right = 0 },261 .transp = { .length = 0, .offset = 0, .msb_right = 0 },262 },263};264 265static bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2)266{267 return f1->length == f2->length &&268 f1->offset == f2->offset &&269 f1->msb_right == f2->msb_right;270}271 272static bool cmp_var_to_colormode(struct fb_var_screeninfo *var,273 struct omapfb_colormode *color)274{275 if (var->bits_per_pixel == 0 ||276 var->red.length == 0 ||277 var->blue.length == 0 ||278 var->green.length == 0)279 return false;280 281 return var->bits_per_pixel == color->bits_per_pixel &&282 cmp_component(&var->red, &color->red) &&283 cmp_component(&var->green, &color->green) &&284 cmp_component(&var->blue, &color->blue) &&285 cmp_component(&var->transp, &color->transp);286}287 288static void assign_colormode_to_var(struct fb_var_screeninfo *var,289 struct omapfb_colormode *color)290{291 var->bits_per_pixel = color->bits_per_pixel;292 var->nonstd = color->nonstd;293 var->red = color->red;294 var->green = color->green;295 var->blue = color->blue;296 var->transp = color->transp;297}298 299static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var,300 enum omap_color_mode *mode)301{302 enum omap_color_mode dssmode;303 int i;304 305 /* first match with nonstd field */306 if (var->nonstd) {307 for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {308 struct omapfb_colormode *m = &omapfb_colormodes[i];309 if (var->nonstd == m->nonstd) {310 assign_colormode_to_var(var, m);311 *mode = m->dssmode;312 return 0;313 }314 }315 316 return -EINVAL;317 }318 319 /* then try exact match of bpp and colors */320 for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {321 struct omapfb_colormode *m = &omapfb_colormodes[i];322 if (cmp_var_to_colormode(var, m)) {323 assign_colormode_to_var(var, m);324 *mode = m->dssmode;325 return 0;326 }327 }328 329 /* match with bpp if user has not filled color fields330 * properly */331 switch (var->bits_per_pixel) {332 case 1:333 dssmode = OMAP_DSS_COLOR_CLUT1;334 break;335 case 2:336 dssmode = OMAP_DSS_COLOR_CLUT2;337 break;338 case 4:339 dssmode = OMAP_DSS_COLOR_CLUT4;340 break;341 case 8:342 dssmode = OMAP_DSS_COLOR_CLUT8;343 break;344 case 12:345 dssmode = OMAP_DSS_COLOR_RGB12U;346 break;347 case 16:348 dssmode = OMAP_DSS_COLOR_RGB16;349 break;350 case 24:351 dssmode = OMAP_DSS_COLOR_RGB24P;352 break;353 case 32:354 dssmode = OMAP_DSS_COLOR_RGB24U;355 break;356 default:357 return -EINVAL;358 }359 360 for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {361 struct omapfb_colormode *m = &omapfb_colormodes[i];362 if (dssmode == m->dssmode) {363 assign_colormode_to_var(var, m);364 *mode = m->dssmode;365 return 0;366 }367 }368 369 return -EINVAL;370}371 372static int check_fb_res_bounds(struct fb_var_screeninfo *var)373{374 int xres_min = OMAPFB_PLANE_XRES_MIN;375 int xres_max = 2048;376 int yres_min = OMAPFB_PLANE_YRES_MIN;377 int yres_max = 2048;378 379 /* XXX: some applications seem to set virtual res to 0. */380 if (var->xres_virtual == 0)381 var->xres_virtual = var->xres;382 383 if (var->yres_virtual == 0)384 var->yres_virtual = var->yres;385 386 if (var->xres_virtual < xres_min || var->yres_virtual < yres_min)387 return -EINVAL;388 389 if (var->xres < xres_min)390 var->xres = xres_min;391 if (var->yres < yres_min)392 var->yres = yres_min;393 if (var->xres > xres_max)394 var->xres = xres_max;395 if (var->yres > yres_max)396 var->yres = yres_max;397 398 if (var->xres > var->xres_virtual)399 var->xres = var->xres_virtual;400 if (var->yres > var->yres_virtual)401 var->yres = var->yres_virtual;402 403 return 0;404}405 406static void shrink_height(unsigned long max_frame_size,407 struct fb_var_screeninfo *var)408{409 DBG("can't fit FB into memory, reducing y\n");410 var->yres_virtual = max_frame_size /411 (var->xres_virtual * var->bits_per_pixel >> 3);412 413 if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN)414 var->yres_virtual = OMAPFB_PLANE_YRES_MIN;415 416 if (var->yres > var->yres_virtual)417 var->yres = var->yres_virtual;418}419 420static void shrink_width(unsigned long max_frame_size,421 struct fb_var_screeninfo *var)422{423 DBG("can't fit FB into memory, reducing x\n");424 var->xres_virtual = max_frame_size / var->yres_virtual /425 (var->bits_per_pixel >> 3);426 427 if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN)428 var->xres_virtual = OMAPFB_PLANE_XRES_MIN;429 430 if (var->xres > var->xres_virtual)431 var->xres = var->xres_virtual;432}433 434static int check_vrfb_fb_size(unsigned long region_size,435 const struct fb_var_screeninfo *var)436{437 unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual,438 var->yres_virtual, var->bits_per_pixel >> 3);439 440 return min_phys_size > region_size ? -EINVAL : 0;441}442 443static int check_fb_size(const struct omapfb_info *ofbi,444 struct fb_var_screeninfo *var)445{446 unsigned long max_frame_size = ofbi->region->size;447 int bytespp = var->bits_per_pixel >> 3;448 unsigned long line_size = var->xres_virtual * bytespp;449 450 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {451 /* One needs to check for both VRFB and OMAPFB limitations. */452 if (check_vrfb_fb_size(max_frame_size, var))453 shrink_height(omap_vrfb_max_height(454 max_frame_size, var->xres_virtual, bytespp) *455 line_size, var);456 457 if (check_vrfb_fb_size(max_frame_size, var)) {458 DBG("cannot fit FB to memory\n");459 return -EINVAL;460 }461 462 return 0;463 }464 465 DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size);466 467 if (line_size * var->yres_virtual > max_frame_size)468 shrink_height(max_frame_size, var);469 470 if (line_size * var->yres_virtual > max_frame_size) {471 shrink_width(max_frame_size, var);472 line_size = var->xres_virtual * bytespp;473 }474 475 if (line_size * var->yres_virtual > max_frame_size) {476 DBG("cannot fit FB to memory\n");477 return -EINVAL;478 }479 480 return 0;481}482 483/*484 * Consider if VRFB assisted rotation is in use and if the virtual space for485 * the zero degree view needs to be mapped. The need for mapping also acts as486 * the trigger for setting up the hardware on the context in question. This487 * ensures that one does not attempt to access the virtual view before the488 * hardware is serving the address translations.489 */490static int setup_vrfb_rotation(struct fb_info *fbi)491{492 struct omapfb_info *ofbi = FB2OFB(fbi);493 struct omapfb2_mem_region *rg = ofbi->region;494 struct vrfb *vrfb = &rg->vrfb;495 struct fb_var_screeninfo *var = &fbi->var;496 struct fb_fix_screeninfo *fix = &fbi->fix;497 unsigned bytespp;498 bool yuv_mode;499 enum omap_color_mode mode;500 int r;501 bool reconf;502 503 if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB)504 return 0;505 506 DBG("setup_vrfb_rotation\n");507 508 r = fb_mode_to_dss_mode(var, &mode);509 if (r)510 return r;511 512 bytespp = var->bits_per_pixel >> 3;513 514 yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY;515 516 /* We need to reconfigure VRFB if the resolution changes, if yuv mode517 * is enabled/disabled, or if bytes per pixel changes */518 519 /* XXX we shouldn't allow this when framebuffer is mmapped */520 521 reconf = false;522 523 if (yuv_mode != vrfb->yuv_mode)524 reconf = true;525 else if (bytespp != vrfb->bytespp)526 reconf = true;527 else if (vrfb->xres != var->xres_virtual ||528 vrfb->yres != var->yres_virtual)529 reconf = true;530 531 if (vrfb->vaddr[0] && reconf) {532 fbi->screen_base = NULL;533 fix->smem_start = 0;534 fix->smem_len = 0;535 iounmap(vrfb->vaddr[0]);536 vrfb->vaddr[0] = NULL;537 DBG("setup_vrfb_rotation: reset fb\n");538 }539 540 if (vrfb->vaddr[0])541 return 0;542 543 omap_vrfb_setup(&rg->vrfb, rg->paddr,544 var->xres_virtual,545 var->yres_virtual,546 bytespp, yuv_mode);547 548 /* Now one can ioremap the 0 angle view */549 r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0);550 if (r)551 return r;552 553 /* used by open/write in fbmem.c */554 fbi->screen_base = ofbi->region->vrfb.vaddr[0];555 556 fix->smem_start = ofbi->region->vrfb.paddr[0];557 558 switch (var->nonstd) {559 case OMAPFB_COLOR_YUV422:560 case OMAPFB_COLOR_YUY422:561 fix->line_length =562 (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;563 break;564 default:565 fix->line_length =566 (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;567 break;568 }569 570 fix->smem_len = var->yres_virtual * fix->line_length;571 572 return 0;573}574 575int dss_mode_to_fb_mode(enum omap_color_mode dssmode,576 struct fb_var_screeninfo *var)577{578 int i;579 580 for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) {581 struct omapfb_colormode *mode = &omapfb_colormodes[i];582 if (dssmode == mode->dssmode) {583 assign_colormode_to_var(var, mode);584 return 0;585 }586 }587 return -ENOENT;588}589 590void set_fb_fix(struct fb_info *fbi)591{592 struct fb_fix_screeninfo *fix = &fbi->fix;593 struct fb_var_screeninfo *var = &fbi->var;594 struct omapfb_info *ofbi = FB2OFB(fbi);595 struct omapfb2_mem_region *rg = ofbi->region;596 597 DBG("set_fb_fix\n");598 599 /* used by open/write in fbmem.c */600 fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi);601 602 /* used by mmap in fbmem.c */603 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {604 switch (var->nonstd) {605 case OMAPFB_COLOR_YUV422:606 case OMAPFB_COLOR_YUY422:607 fix->line_length =608 (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2;609 break;610 default:611 fix->line_length =612 (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3;613 break;614 }615 616 fix->smem_len = var->yres_virtual * fix->line_length;617 } else {618 fix->line_length =619 (var->xres_virtual * var->bits_per_pixel) >> 3;620 fix->smem_len = rg->size;621 }622 623 fix->smem_start = omapfb_get_region_paddr(ofbi);624 625 fix->type = FB_TYPE_PACKED_PIXELS;626 627 if (var->nonstd)628 fix->visual = FB_VISUAL_PSEUDOCOLOR;629 else {630 switch (var->bits_per_pixel) {631 case 32:632 case 24:633 case 16:634 case 12:635 fix->visual = FB_VISUAL_TRUECOLOR;636 /* 12bpp is stored in 16 bits */637 break;638 case 1:639 case 2:640 case 4:641 case 8:642 fix->visual = FB_VISUAL_PSEUDOCOLOR;643 break;644 }645 }646 647 fix->accel = FB_ACCEL_NONE;648 649 fix->xpanstep = 1;650 fix->ypanstep = 1;651}652 653/* check new var and possibly modify it to be ok */654int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)655{656 struct omapfb_info *ofbi = FB2OFB(fbi);657 struct omap_dss_device *display = fb2display(fbi);658 enum omap_color_mode mode = 0;659 int i;660 int r;661 662 DBG("check_fb_var %d\n", ofbi->id);663 664 WARN_ON(!atomic_read(&ofbi->region->lock_count));665 666 r = fb_mode_to_dss_mode(var, &mode);667 if (r) {668 DBG("cannot convert var to omap dss mode\n");669 return r;670 }671 672 for (i = 0; i < ofbi->num_overlays; ++i) {673 if ((ofbi->overlays[i]->supported_modes & mode) == 0) {674 DBG("invalid mode\n");675 return -EINVAL;676 }677 }678 679 if (var->rotate > 3)680 return -EINVAL;681 682 if (check_fb_res_bounds(var))683 return -EINVAL;684 685 /* When no memory is allocated ignore the size check */686 if (ofbi->region->size != 0 && check_fb_size(ofbi, var))687 return -EINVAL;688 689 if (var->xres + var->xoffset > var->xres_virtual)690 var->xoffset = var->xres_virtual - var->xres;691 if (var->yres + var->yoffset > var->yres_virtual)692 var->yoffset = var->yres_virtual - var->yres;693 694 DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n",695 var->xres, var->yres,696 var->xres_virtual, var->yres_virtual);697 698 if (display && display->driver->get_dimensions) {699 u32 w, h;700 display->driver->get_dimensions(display, &w, &h);701 var->width = DIV_ROUND_CLOSEST(w, 1000);702 var->height = DIV_ROUND_CLOSEST(h, 1000);703 } else {704 var->height = -1;705 var->width = -1;706 }707 708 var->grayscale = 0;709 710 if (display && display->driver->get_timings) {711 struct omap_video_timings timings;712 display->driver->get_timings(display, &timings);713 714 /* pixclock in ps, the rest in pixclock */715 var->pixclock = timings.pixelclock != 0 ?716 KHZ2PICOS(timings.pixelclock / 1000) :717 0;718 var->left_margin = timings.hbp;719 var->right_margin = timings.hfp;720 var->upper_margin = timings.vbp;721 var->lower_margin = timings.vfp;722 var->hsync_len = timings.hsw;723 var->vsync_len = timings.vsw;724 var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?725 FB_SYNC_HOR_HIGH_ACT : 0;726 var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ?727 FB_SYNC_VERT_HIGH_ACT : 0;728 var->vmode = timings.interlace ?729 FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;730 } else {731 var->pixclock = 0;732 var->left_margin = 0;733 var->right_margin = 0;734 var->upper_margin = 0;735 var->lower_margin = 0;736 var->hsync_len = 0;737 var->vsync_len = 0;738 var->sync = 0;739 var->vmode = FB_VMODE_NONINTERLACED;740 }741 742 return 0;743}744 745/*746 * ---------------------------------------------------------------------------747 * fbdev framework callbacks748 * ---------------------------------------------------------------------------749 */750static int omapfb_open(struct fb_info *fbi, int user)751{752 return 0;753}754 755static int omapfb_release(struct fb_info *fbi, int user)756{757 return 0;758}759 760static unsigned calc_rotation_offset_dma(const struct fb_var_screeninfo *var,761 const struct fb_fix_screeninfo *fix, int rotation)762{763 unsigned offset;764 765 offset = var->yoffset * fix->line_length +766 var->xoffset * (var->bits_per_pixel >> 3);767 768 return offset;769}770 771static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var,772 const struct fb_fix_screeninfo *fix, int rotation)773{774 unsigned offset;775 776 if (rotation == FB_ROTATE_UD)777 offset = (var->yres_virtual - var->yres) *778 fix->line_length;779 else if (rotation == FB_ROTATE_CW)780 offset = (var->yres_virtual - var->yres) *781 (var->bits_per_pixel >> 3);782 else783 offset = 0;784 785 if (rotation == FB_ROTATE_UR)786 offset += var->yoffset * fix->line_length +787 var->xoffset * (var->bits_per_pixel >> 3);788 else if (rotation == FB_ROTATE_UD)789 offset -= var->yoffset * fix->line_length +790 var->xoffset * (var->bits_per_pixel >> 3);791 else if (rotation == FB_ROTATE_CW)792 offset -= var->xoffset * fix->line_length +793 var->yoffset * (var->bits_per_pixel >> 3);794 else if (rotation == FB_ROTATE_CCW)795 offset += var->xoffset * fix->line_length +796 var->yoffset * (var->bits_per_pixel >> 3);797 798 return offset;799}800 801static void omapfb_calc_addr(const struct omapfb_info *ofbi,802 const struct fb_var_screeninfo *var,803 const struct fb_fix_screeninfo *fix,804 int rotation, u32 *paddr)805{806 u32 data_start_p;807 int offset;808 809 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)810 data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation);811 else812 data_start_p = omapfb_get_region_paddr(ofbi);813 814 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)815 offset = calc_rotation_offset_vrfb(var, fix, rotation);816 else817 offset = calc_rotation_offset_dma(var, fix, rotation);818 819 data_start_p += offset;820 821 if (offset)822 DBG("offset %d, %d = %d\n",823 var->xoffset, var->yoffset, offset);824 825 DBG("paddr %x\n", data_start_p);826 827 *paddr = data_start_p;828}829 830/* setup overlay according to the fb */831int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl,832 u16 posx, u16 posy, u16 outw, u16 outh)833{834 int r = 0;835 struct omapfb_info *ofbi = FB2OFB(fbi);836 struct fb_var_screeninfo *var = &fbi->var;837 struct fb_fix_screeninfo *fix = &fbi->fix;838 enum omap_color_mode mode = 0;839 u32 data_start_p = 0;840 struct omap_overlay_info info;841 int xres, yres;842 int screen_width;843 int mirror;844 int rotation = var->rotate;845 int i;846 847 WARN_ON(!atomic_read(&ofbi->region->lock_count));848 849 for (i = 0; i < ofbi->num_overlays; i++) {850 if (ovl != ofbi->overlays[i])851 continue;852 853 rotation = (rotation + ofbi->rotation[i]) % 4;854 break;855 }856 857 DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id,858 posx, posy, outw, outh);859 860 if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) {861 xres = var->yres;862 yres = var->xres;863 } else {864 xres = var->xres;865 yres = var->yres;866 }867 868 if (ofbi->region->size)869 omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p);870 871 r = fb_mode_to_dss_mode(var, &mode);872 if (r) {873 DBG("fb_mode_to_dss_mode failed");874 goto err;875 }876 877 switch (var->nonstd) {878 case OMAPFB_COLOR_YUV422:879 case OMAPFB_COLOR_YUY422:880 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {881 screen_width = fix->line_length882 / (var->bits_per_pixel >> 2);883 break;884 }885 fallthrough;886 default:887 screen_width = fix->line_length / (var->bits_per_pixel >> 3);888 break;889 }890 891 ovl->get_overlay_info(ovl, &info);892 893 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)894 mirror = 0;895 else896 mirror = ofbi->mirror;897 898 info.paddr = data_start_p;899 info.screen_width = screen_width;900 info.width = xres;901 info.height = yres;902 info.color_mode = mode;903 info.rotation_type = ofbi->rotation_type;904 info.rotation = rotation;905 info.mirror = mirror;906 907 info.pos_x = posx;908 info.pos_y = posy;909 info.out_width = outw;910 info.out_height = outh;911 912 r = ovl->set_overlay_info(ovl, &info);913 if (r) {914 DBG("ovl->setup_overlay_info failed\n");915 goto err;916 }917 918 return 0;919 920err:921 DBG("setup_overlay failed\n");922 return r;923}924 925/* apply var to the overlay */926int omapfb_apply_changes(struct fb_info *fbi, int init)927{928 int r = 0;929 struct omapfb_info *ofbi = FB2OFB(fbi);930 struct fb_var_screeninfo *var = &fbi->var;931 struct omap_overlay *ovl;932 u16 posx, posy;933 u16 outw, outh;934 int i;935 936#ifdef DEBUG937 if (omapfb_test_pattern)938 fill_fb(fbi);939#endif940 941 WARN_ON(!atomic_read(&ofbi->region->lock_count));942 943 for (i = 0; i < ofbi->num_overlays; i++) {944 ovl = ofbi->overlays[i];945 946 DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id);947 948 if (ofbi->region->size == 0) {949 /* the fb is not available. disable the overlay */950 omapfb_overlay_enable(ovl, 0);951 if (!init && ovl->manager)952 ovl->manager->apply(ovl->manager);953 continue;954 }955 956 if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) {957 int rotation = (var->rotate + ofbi->rotation[i]) % 4;958 if (rotation == FB_ROTATE_CW ||959 rotation == FB_ROTATE_CCW) {960 outw = var->yres;961 outh = var->xres;962 } else {963 outw = var->xres;964 outh = var->yres;965 }966 } else {967 struct omap_overlay_info info;968 ovl->get_overlay_info(ovl, &info);969 outw = info.out_width;970 outh = info.out_height;971 }972 973 if (init) {974 posx = 0;975 posy = 0;976 } else {977 struct omap_overlay_info info;978 ovl->get_overlay_info(ovl, &info);979 posx = info.pos_x;980 posy = info.pos_y;981 }982 983 r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh);984 if (r)985 goto err;986 987 if (!init && ovl->manager)988 ovl->manager->apply(ovl->manager);989 }990 return 0;991err:992 DBG("apply_changes failed\n");993 return r;994}995 996/* checks var and eventually tweaks it to something supported,997 * DO NOT MODIFY PAR */998static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)999{1000 struct omapfb_info *ofbi = FB2OFB(fbi);1001 int r;1002 1003 DBG("check_var(%d)\n", FB2OFB(fbi)->id);1004 1005 omapfb_get_mem_region(ofbi->region);1006 1007 r = check_fb_var(fbi, var);1008 1009 omapfb_put_mem_region(ofbi->region);1010 1011 return r;1012}1013 1014/* set the video mode according to info->var */1015static int omapfb_set_par(struct fb_info *fbi)1016{1017 struct omapfb_info *ofbi = FB2OFB(fbi);1018 int r;1019 1020 DBG("set_par(%d)\n", FB2OFB(fbi)->id);1021 1022 omapfb_get_mem_region(ofbi->region);1023 1024 set_fb_fix(fbi);1025 1026 r = setup_vrfb_rotation(fbi);1027 if (r)1028 goto out;1029 1030 r = omapfb_apply_changes(fbi, 0);1031 1032 out:1033 omapfb_put_mem_region(ofbi->region);1034 1035 return r;1036}1037 1038static int omapfb_pan_display(struct fb_var_screeninfo *var,1039 struct fb_info *fbi)1040{1041 struct omapfb_info *ofbi = FB2OFB(fbi);1042 struct fb_var_screeninfo new_var;1043 int r;1044 1045 DBG("pan_display(%d)\n", FB2OFB(fbi)->id);1046 1047 if (var->xoffset == fbi->var.xoffset &&1048 var->yoffset == fbi->var.yoffset)1049 return 0;1050 1051 new_var = fbi->var;1052 new_var.xoffset = var->xoffset;1053 new_var.yoffset = var->yoffset;1054 1055 fbi->var = new_var;1056 1057 omapfb_get_mem_region(ofbi->region);1058 1059 r = omapfb_apply_changes(fbi, 0);1060 1061 omapfb_put_mem_region(ofbi->region);1062 1063 return r;1064}1065 1066static void mmap_user_open(struct vm_area_struct *vma)1067{1068 struct omapfb2_mem_region *rg = vma->vm_private_data;1069 1070 omapfb_get_mem_region(rg);1071 atomic_inc(&rg->map_count);1072 omapfb_put_mem_region(rg);1073}1074 1075static void mmap_user_close(struct vm_area_struct *vma)1076{1077 struct omapfb2_mem_region *rg = vma->vm_private_data;1078 1079 omapfb_get_mem_region(rg);1080 atomic_dec(&rg->map_count);1081 omapfb_put_mem_region(rg);1082}1083 1084static const struct vm_operations_struct mmap_user_ops = {1085 .open = mmap_user_open,1086 .close = mmap_user_close,1087};1088 1089static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)1090{1091 struct omapfb_info *ofbi = FB2OFB(fbi);1092 struct fb_fix_screeninfo *fix = &fbi->fix;1093 struct omapfb2_mem_region *rg;1094 unsigned long start;1095 u32 len;1096 int r;1097 1098 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);1099 1100 rg = omapfb_get_mem_region(ofbi->region);1101 1102 start = omapfb_get_region_paddr(ofbi);1103 len = fix->smem_len;1104 1105 DBG("user mmap region start %lx, len %d, off %lx\n", start, len,1106 vma->vm_pgoff << PAGE_SHIFT);1107 1108 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);1109 vma->vm_ops = &mmap_user_ops;1110 vma->vm_private_data = rg;1111 1112 r = vm_iomap_memory(vma, start, len);1113 if (r)1114 goto error;1115 1116 /* vm_ops.open won't be called for mmap itself. */1117 atomic_inc(&rg->map_count);1118 1119 omapfb_put_mem_region(rg);1120 1121 return 0;1122 1123error:1124 omapfb_put_mem_region(ofbi->region);1125 1126 return r;1127}1128 1129/* Store a single color palette entry into a pseudo palette or the hardware1130 * palette if one is available. For now we support only 16bpp and thus store1131 * the entry only to the pseudo palette.1132 */1133static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green,1134 u_int blue, u_int transp, int update_hw_pal)1135{1136 /*struct omapfb_info *ofbi = FB2OFB(fbi);*/1137 /*struct omapfb2_device *fbdev = ofbi->fbdev;*/1138 struct fb_var_screeninfo *var = &fbi->var;1139 int r = 0;1140 1141 enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */1142 1143 /*switch (plane->color_mode) {*/1144 switch (mode) {1145 case OMAPFB_COLOR_YUV422:1146 case OMAPFB_COLOR_YUV420:1147 case OMAPFB_COLOR_YUY422:1148 r = -EINVAL;1149 break;1150 case OMAPFB_COLOR_CLUT_8BPP:1151 case OMAPFB_COLOR_CLUT_4BPP:1152 case OMAPFB_COLOR_CLUT_2BPP:1153 case OMAPFB_COLOR_CLUT_1BPP:1154 /*1155 if (fbdev->ctrl->setcolreg)1156 r = fbdev->ctrl->setcolreg(regno, red, green, blue,1157 transp, update_hw_pal);1158 */1159 r = -EINVAL;1160 break;1161 case OMAPFB_COLOR_RGB565:1162 case OMAPFB_COLOR_RGB444:1163 case OMAPFB_COLOR_RGB24P:1164 case OMAPFB_COLOR_RGB24U:1165 if (regno < 16) {1166 u32 pal;1167 pal = ((red >> (16 - var->red.length)) <<1168 var->red.offset) |1169 ((green >> (16 - var->green.length)) <<1170 var->green.offset) |1171 (blue >> (16 - var->blue.length));1172 ((u32 *)(fbi->pseudo_palette))[regno] = pal;1173 }1174 break;1175 default:1176 BUG();1177 }1178 return r;1179}1180 1181static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,1182 u_int transp, struct fb_info *info)1183{1184 DBG("setcolreg\n");1185 1186 return _setcolreg(info, regno, red, green, blue, transp, 1);1187}1188 1189static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info)1190{1191 int count, index, r;1192 u16 *red, *green, *blue, *transp;1193 u16 trans = 0xffff;1194 1195 DBG("setcmap\n");1196 1197 red = cmap->red;1198 green = cmap->green;1199 blue = cmap->blue;1200 transp = cmap->transp;1201 index = cmap->start;1202 1203 for (count = 0; count < cmap->len; count++) {1204 if (transp)1205 trans = *transp++;1206 r = _setcolreg(info, index++, *red++, *green++, *blue++, trans,1207 count == cmap->len - 1);1208 if (r != 0)1209 return r;1210 }1211 1212 return 0;1213}1214 1215static int omapfb_blank(int blank, struct fb_info *fbi)1216{1217 struct omapfb_info *ofbi = FB2OFB(fbi);1218 struct omapfb2_device *fbdev = ofbi->fbdev;1219 struct omap_dss_device *display = fb2display(fbi);1220 struct omapfb_display_data *d;1221 int r = 0;1222 1223 if (!display)1224 return -EINVAL;1225 1226 omapfb_lock(fbdev);1227 1228 d = get_display_data(fbdev, display);1229 1230 switch (blank) {1231 case FB_BLANK_UNBLANK:1232 if (display->state == OMAP_DSS_DISPLAY_ACTIVE)1233 goto exit;1234 1235 r = display->driver->enable(display);1236 1237 if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) &&1238 d->update_mode == OMAPFB_AUTO_UPDATE &&1239 !d->auto_update_work_enabled)1240 omapfb_start_auto_update(fbdev, display);1241 1242 break;1243 1244 case FB_BLANK_NORMAL:1245 /* FB_BLANK_NORMAL could be implemented.1246 * Needs DSS additions. */1247 case FB_BLANK_VSYNC_SUSPEND:1248 case FB_BLANK_HSYNC_SUSPEND:1249 case FB_BLANK_POWERDOWN:1250 if (display->state != OMAP_DSS_DISPLAY_ACTIVE)1251 goto exit;1252 1253 if (d->auto_update_work_enabled)1254 omapfb_stop_auto_update(fbdev, display);1255 1256 display->driver->disable(display);1257 1258 break;1259 1260 default:1261 r = -EINVAL;1262 }1263 1264exit:1265 omapfb_unlock(fbdev);1266 1267 return r;1268}1269 1270#if 01271/* XXX fb_read and fb_write are needed for VRFB */1272ssize_t omapfb_write(struct fb_info *info, const char __user *buf,1273 size_t count, loff_t *ppos)1274{1275 DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos);1276 /* XXX needed for VRFB */1277 return count;1278}1279#endif1280 1281static const struct fb_ops omapfb_ops = {1282 .owner = THIS_MODULE,1283 .fb_open = omapfb_open,1284 .fb_release = omapfb_release,1285 __FB_DEFAULT_IOMEM_OPS_RDWR,1286 .fb_blank = omapfb_blank,1287 __FB_DEFAULT_IOMEM_OPS_DRAW,1288 .fb_ioctl = omapfb_ioctl,1289 .fb_check_var = omapfb_check_var,1290 .fb_set_par = omapfb_set_par,1291 .fb_pan_display = omapfb_pan_display,1292 .fb_mmap = omapfb_mmap,1293 .fb_setcolreg = omapfb_setcolreg,1294 .fb_setcmap = omapfb_setcmap,1295 /*.fb_write = omapfb_write,*/1296};1297 1298static void omapfb_free_fbmem(struct fb_info *fbi)1299{1300 struct omapfb_info *ofbi = FB2OFB(fbi);1301 struct omapfb2_device *fbdev = ofbi->fbdev;1302 struct omapfb2_mem_region *rg;1303 1304 rg = ofbi->region;1305 1306 if (rg->token == NULL)1307 return;1308 1309 WARN_ON(atomic_read(&rg->map_count));1310 1311 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {1312 /* unmap the 0 angle rotation */1313 if (rg->vrfb.vaddr[0]) {1314 iounmap(rg->vrfb.vaddr[0]);1315 rg->vrfb.vaddr[0] = NULL;1316 }1317 1318 omap_vrfb_release_ctx(&rg->vrfb);1319 }1320 1321 dma_free_attrs(fbdev->dev, rg->size, rg->token, rg->dma_handle,1322 rg->attrs);1323 1324 rg->token = NULL;1325 rg->vaddr = NULL;1326 rg->paddr = 0;1327 rg->alloc = 0;1328 rg->size = 0;1329}1330 1331static void clear_fb_info(struct fb_info *fbi)1332{1333 memset(&fbi->var, 0, sizeof(fbi->var));1334 memset(&fbi->fix, 0, sizeof(fbi->fix));1335 strscpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id));1336}1337 1338static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev)1339{1340 int i;1341 1342 DBG("free all fbmem\n");1343 1344 for (i = 0; i < fbdev->num_fbs; i++) {1345 struct fb_info *fbi = fbdev->fbs[i];1346 omapfb_free_fbmem(fbi);1347 clear_fb_info(fbi);1348 }1349 1350 return 0;1351}1352 1353static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,1354 unsigned long paddr)1355{1356 struct omapfb_info *ofbi = FB2OFB(fbi);1357 struct omapfb2_device *fbdev = ofbi->fbdev;1358 struct omapfb2_mem_region *rg;1359 void *token;1360 unsigned long attrs;1361 dma_addr_t dma_handle;1362 int r;1363 1364 rg = ofbi->region;1365 1366 rg->paddr = 0;1367 rg->vaddr = NULL;1368 memset(&rg->vrfb, 0, sizeof rg->vrfb);1369 rg->size = 0;1370 rg->type = 0;1371 rg->alloc = false;1372 rg->map = false;1373 1374 size = PAGE_ALIGN(size);1375 1376 attrs = DMA_ATTR_WRITE_COMBINE;1377 1378 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB)1379 attrs |= DMA_ATTR_NO_KERNEL_MAPPING;1380 1381 DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);1382 1383 token = dma_alloc_attrs(fbdev->dev, size, &dma_handle,1384 GFP_KERNEL, attrs);1385 1386 if (token == NULL) {1387 dev_err(fbdev->dev, "failed to allocate framebuffer\n");1388 return -ENOMEM;1389 }1390 1391 DBG("allocated VRAM paddr %lx, vaddr %p\n",1392 (unsigned long)dma_handle, token);1393 1394 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {1395 r = omap_vrfb_request_ctx(&rg->vrfb);1396 if (r) {1397 dma_free_attrs(fbdev->dev, size, token, dma_handle,1398 attrs);1399 dev_err(fbdev->dev, "vrfb create ctx failed\n");1400 return r;1401 }1402 }1403 1404 rg->attrs = attrs;1405 rg->token = token;1406 rg->dma_handle = dma_handle;1407 1408 rg->paddr = (unsigned long)dma_handle;1409 rg->vaddr = (void __iomem *)token;1410 rg->size = size;1411 rg->alloc = 1;1412 1413 return 0;1414}1415 1416/* allocate fbmem using display resolution as reference */1417static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,1418 unsigned long paddr)1419{1420 struct omapfb_info *ofbi = FB2OFB(fbi);1421 struct omapfb2_device *fbdev = ofbi->fbdev;1422 struct omap_dss_device *display;1423 int bytespp;1424 1425 display = fb2display(fbi);1426 1427 if (!display)1428 return 0;1429 1430 switch (omapfb_get_recommended_bpp(fbdev, display)) {1431 case 16:1432 bytespp = 2;1433 break;1434 case 24:1435 bytespp = 4;1436 break;1437 default:1438 bytespp = 4;1439 break;1440 }1441 1442 if (!size) {1443 u16 w, h;1444 1445 display->driver->get_resolution(display, &w, &h);1446 1447 if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) {1448 size = max(omap_vrfb_min_phys_size(w, h, bytespp),1449 omap_vrfb_min_phys_size(h, w, bytespp));1450 1451 DBG("adjusting fb mem size for VRFB, %u -> %lu\n",1452 w * h * bytespp, size);1453 } else {1454 size = w * h * bytespp;1455 }1456 }1457 1458 if (!size)1459 return 0;1460 1461 return omapfb_alloc_fbmem(fbi, size, paddr);1462}1463 1464static int omapfb_parse_vram_param(const char *param, int max_entries,1465 unsigned long *sizes, unsigned long *paddrs)1466{1467 unsigned int fbnum;1468 unsigned long size;1469 unsigned long paddr = 0;1470 char *p, *start;1471 1472 start = (char *)param;1473 1474 while (1) {1475 p = start;1476 1477 fbnum = simple_strtoul(p, &p, 10);1478 1479 if (p == start)1480 return -EINVAL;1481 1482 if (*p != ':')1483 return -EINVAL;1484 1485 if (fbnum >= max_entries)1486 return -EINVAL;1487 1488 size = memparse(p + 1, &p);1489 1490 if (!size)1491 return -EINVAL;1492 1493 paddr = 0;1494 1495 if (*p == '@') {1496 paddr = simple_strtoul(p + 1, &p, 16);1497 1498 if (!paddr)1499 return -EINVAL;1500 1501 }1502 1503 WARN_ONCE(paddr,1504 "reserving memory at predefined address not supported\n");1505 1506 paddrs[fbnum] = paddr;1507 sizes[fbnum] = size;1508 1509 if (*p == 0)1510 break;1511 1512 if (*p != ',')1513 return -EINVAL;1514 1515 ++p;1516 1517 start = p;1518 }1519 1520 return 0;1521}1522 1523static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)1524{1525 int i, r;1526 unsigned long vram_sizes[10];1527 unsigned long vram_paddrs[10];1528 1529 memset(&vram_sizes, 0, sizeof(vram_sizes));1530 memset(&vram_paddrs, 0, sizeof(vram_paddrs));1531 1532 if (def_vram && omapfb_parse_vram_param(def_vram, 10,1533 vram_sizes, vram_paddrs)) {1534 dev_err(fbdev->dev, "failed to parse vram parameter\n");1535 1536 memset(&vram_sizes, 0, sizeof(vram_sizes));1537 memset(&vram_paddrs, 0, sizeof(vram_paddrs));1538 }1539 1540 for (i = 0; i < fbdev->num_fbs; i++) {1541 /* allocate memory automatically only for fb0, or if1542 * excplicitly defined with vram or plat data option */1543 if (i == 0 || vram_sizes[i] != 0) {1544 r = omapfb_alloc_fbmem_display(fbdev->fbs[i],1545 vram_sizes[i], vram_paddrs[i]);1546 1547 if (r)1548 return r;1549 }1550 }1551 1552 for (i = 0; i < fbdev->num_fbs; i++) {1553 struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);1554 struct omapfb2_mem_region *rg;1555 rg = ofbi->region;1556 1557 DBG("region%d phys %08x virt %p size=%lu\n",1558 i,1559 rg->paddr,1560 rg->vaddr,1561 rg->size);1562 }1563 1564 return 0;1565}1566 1567static void omapfb_clear_fb(struct fb_info *fbi)1568{1569 const struct fb_fillrect rect = {1570 .dx = 0,1571 .dy = 0,1572 .width = fbi->var.xres_virtual,1573 .height = fbi->var.yres_virtual,1574 .color = 0,1575 .rop = ROP_COPY,1576 };1577 1578 cfb_fillrect(fbi, &rect);1579}1580 1581int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)1582{1583 struct omapfb_info *ofbi = FB2OFB(fbi);1584 struct omapfb2_device *fbdev = ofbi->fbdev;1585 struct omapfb2_mem_region *rg = ofbi->region;1586 unsigned long old_size = rg->size;1587 unsigned long old_paddr = rg->paddr;1588 int old_type = rg->type;1589 int r;1590 1591 if (type != OMAPFB_MEMTYPE_SDRAM)1592 return -EINVAL;1593 1594 size = PAGE_ALIGN(size);1595 1596 if (old_size == size && old_type == type)1597 return 0;1598 1599 omapfb_free_fbmem(fbi);1600 1601 if (size == 0) {1602 clear_fb_info(fbi);1603 return 0;1604 }1605 1606 r = omapfb_alloc_fbmem(fbi, size, 0);1607 1608 if (r) {1609 if (old_size)1610 omapfb_alloc_fbmem(fbi, old_size, old_paddr);1611 1612 if (rg->size == 0)1613 clear_fb_info(fbi);1614 1615 return r;1616 }1617 1618 if (old_size == size)1619 return 0;1620 1621 if (old_size == 0) {1622 DBG("initializing fb %d\n", ofbi->id);1623 r = omapfb_fb_init(fbdev, fbi);1624 if (r) {1625 DBG("omapfb_fb_init failed\n");1626 goto err;1627 }1628 r = omapfb_apply_changes(fbi, 1);1629 if (r) {1630 DBG("omapfb_apply_changes failed\n");1631 goto err;1632 }1633 } else {1634 struct fb_var_screeninfo new_var;1635 memcpy(&new_var, &fbi->var, sizeof(new_var));1636 r = check_fb_var(fbi, &new_var);1637 if (r)1638 goto err;1639 memcpy(&fbi->var, &new_var, sizeof(fbi->var));1640 set_fb_fix(fbi);1641 r = setup_vrfb_rotation(fbi);1642 if (r)1643 goto err;1644 }1645 1646 omapfb_clear_fb(fbi);1647 1648 return 0;1649err:1650 omapfb_free_fbmem(fbi);1651 clear_fb_info(fbi);1652 return r;1653}1654 1655static void omapfb_auto_update_work(struct work_struct *work)1656{1657 struct omap_dss_device *dssdev;1658 struct omap_dss_driver *dssdrv;1659 struct omapfb_display_data *d;1660 u16 w, h;1661 unsigned int freq;1662 struct omapfb2_device *fbdev;1663 1664 d = container_of(work, struct omapfb_display_data,1665 auto_update_work.work);1666 1667 dssdev = d->dssdev;1668 dssdrv = dssdev->driver;1669 fbdev = d->fbdev;1670 1671 if (!dssdrv || !dssdrv->update)1672 return;1673 1674 if (dssdrv->sync)1675 dssdrv->sync(dssdev);1676 1677 dssdrv->get_resolution(dssdev, &w, &h);1678 dssdrv->update(dssdev, 0, 0, w, h);1679 1680 freq = auto_update_freq;1681 if (freq == 0)1682 freq = 20;1683 queue_delayed_work(fbdev->auto_update_wq,1684 &d->auto_update_work, HZ / freq);1685}1686 1687void omapfb_start_auto_update(struct omapfb2_device *fbdev,1688 struct omap_dss_device *display)1689{1690 struct omapfb_display_data *d;1691 1692 if (fbdev->auto_update_wq == NULL) {1693 struct workqueue_struct *wq;1694 1695 wq = create_singlethread_workqueue("omapfb_auto_update");1696 1697 if (wq == NULL) {1698 dev_err(fbdev->dev, "Failed to create workqueue for "1699 "auto-update\n");1700 return;1701 }1702 1703 fbdev->auto_update_wq = wq;1704 }1705 1706 d = get_display_data(fbdev, display);1707 1708 INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work);1709 1710 d->auto_update_work_enabled = true;1711 1712 omapfb_auto_update_work(&d->auto_update_work.work);1713}1714 1715void omapfb_stop_auto_update(struct omapfb2_device *fbdev,1716 struct omap_dss_device *display)1717{1718 struct omapfb_display_data *d;1719 1720 d = get_display_data(fbdev, display);1721 1722 cancel_delayed_work_sync(&d->auto_update_work);1723 1724 d->auto_update_work_enabled = false;1725}1726 1727/* initialize fb_info, var, fix to something sane based on the display */1728static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)1729{1730 struct fb_var_screeninfo *var = &fbi->var;1731 struct omap_dss_device *display = fb2display(fbi);1732 struct omapfb_info *ofbi = FB2OFB(fbi);1733 int r = 0;1734 1735 fbi->fbops = &omapfb_ops;1736 fbi->pseudo_palette = fbdev->pseudo_palette;1737 1738 if (ofbi->region->size == 0) {1739 clear_fb_info(fbi);1740 return 0;1741 }1742 1743 var->nonstd = 0;1744 var->bits_per_pixel = 0;1745 1746 var->rotate = def_rotate;1747 1748 if (display) {1749 u16 w, h;1750 int rotation = (var->rotate + ofbi->rotation[0]) % 4;1751 1752 display->driver->get_resolution(display, &w, &h);1753 1754 if (rotation == FB_ROTATE_CW ||1755 rotation == FB_ROTATE_CCW) {1756 var->xres = h;1757 var->yres = w;1758 } else {1759 var->xres = w;1760 var->yres = h;1761 }1762 1763 var->xres_virtual = var->xres;1764 var->yres_virtual = var->yres;1765 1766 if (!var->bits_per_pixel) {1767 switch (omapfb_get_recommended_bpp(fbdev, display)) {1768 case 16:1769 var->bits_per_pixel = 16;1770 break;1771 case 24:1772 var->bits_per_pixel = 32;1773 break;1774 default:1775 dev_err(fbdev->dev, "illegal display "1776 "bpp\n");1777 return -EINVAL;1778 }1779 }1780 } else {1781 /* if there's no display, let's just guess some basic values */1782 var->xres = 320;1783 var->yres = 240;1784 var->xres_virtual = var->xres;1785 var->yres_virtual = var->yres;1786 if (!var->bits_per_pixel)1787 var->bits_per_pixel = 16;1788 }1789 1790 r = check_fb_var(fbi, var);1791 if (r)1792 goto err;1793 1794 set_fb_fix(fbi);1795 r = setup_vrfb_rotation(fbi);1796 if (r)1797 goto err;1798 1799 r = fb_alloc_cmap(&fbi->cmap, 256, 0);1800 if (r)1801 dev_err(fbdev->dev, "unable to allocate color map memory\n");1802 1803err:1804 return r;1805}1806 1807static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi)1808{1809 fb_dealloc_cmap(&fbi->cmap);1810}1811 1812 1813static void omapfb_free_resources(struct omapfb2_device *fbdev)1814{1815 int i;1816 1817 DBG("free_resources\n");1818 1819 if (fbdev == NULL)1820 return;1821 1822 for (i = 0; i < fbdev->num_overlays; i++) {1823 struct omap_overlay *ovl = fbdev->overlays[i];1824 1825 ovl->disable(ovl);1826 1827 if (ovl->manager)1828 ovl->unset_manager(ovl);1829 }1830 1831 for (i = 0; i < fbdev->num_fbs; i++)1832 unregister_framebuffer(fbdev->fbs[i]);1833 1834 /* free the reserved fbmem */1835 omapfb_free_all_fbmem(fbdev);1836 1837 for (i = 0; i < fbdev->num_fbs; i++) {1838 fbinfo_cleanup(fbdev, fbdev->fbs[i]);1839 framebuffer_release(fbdev->fbs[i]);1840 }1841 1842 for (i = 0; i < fbdev->num_displays; i++) {1843 struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;1844 1845 if (fbdev->displays[i].auto_update_work_enabled)1846 omapfb_stop_auto_update(fbdev, dssdev);1847 1848 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)1849 dssdev->driver->disable(dssdev);1850 1851 dssdev->driver->disconnect(dssdev);1852 1853 omap_dss_put_device(dssdev);1854 }1855 1856 if (fbdev->auto_update_wq != NULL) {1857 destroy_workqueue(fbdev->auto_update_wq);1858 fbdev->auto_update_wq = NULL;1859 }1860 1861 dev_set_drvdata(fbdev->dev, NULL);1862}1863 1864static int omapfb_create_framebuffers(struct omapfb2_device *fbdev)1865{1866 int r, i;1867 1868 fbdev->num_fbs = 0;1869 1870 DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS);1871 1872 /* allocate fb_infos */1873 for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) {1874 struct fb_info *fbi;1875 struct omapfb_info *ofbi;1876 1877 fbi = framebuffer_alloc(sizeof(struct omapfb_info),1878 fbdev->dev);1879 if (!fbi)1880 return -ENOMEM;1881 1882 clear_fb_info(fbi);1883 1884 fbdev->fbs[i] = fbi;1885 1886 ofbi = FB2OFB(fbi);1887 ofbi->fbdev = fbdev;1888 ofbi->id = i;1889 1890 ofbi->region = &fbdev->regions[i];1891 ofbi->region->id = i;1892 init_rwsem(&ofbi->region->lock);1893 1894 /* assign these early, so that fb alloc can use them */1895 ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB :1896 OMAP_DSS_ROT_DMA;1897 ofbi->mirror = def_mirror;1898 1899 fbdev->num_fbs++;1900 }1901 1902 DBG("fb_infos allocated\n");1903 1904 /* assign overlays for the fbs */1905 for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) {1906 struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]);1907 1908 ofbi->overlays[0] = fbdev->overlays[i];1909 ofbi->num_overlays = 1;1910 }1911 1912 /* allocate fb memories */1913 r = omapfb_allocate_all_fbs(fbdev);1914 if (r) {1915 dev_err(fbdev->dev, "failed to allocate fbmem\n");1916 return r;1917 }1918 1919 DBG("fbmems allocated\n");1920 1921 /* setup fb_infos */1922 for (i = 0; i < fbdev->num_fbs; i++) {1923 struct fb_info *fbi = fbdev->fbs[i];1924 struct omapfb_info *ofbi = FB2OFB(fbi);1925 1926 omapfb_get_mem_region(ofbi->region);1927 r = omapfb_fb_init(fbdev, fbi);1928 omapfb_put_mem_region(ofbi->region);1929 1930 if (r) {1931 dev_err(fbdev->dev, "failed to setup fb_info\n");1932 return r;1933 }1934 }1935 1936 for (i = 0; i < fbdev->num_fbs; i++) {1937 struct fb_info *fbi = fbdev->fbs[i];1938 struct omapfb_info *ofbi = FB2OFB(fbi);1939 1940 if (ofbi->region->size == 0)1941 continue;1942 1943 omapfb_clear_fb(fbi);1944 }1945 1946 DBG("fb_infos initialized\n");1947 1948 for (i = 0; i < fbdev->num_fbs; i++) {1949 r = register_framebuffer(fbdev->fbs[i]);1950 if (r != 0) {1951 dev_err(fbdev->dev,1952 "registering framebuffer %d failed\n", i);1953 return r;1954 }1955 }1956 1957 DBG("framebuffers registered\n");1958 1959 for (i = 0; i < fbdev->num_fbs; i++) {1960 struct fb_info *fbi = fbdev->fbs[i];1961 struct omapfb_info *ofbi = FB2OFB(fbi);1962 1963 omapfb_get_mem_region(ofbi->region);1964 r = omapfb_apply_changes(fbi, 1);1965 omapfb_put_mem_region(ofbi->region);1966 1967 if (r) {1968 dev_err(fbdev->dev, "failed to change mode\n");1969 return r;1970 }1971 }1972 1973 /* Enable fb0 */1974 if (fbdev->num_fbs > 0) {1975 struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]);1976 1977 if (ofbi->num_overlays > 0) {1978 struct omap_overlay *ovl = ofbi->overlays[0];1979 1980 ovl->manager->apply(ovl->manager);1981 1982 r = omapfb_overlay_enable(ovl, 1);1983 1984 if (r) {1985 dev_err(fbdev->dev,1986 "failed to enable overlay\n");1987 return r;1988 }1989 }1990 }1991 1992 DBG("create_framebuffers done\n");1993 1994 return 0;1995}1996 1997static int omapfb_mode_to_timings(const char *mode_str,1998 struct omap_dss_device *display,1999 struct omap_video_timings *timings, u8 *bpp)2000{2001 struct fb_info *fbi;2002 struct fb_var_screeninfo *var;2003 struct fb_ops *fbops;2004 int r;2005 2006#ifdef CONFIG_OMAP2_DSS_VENC2007 if (strcmp(mode_str, "pal") == 0) {2008 *timings = omap_dss_pal_timings;2009 *bpp = 24;2010 return 0;2011 } else if (strcmp(mode_str, "ntsc") == 0) {2012 *timings = omap_dss_ntsc_timings;2013 *bpp = 24;2014 return 0;2015 }2016#endif2017 2018 /* this is quite a hack, but I wanted to use the modedb and for2019 * that we need fb_info and var, so we create dummy ones */2020 2021 *bpp = 0;2022 fbi = NULL;2023 var = NULL;2024 fbops = NULL;2025 2026 fbi = kzalloc(sizeof(*fbi), GFP_KERNEL);2027 if (fbi == NULL) {2028 r = -ENOMEM;2029 goto err;2030 }2031 2032 var = kzalloc(sizeof(*var), GFP_KERNEL);2033 if (var == NULL) {2034 r = -ENOMEM;2035 goto err;2036 }2037 2038 fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);2039 if (fbops == NULL) {2040 r = -ENOMEM;2041 goto err;2042 }2043 2044 fbi->fbops = fbops;2045 2046 r = fb_find_mode(var, fbi, mode_str, NULL, 0, NULL, 24);2047 if (r == 0) {2048 r = -EINVAL;2049 goto err;2050 }2051 2052 if (display->driver->get_timings) {2053 display->driver->get_timings(display, timings);2054 } else {2055 timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;2056 timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH;2057 timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;2058 }2059 2060 timings->pixelclock = PICOS2KHZ(var->pixclock) * 1000;2061 timings->hbp = var->left_margin;2062 timings->hfp = var->right_margin;2063 timings->vbp = var->upper_margin;2064 timings->vfp = var->lower_margin;2065 timings->hsw = var->hsync_len;2066 timings->vsw = var->vsync_len;2067 timings->x_res = var->xres;2068 timings->y_res = var->yres;2069 timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ?2070 OMAPDSS_SIG_ACTIVE_HIGH :2071 OMAPDSS_SIG_ACTIVE_LOW;2072 timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ?2073 OMAPDSS_SIG_ACTIVE_HIGH :2074 OMAPDSS_SIG_ACTIVE_LOW;2075 timings->interlace = var->vmode & FB_VMODE_INTERLACED;2076 2077 switch (var->bits_per_pixel) {2078 case 16:2079 *bpp = 16;2080 break;2081 case 24:2082 case 32:2083 default:2084 *bpp = 24;2085 break;2086 }2087 2088 r = 0;2089 2090err:2091 kfree(fbi);2092 kfree(var);2093 kfree(fbops);2094 2095 return r;2096}2097 2098static int omapfb_set_def_mode(struct omapfb2_device *fbdev,2099 struct omap_dss_device *display, char *mode_str)2100{2101 int r;2102 u8 bpp;2103 struct omap_video_timings timings, temp_timings;2104 struct omapfb_display_data *d;2105 2106 r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp);2107 if (r)2108 return r;2109 2110 d = get_display_data(fbdev, display);2111 d->bpp_override = bpp;2112 2113 if (display->driver->check_timings) {2114 r = display->driver->check_timings(display, &timings);2115 if (r)2116 return r;2117 } else {2118 /* If check_timings is not present compare xres and yres */2119 if (display->driver->get_timings) {2120 display->driver->get_timings(display, &temp_timings);2121 2122 if (temp_timings.x_res != timings.x_res ||2123 temp_timings.y_res != timings.y_res)2124 return -EINVAL;2125 }2126 }2127 2128 if (display->driver->set_timings)2129 display->driver->set_timings(display, &timings);2130 2131 return 0;2132}2133 2134static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev,2135 struct omap_dss_device *dssdev)2136{2137 struct omapfb_display_data *d;2138 2139 BUG_ON(dssdev->driver->get_recommended_bpp == NULL);2140 2141 d = get_display_data(fbdev, dssdev);2142 2143 if (d->bpp_override != 0)2144 return d->bpp_override;2145 2146 return dssdev->driver->get_recommended_bpp(dssdev);2147}2148 2149static int omapfb_parse_def_modes(struct omapfb2_device *fbdev)2150{2151 char *str, *options, *this_opt;2152 int r = 0;2153 2154 str = kstrdup(def_mode, GFP_KERNEL);2155 if (!str)2156 return -ENOMEM;2157 options = str;2158 2159 while (!r && (this_opt = strsep(&options, ",")) != NULL) {2160 char *p, *display_str, *mode_str;2161 struct omap_dss_device *display;2162 int i;2163 2164 p = strchr(this_opt, ':');2165 if (!p) {2166 r = -EINVAL;2167 break;2168 }2169 2170 *p = 0;2171 display_str = this_opt;2172 mode_str = p + 1;2173 2174 display = NULL;2175 for (i = 0; i < fbdev->num_displays; ++i) {2176 if (strcmp(fbdev->displays[i].dssdev->name,2177 display_str) == 0) {2178 display = fbdev->displays[i].dssdev;2179 break;2180 }2181 }2182 2183 if (!display) {2184 r = -EINVAL;2185 break;2186 }2187 2188 r = omapfb_set_def_mode(fbdev, display, mode_str);2189 if (r)2190 break;2191 }2192 2193 kfree(str);2194 2195 return r;2196}2197 2198static void fb_videomode_to_omap_timings(struct fb_videomode *m,2199 struct omap_dss_device *display,2200 struct omap_video_timings *t)2201{2202 if (display->driver->get_timings) {2203 display->driver->get_timings(display, t);2204 } else {2205 t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE;2206 t->de_level = OMAPDSS_SIG_ACTIVE_HIGH;2207 t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE;2208 }2209 2210 t->x_res = m->xres;2211 t->y_res = m->yres;2212 t->pixelclock = PICOS2KHZ(m->pixclock) * 1000;2213 t->hsw = m->hsync_len;2214 t->hfp = m->right_margin;2215 t->hbp = m->left_margin;2216 t->vsw = m->vsync_len;2217 t->vfp = m->lower_margin;2218 t->vbp = m->upper_margin;2219 t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ?2220 OMAPDSS_SIG_ACTIVE_HIGH :2221 OMAPDSS_SIG_ACTIVE_LOW;2222 t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ?2223 OMAPDSS_SIG_ACTIVE_HIGH :2224 OMAPDSS_SIG_ACTIVE_LOW;2225 t->interlace = m->vmode & FB_VMODE_INTERLACED;2226}2227 2228static int omapfb_find_best_mode(struct omap_dss_device *display,2229 struct omap_video_timings *timings)2230{2231 struct fb_monspecs *specs;2232 u8 *edid;2233 int r, i, best_idx, len;2234 2235 if (!display->driver->read_edid)2236 return -ENODEV;2237 2238 len = 0x80 * 2;2239 edid = kmalloc(len, GFP_KERNEL);2240 if (edid == NULL)2241 return -ENOMEM;2242 2243 r = display->driver->read_edid(display, edid, len);2244 if (r < 0)2245 goto err1;2246 2247 specs = kzalloc(sizeof(*specs), GFP_KERNEL);2248 if (specs == NULL) {2249 r = -ENOMEM;2250 goto err1;2251 }2252 2253 fb_edid_to_monspecs(edid, specs);2254 2255 best_idx = -1;2256 2257 for (i = 0; i < specs->modedb_len; ++i) {2258 struct fb_videomode *m;2259 struct omap_video_timings t;2260 2261 m = &specs->modedb[i];2262 2263 if (m->pixclock == 0)2264 continue;2265 2266 /* skip repeated pixel modes */2267 if (m->xres == 2880 || m->xres == 1440)2268 continue;2269 2270 if (m->vmode & FB_VMODE_INTERLACED ||2271 m->vmode & FB_VMODE_DOUBLE)2272 continue;2273 2274 fb_videomode_to_omap_timings(m, display, &t);2275 2276 r = display->driver->check_timings(display, &t);2277 if (r == 0) {2278 best_idx = i;2279 break;2280 }2281 }2282 2283 if (best_idx == -1) {2284 r = -ENOENT;2285 goto err2;2286 }2287 2288 fb_videomode_to_omap_timings(&specs->modedb[best_idx], display,2289 timings);2290 2291 r = 0;2292 2293err2:2294 fb_destroy_modedb(specs->modedb);2295 kfree(specs);2296err1:2297 kfree(edid);2298 2299 return r;2300}2301 2302static int omapfb_init_display(struct omapfb2_device *fbdev,2303 struct omap_dss_device *dssdev)2304{2305 struct omap_dss_driver *dssdrv = dssdev->driver;2306 struct omapfb_display_data *d;2307 int r;2308 2309 r = dssdrv->enable(dssdev);2310 if (r) {2311 dev_warn(fbdev->dev, "Failed to enable display '%s'\n",2312 dssdev->name);2313 return r;2314 }2315 2316 d = get_display_data(fbdev, dssdev);2317 2318 d->fbdev = fbdev;2319 2320 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {2321 u16 w, h;2322 2323 if (auto_update) {2324 omapfb_start_auto_update(fbdev, dssdev);2325 d->update_mode = OMAPFB_AUTO_UPDATE;2326 } else {2327 d->update_mode = OMAPFB_MANUAL_UPDATE;2328 }2329 2330 if (dssdrv->enable_te) {2331 r = dssdrv->enable_te(dssdev, 1);2332 if (r) {2333 dev_err(fbdev->dev, "Failed to set TE\n");2334 return r;2335 }2336 }2337 2338 dssdrv->get_resolution(dssdev, &w, &h);2339 r = dssdrv->update(dssdev, 0, 0, w, h);2340 if (r) {2341 dev_err(fbdev->dev,2342 "Failed to update display\n");2343 return r;2344 }2345 } else {2346 d->update_mode = OMAPFB_AUTO_UPDATE;2347 }2348 2349 return 0;2350}2351 2352static int omapfb_init_connections(struct omapfb2_device *fbdev,2353 struct omap_dss_device *def_dssdev)2354{2355 int i, r;2356 struct omap_overlay_manager *mgr;2357 2358 r = def_dssdev->driver->connect(def_dssdev);2359 if (r) {2360 dev_err(fbdev->dev, "failed to connect default display\n");2361 return r;2362 }2363 2364 for (i = 0; i < fbdev->num_displays; ++i) {2365 struct omap_dss_device *dssdev = fbdev->displays[i].dssdev;2366 2367 if (dssdev == def_dssdev)2368 continue;2369 2370 /*2371 * We don't care if the connect succeeds or not. We just want to2372 * connect as many displays as possible.2373 */2374 dssdev->driver->connect(dssdev);2375 }2376 2377 mgr = omapdss_find_mgr_from_display(def_dssdev);2378 2379 if (!mgr) {2380 dev_err(fbdev->dev, "no ovl manager for the default display\n");2381 return -EINVAL;2382 }2383 2384 for (i = 0; i < fbdev->num_overlays; i++) {2385 struct omap_overlay *ovl = fbdev->overlays[i];2386 2387 if (ovl->manager)2388 ovl->unset_manager(ovl);2389 2390 r = ovl->set_manager(ovl, mgr);2391 if (r)2392 dev_warn(fbdev->dev,2393 "failed to connect overlay %s to manager %s\n",2394 ovl->name, mgr->name);2395 }2396 2397 return 0;2398}2399 2400static struct omap_dss_device *2401omapfb_find_default_display(struct omapfb2_device *fbdev)2402{2403 const char *def_name;2404 int i;2405 2406 /*2407 * Search with the display name from the user or the board file,2408 * comparing to display names and aliases2409 */2410 2411 def_name = omapdss_get_default_display_name();2412 2413 if (def_name) {2414 for (i = 0; i < fbdev->num_displays; ++i) {2415 struct omap_dss_device *dssdev;2416 2417 dssdev = fbdev->displays[i].dssdev;2418 2419 if (dssdev->name && strcmp(def_name, dssdev->name) == 0)2420 return dssdev;2421 2422 if (strcmp(def_name, dssdev->alias) == 0)2423 return dssdev;2424 }2425 2426 /* def_name given but not found */2427 return NULL;2428 }2429 2430 /* then look for DT alias display0 */2431 for (i = 0; i < fbdev->num_displays; ++i) {2432 struct omap_dss_device *dssdev;2433 int id;2434 2435 dssdev = fbdev->displays[i].dssdev;2436 2437 if (dssdev->dev->of_node == NULL)2438 continue;2439 2440 id = of_alias_get_id(dssdev->dev->of_node, "display");2441 if (id == 0)2442 return dssdev;2443 }2444 2445 /* return the first display we have in the list */2446 return fbdev->displays[0].dssdev;2447}2448 2449static int omapfb_probe(struct platform_device *pdev)2450{2451 struct omapfb2_device *fbdev = NULL;2452 int r = 0;2453 int i;2454 struct omap_dss_device *def_display;2455 struct omap_dss_device *dssdev;2456 2457 DBG("omapfb_probe\n");2458 2459 if (omapdss_is_initialized() == false)2460 return -EPROBE_DEFER;2461 2462 if (pdev->num_resources != 0) {2463 dev_err(&pdev->dev, "probed for an unknown device\n");2464 r = -ENODEV;2465 goto err0;2466 }2467 2468 fbdev = devm_kzalloc(&pdev->dev, sizeof(struct omapfb2_device),2469 GFP_KERNEL);2470 if (fbdev == NULL) {2471 r = -ENOMEM;2472 goto err0;2473 }2474 2475 if (def_vrfb && !omap_vrfb_supported()) {2476 def_vrfb = 0;2477 dev_warn(&pdev->dev, "VRFB is not supported on this hardware, "2478 "ignoring the module parameter vrfb=y\n");2479 }2480 2481 r = omapdss_compat_init();2482 if (r)2483 goto err0;2484 2485 mutex_init(&fbdev->mtx);2486 2487 fbdev->dev = &pdev->dev;2488 platform_set_drvdata(pdev, fbdev);2489 2490 fbdev->num_displays = 0;2491 dssdev = NULL;2492 for_each_dss_dev(dssdev) {2493 struct omapfb_display_data *d;2494 2495 omap_dss_get_device(dssdev);2496 2497 if (!dssdev->driver) {2498 dev_warn(&pdev->dev, "no driver for display: %s\n",2499 dssdev->name);2500 omap_dss_put_device(dssdev);2501 continue;2502 }2503 2504 d = &fbdev->displays[fbdev->num_displays++];2505 d->dssdev = dssdev;2506 if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE)2507 d->update_mode = OMAPFB_MANUAL_UPDATE;2508 else2509 d->update_mode = OMAPFB_AUTO_UPDATE;2510 }2511 2512 if (fbdev->num_displays == 0) {2513 dev_err(&pdev->dev, "no displays\n");2514 r = -EPROBE_DEFER;2515 goto cleanup;2516 }2517 2518 fbdev->num_overlays = omap_dss_get_num_overlays();2519 for (i = 0; i < fbdev->num_overlays; i++)2520 fbdev->overlays[i] = omap_dss_get_overlay(i);2521 2522 fbdev->num_managers = omap_dss_get_num_overlay_managers();2523 for (i = 0; i < fbdev->num_managers; i++)2524 fbdev->managers[i] = omap_dss_get_overlay_manager(i);2525 2526 def_display = omapfb_find_default_display(fbdev);2527 if (def_display == NULL) {2528 dev_err(fbdev->dev, "failed to find default display\n");2529 r = -EPROBE_DEFER;2530 goto cleanup;2531 }2532 2533 r = omapfb_init_connections(fbdev, def_display);2534 if (r) {2535 dev_err(fbdev->dev, "failed to init overlay connections\n");2536 goto cleanup;2537 }2538 2539 if (def_mode && strlen(def_mode) > 0) {2540 if (omapfb_parse_def_modes(fbdev))2541 dev_warn(&pdev->dev, "cannot parse default modes\n");2542 } else if (def_display && def_display->driver->set_timings &&2543 def_display->driver->check_timings) {2544 struct omap_video_timings t;2545 2546 r = omapfb_find_best_mode(def_display, &t);2547 2548 if (r == 0)2549 def_display->driver->set_timings(def_display, &t);2550 }2551 2552 r = omapfb_create_framebuffers(fbdev);2553 if (r)2554 goto cleanup;2555 2556 for (i = 0; i < fbdev->num_managers; i++) {2557 struct omap_overlay_manager *mgr;2558 mgr = fbdev->managers[i];2559 r = mgr->apply(mgr);2560 if (r)2561 dev_warn(fbdev->dev, "failed to apply dispc config\n");2562 }2563 2564 DBG("mgr->apply'ed\n");2565 2566 if (def_display) {2567 r = omapfb_init_display(fbdev, def_display);2568 if (r) {2569 dev_err(fbdev->dev,2570 "failed to initialize default "2571 "display\n");2572 goto cleanup;2573 }2574 }2575 2576 DBG("create sysfs for fbs\n");2577 r = omapfb_create_sysfs(fbdev);2578 if (r) {2579 dev_err(fbdev->dev, "failed to create sysfs entries\n");2580 goto cleanup;2581 }2582 2583 if (def_display) {2584 u16 w, h;2585 2586 def_display->driver->get_resolution(def_display, &w, &h);2587 2588 dev_info(fbdev->dev, "using display '%s' mode %dx%d\n",2589 def_display->name, w, h);2590 }2591 2592 return 0;2593 2594cleanup:2595 omapfb_free_resources(fbdev);2596 omapdss_compat_uninit();2597err0:2598 dev_err(&pdev->dev, "failed to setup omapfb\n");2599 return r;2600}2601 2602static void omapfb_remove(struct platform_device *pdev)2603{2604 struct omapfb2_device *fbdev = platform_get_drvdata(pdev);2605 2606 /* FIXME: wait till completion of pending events */2607 2608 omapfb_remove_sysfs(fbdev);2609 2610 omapfb_free_resources(fbdev);2611 2612 omapdss_compat_uninit();2613}2614 2615static struct platform_driver omapfb_driver = {2616 .probe = omapfb_probe,2617 .remove = omapfb_remove,2618 .driver = {2619 .name = "omapfb",2620 },2621};2622 2623module_param_named(mode, def_mode, charp, 0);2624module_param_named(vram, def_vram, charp, 0);2625module_param_named(rotate, def_rotate, int, 0);2626module_param_named(vrfb, def_vrfb, bool, 0);2627module_param_named(mirror, def_mirror, bool, 0);2628 2629module_platform_driver(omapfb_driver);2630 2631MODULE_ALIAS("platform:omapfb");2632MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");2633MODULE_DESCRIPTION("OMAP2/3 Framebuffer");2634MODULE_LICENSE("GPL v2");2635