162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/video/omap2/omapfb-main.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 662306a36Sopenharmony_ci * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Some code and ideas taken from drivers/video/omap/ driver 962306a36Sopenharmony_ci * by Imre Deak. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/fb.h> 1662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1762306a36Sopenharmony_ci#include <linux/vmalloc.h> 1862306a36Sopenharmony_ci#include <linux/device.h> 1962306a36Sopenharmony_ci#include <linux/platform_device.h> 2062306a36Sopenharmony_ci#include <linux/omapfb.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <video/omapfb_dss.h> 2362306a36Sopenharmony_ci#include <video/omapvrfb.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "omapfb.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define MODULE_NAME "omapfb" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define OMAPFB_PLANE_XRES_MIN 8 3062306a36Sopenharmony_ci#define OMAPFB_PLANE_YRES_MIN 8 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic char *def_mode; 3362306a36Sopenharmony_cistatic char *def_vram; 3462306a36Sopenharmony_cistatic bool def_vrfb; 3562306a36Sopenharmony_cistatic int def_rotate; 3662306a36Sopenharmony_cistatic bool def_mirror; 3762306a36Sopenharmony_cistatic bool auto_update; 3862306a36Sopenharmony_cistatic unsigned int auto_update_freq; 3962306a36Sopenharmony_cimodule_param(auto_update, bool, 0); 4062306a36Sopenharmony_cimodule_param(auto_update_freq, uint, 0644); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#ifdef DEBUG 4362306a36Sopenharmony_cibool omapfb_debug; 4462306a36Sopenharmony_cimodule_param_named(debug, omapfb_debug, bool, 0644); 4562306a36Sopenharmony_cistatic bool omapfb_test_pattern; 4662306a36Sopenharmony_cimodule_param_named(test, omapfb_test_pattern, bool, 0644); 4762306a36Sopenharmony_ci#endif 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); 5062306a36Sopenharmony_cistatic int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, 5162306a36Sopenharmony_ci struct omap_dss_device *dssdev); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#ifdef DEBUG 5462306a36Sopenharmony_cistatic void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 5762306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 5862306a36Sopenharmony_ci void __iomem *addr = fbi->screen_base; 5962306a36Sopenharmony_ci const unsigned bytespp = var->bits_per_pixel >> 3; 6062306a36Sopenharmony_ci const unsigned line_len = fix->line_length / bytespp; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci int r = (color >> 16) & 0xff; 6362306a36Sopenharmony_ci int g = (color >> 8) & 0xff; 6462306a36Sopenharmony_ci int b = (color >> 0) & 0xff; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (var->bits_per_pixel == 16) { 6762306a36Sopenharmony_ci u16 __iomem *p = (u16 __iomem *)addr; 6862306a36Sopenharmony_ci p += y * line_len + x; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci r = r * 32 / 256; 7162306a36Sopenharmony_ci g = g * 64 / 256; 7262306a36Sopenharmony_ci b = b * 32 / 256; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci __raw_writew((r << 11) | (g << 5) | (b << 0), p); 7562306a36Sopenharmony_ci } else if (var->bits_per_pixel == 24) { 7662306a36Sopenharmony_ci u8 __iomem *p = (u8 __iomem *)addr; 7762306a36Sopenharmony_ci p += (y * line_len + x) * 3; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci __raw_writeb(b, p + 0); 8062306a36Sopenharmony_ci __raw_writeb(g, p + 1); 8162306a36Sopenharmony_ci __raw_writeb(r, p + 2); 8262306a36Sopenharmony_ci } else if (var->bits_per_pixel == 32) { 8362306a36Sopenharmony_ci u32 __iomem *p = (u32 __iomem *)addr; 8462306a36Sopenharmony_ci p += y * line_len + x; 8562306a36Sopenharmony_ci __raw_writel(color, p); 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic void fill_fb(struct fb_info *fbi) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 9262306a36Sopenharmony_ci const short w = var->xres_virtual; 9362306a36Sopenharmony_ci const short h = var->yres_virtual; 9462306a36Sopenharmony_ci void __iomem *addr = fbi->screen_base; 9562306a36Sopenharmony_ci int y, x; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (!addr) 9862306a36Sopenharmony_ci return; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (y = 0; y < h; y++) { 10362306a36Sopenharmony_ci for (x = 0; x < w; x++) { 10462306a36Sopenharmony_ci if (x < 20 && y < 20) 10562306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xffffff); 10662306a36Sopenharmony_ci else if (x < 20 && (y > 20 && y < h - 20)) 10762306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xff); 10862306a36Sopenharmony_ci else if (y < 20 && (x > 20 && x < w - 20)) 10962306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xff00); 11062306a36Sopenharmony_ci else if (x > w - 20 && (y > 20 && y < h - 20)) 11162306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xff0000); 11262306a36Sopenharmony_ci else if (y > h - 20 && (x > 20 && x < w - 20)) 11362306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xffff00); 11462306a36Sopenharmony_ci else if (x == 20 || x == w - 20 || 11562306a36Sopenharmony_ci y == 20 || y == h - 20) 11662306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xffffff); 11762306a36Sopenharmony_ci else if (x == y || w - x == h - y) 11862306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0xff00ff); 11962306a36Sopenharmony_ci else if (w - x == y || x == h - y) 12062306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0x00ffff); 12162306a36Sopenharmony_ci else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { 12262306a36Sopenharmony_ci int t = x * 3 / w; 12362306a36Sopenharmony_ci unsigned r = 0, g = 0, b = 0; 12462306a36Sopenharmony_ci unsigned c; 12562306a36Sopenharmony_ci if (var->bits_per_pixel == 16) { 12662306a36Sopenharmony_ci if (t == 0) 12762306a36Sopenharmony_ci b = (y % 32) * 256 / 32; 12862306a36Sopenharmony_ci else if (t == 1) 12962306a36Sopenharmony_ci g = (y % 64) * 256 / 64; 13062306a36Sopenharmony_ci else if (t == 2) 13162306a36Sopenharmony_ci r = (y % 32) * 256 / 32; 13262306a36Sopenharmony_ci } else { 13362306a36Sopenharmony_ci if (t == 0) 13462306a36Sopenharmony_ci b = (y % 256); 13562306a36Sopenharmony_ci else if (t == 1) 13662306a36Sopenharmony_ci g = (y % 256); 13762306a36Sopenharmony_ci else if (t == 2) 13862306a36Sopenharmony_ci r = (y % 256); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci c = (r << 16) | (g << 8) | (b << 0); 14162306a36Sopenharmony_ci draw_pixel(fbi, x, y, c); 14262306a36Sopenharmony_ci } else { 14362306a36Sopenharmony_ci draw_pixel(fbi, x, y, 0); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci#endif 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci const struct vrfb *vrfb = &ofbi->region->vrfb; 15362306a36Sopenharmony_ci unsigned offset; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci switch (rot) { 15662306a36Sopenharmony_ci case FB_ROTATE_UR: 15762306a36Sopenharmony_ci offset = 0; 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case FB_ROTATE_CW: 16062306a36Sopenharmony_ci offset = vrfb->yoffset; 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case FB_ROTATE_UD: 16362306a36Sopenharmony_ci offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case FB_ROTATE_CCW: 16662306a36Sopenharmony_ci offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; 16762306a36Sopenharmony_ci break; 16862306a36Sopenharmony_ci default: 16962306a36Sopenharmony_ci BUG(); 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci offset *= vrfb->bytespp; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return offset; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 18162306a36Sopenharmony_ci return ofbi->region->vrfb.paddr[rot] 18262306a36Sopenharmony_ci + omapfb_get_vrfb_offset(ofbi, rot); 18362306a36Sopenharmony_ci } else { 18462306a36Sopenharmony_ci return ofbi->region->paddr; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 19162306a36Sopenharmony_ci return ofbi->region->vrfb.paddr[0]; 19262306a36Sopenharmony_ci else 19362306a36Sopenharmony_ci return ofbi->region->paddr; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 19962306a36Sopenharmony_ci return ofbi->region->vrfb.vaddr[0]; 20062306a36Sopenharmony_ci else 20162306a36Sopenharmony_ci return ofbi->region->vaddr; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic struct omapfb_colormode omapfb_colormodes[] = { 20562306a36Sopenharmony_ci { 20662306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_UYVY, 20762306a36Sopenharmony_ci .bits_per_pixel = 16, 20862306a36Sopenharmony_ci .nonstd = OMAPFB_COLOR_YUV422, 20962306a36Sopenharmony_ci }, { 21062306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_YUV2, 21162306a36Sopenharmony_ci .bits_per_pixel = 16, 21262306a36Sopenharmony_ci .nonstd = OMAPFB_COLOR_YUY422, 21362306a36Sopenharmony_ci }, { 21462306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_ARGB16, 21562306a36Sopenharmony_ci .bits_per_pixel = 16, 21662306a36Sopenharmony_ci .red = { .length = 4, .offset = 8, .msb_right = 0 }, 21762306a36Sopenharmony_ci .green = { .length = 4, .offset = 4, .msb_right = 0 }, 21862306a36Sopenharmony_ci .blue = { .length = 4, .offset = 0, .msb_right = 0 }, 21962306a36Sopenharmony_ci .transp = { .length = 4, .offset = 12, .msb_right = 0 }, 22062306a36Sopenharmony_ci }, { 22162306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_RGB16, 22262306a36Sopenharmony_ci .bits_per_pixel = 16, 22362306a36Sopenharmony_ci .red = { .length = 5, .offset = 11, .msb_right = 0 }, 22462306a36Sopenharmony_ci .green = { .length = 6, .offset = 5, .msb_right = 0 }, 22562306a36Sopenharmony_ci .blue = { .length = 5, .offset = 0, .msb_right = 0 }, 22662306a36Sopenharmony_ci .transp = { .length = 0, .offset = 0, .msb_right = 0 }, 22762306a36Sopenharmony_ci }, { 22862306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_RGB24P, 22962306a36Sopenharmony_ci .bits_per_pixel = 24, 23062306a36Sopenharmony_ci .red = { .length = 8, .offset = 16, .msb_right = 0 }, 23162306a36Sopenharmony_ci .green = { .length = 8, .offset = 8, .msb_right = 0 }, 23262306a36Sopenharmony_ci .blue = { .length = 8, .offset = 0, .msb_right = 0 }, 23362306a36Sopenharmony_ci .transp = { .length = 0, .offset = 0, .msb_right = 0 }, 23462306a36Sopenharmony_ci }, { 23562306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_RGB24U, 23662306a36Sopenharmony_ci .bits_per_pixel = 32, 23762306a36Sopenharmony_ci .red = { .length = 8, .offset = 16, .msb_right = 0 }, 23862306a36Sopenharmony_ci .green = { .length = 8, .offset = 8, .msb_right = 0 }, 23962306a36Sopenharmony_ci .blue = { .length = 8, .offset = 0, .msb_right = 0 }, 24062306a36Sopenharmony_ci .transp = { .length = 0, .offset = 0, .msb_right = 0 }, 24162306a36Sopenharmony_ci }, { 24262306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_ARGB32, 24362306a36Sopenharmony_ci .bits_per_pixel = 32, 24462306a36Sopenharmony_ci .red = { .length = 8, .offset = 16, .msb_right = 0 }, 24562306a36Sopenharmony_ci .green = { .length = 8, .offset = 8, .msb_right = 0 }, 24662306a36Sopenharmony_ci .blue = { .length = 8, .offset = 0, .msb_right = 0 }, 24762306a36Sopenharmony_ci .transp = { .length = 8, .offset = 24, .msb_right = 0 }, 24862306a36Sopenharmony_ci }, { 24962306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_RGBA32, 25062306a36Sopenharmony_ci .bits_per_pixel = 32, 25162306a36Sopenharmony_ci .red = { .length = 8, .offset = 24, .msb_right = 0 }, 25262306a36Sopenharmony_ci .green = { .length = 8, .offset = 16, .msb_right = 0 }, 25362306a36Sopenharmony_ci .blue = { .length = 8, .offset = 8, .msb_right = 0 }, 25462306a36Sopenharmony_ci .transp = { .length = 8, .offset = 0, .msb_right = 0 }, 25562306a36Sopenharmony_ci }, { 25662306a36Sopenharmony_ci .dssmode = OMAP_DSS_COLOR_RGBX32, 25762306a36Sopenharmony_ci .bits_per_pixel = 32, 25862306a36Sopenharmony_ci .red = { .length = 8, .offset = 24, .msb_right = 0 }, 25962306a36Sopenharmony_ci .green = { .length = 8, .offset = 16, .msb_right = 0 }, 26062306a36Sopenharmony_ci .blue = { .length = 8, .offset = 8, .msb_right = 0 }, 26162306a36Sopenharmony_ci .transp = { .length = 0, .offset = 0, .msb_right = 0 }, 26262306a36Sopenharmony_ci }, 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci return f1->length == f2->length && 26862306a36Sopenharmony_ci f1->offset == f2->offset && 26962306a36Sopenharmony_ci f1->msb_right == f2->msb_right; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic bool cmp_var_to_colormode(struct fb_var_screeninfo *var, 27362306a36Sopenharmony_ci struct omapfb_colormode *color) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci if (var->bits_per_pixel == 0 || 27662306a36Sopenharmony_ci var->red.length == 0 || 27762306a36Sopenharmony_ci var->blue.length == 0 || 27862306a36Sopenharmony_ci var->green.length == 0) 27962306a36Sopenharmony_ci return false; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return var->bits_per_pixel == color->bits_per_pixel && 28262306a36Sopenharmony_ci cmp_component(&var->red, &color->red) && 28362306a36Sopenharmony_ci cmp_component(&var->green, &color->green) && 28462306a36Sopenharmony_ci cmp_component(&var->blue, &color->blue) && 28562306a36Sopenharmony_ci cmp_component(&var->transp, &color->transp); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void assign_colormode_to_var(struct fb_var_screeninfo *var, 28962306a36Sopenharmony_ci struct omapfb_colormode *color) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci var->bits_per_pixel = color->bits_per_pixel; 29262306a36Sopenharmony_ci var->nonstd = color->nonstd; 29362306a36Sopenharmony_ci var->red = color->red; 29462306a36Sopenharmony_ci var->green = color->green; 29562306a36Sopenharmony_ci var->blue = color->blue; 29662306a36Sopenharmony_ci var->transp = color->transp; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int fb_mode_to_dss_mode(struct fb_var_screeninfo *var, 30062306a36Sopenharmony_ci enum omap_color_mode *mode) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci enum omap_color_mode dssmode; 30362306a36Sopenharmony_ci int i; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* first match with nonstd field */ 30662306a36Sopenharmony_ci if (var->nonstd) { 30762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { 30862306a36Sopenharmony_ci struct omapfb_colormode *m = &omapfb_colormodes[i]; 30962306a36Sopenharmony_ci if (var->nonstd == m->nonstd) { 31062306a36Sopenharmony_ci assign_colormode_to_var(var, m); 31162306a36Sopenharmony_ci *mode = m->dssmode; 31262306a36Sopenharmony_ci return 0; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* then try exact match of bpp and colors */ 32062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { 32162306a36Sopenharmony_ci struct omapfb_colormode *m = &omapfb_colormodes[i]; 32262306a36Sopenharmony_ci if (cmp_var_to_colormode(var, m)) { 32362306a36Sopenharmony_ci assign_colormode_to_var(var, m); 32462306a36Sopenharmony_ci *mode = m->dssmode; 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* match with bpp if user has not filled color fields 33062306a36Sopenharmony_ci * properly */ 33162306a36Sopenharmony_ci switch (var->bits_per_pixel) { 33262306a36Sopenharmony_ci case 1: 33362306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_CLUT1; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case 2: 33662306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_CLUT2; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci case 4: 33962306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_CLUT4; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case 8: 34262306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_CLUT8; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci case 12: 34562306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_RGB12U; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case 16: 34862306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_RGB16; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci case 24: 35162306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_RGB24P; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case 32: 35462306a36Sopenharmony_ci dssmode = OMAP_DSS_COLOR_RGB24U; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci default: 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { 36162306a36Sopenharmony_ci struct omapfb_colormode *m = &omapfb_colormodes[i]; 36262306a36Sopenharmony_ci if (dssmode == m->dssmode) { 36362306a36Sopenharmony_ci assign_colormode_to_var(var, m); 36462306a36Sopenharmony_ci *mode = m->dssmode; 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int check_fb_res_bounds(struct fb_var_screeninfo *var) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci int xres_min = OMAPFB_PLANE_XRES_MIN; 37562306a36Sopenharmony_ci int xres_max = 2048; 37662306a36Sopenharmony_ci int yres_min = OMAPFB_PLANE_YRES_MIN; 37762306a36Sopenharmony_ci int yres_max = 2048; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* XXX: some applications seem to set virtual res to 0. */ 38062306a36Sopenharmony_ci if (var->xres_virtual == 0) 38162306a36Sopenharmony_ci var->xres_virtual = var->xres; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci if (var->yres_virtual == 0) 38462306a36Sopenharmony_ci var->yres_virtual = var->yres; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (var->xres < xres_min) 39062306a36Sopenharmony_ci var->xres = xres_min; 39162306a36Sopenharmony_ci if (var->yres < yres_min) 39262306a36Sopenharmony_ci var->yres = yres_min; 39362306a36Sopenharmony_ci if (var->xres > xres_max) 39462306a36Sopenharmony_ci var->xres = xres_max; 39562306a36Sopenharmony_ci if (var->yres > yres_max) 39662306a36Sopenharmony_ci var->yres = yres_max; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (var->xres > var->xres_virtual) 39962306a36Sopenharmony_ci var->xres = var->xres_virtual; 40062306a36Sopenharmony_ci if (var->yres > var->yres_virtual) 40162306a36Sopenharmony_ci var->yres = var->yres_virtual; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void shrink_height(unsigned long max_frame_size, 40762306a36Sopenharmony_ci struct fb_var_screeninfo *var) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci DBG("can't fit FB into memory, reducing y\n"); 41062306a36Sopenharmony_ci var->yres_virtual = max_frame_size / 41162306a36Sopenharmony_ci (var->xres_virtual * var->bits_per_pixel >> 3); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN) 41462306a36Sopenharmony_ci var->yres_virtual = OMAPFB_PLANE_YRES_MIN; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (var->yres > var->yres_virtual) 41762306a36Sopenharmony_ci var->yres = var->yres_virtual; 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void shrink_width(unsigned long max_frame_size, 42162306a36Sopenharmony_ci struct fb_var_screeninfo *var) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci DBG("can't fit FB into memory, reducing x\n"); 42462306a36Sopenharmony_ci var->xres_virtual = max_frame_size / var->yres_virtual / 42562306a36Sopenharmony_ci (var->bits_per_pixel >> 3); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN) 42862306a36Sopenharmony_ci var->xres_virtual = OMAPFB_PLANE_XRES_MIN; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (var->xres > var->xres_virtual) 43162306a36Sopenharmony_ci var->xres = var->xres_virtual; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int check_vrfb_fb_size(unsigned long region_size, 43562306a36Sopenharmony_ci const struct fb_var_screeninfo *var) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual, 43862306a36Sopenharmony_ci var->yres_virtual, var->bits_per_pixel >> 3); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return min_phys_size > region_size ? -EINVAL : 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int check_fb_size(const struct omapfb_info *ofbi, 44462306a36Sopenharmony_ci struct fb_var_screeninfo *var) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci unsigned long max_frame_size = ofbi->region->size; 44762306a36Sopenharmony_ci int bytespp = var->bits_per_pixel >> 3; 44862306a36Sopenharmony_ci unsigned long line_size = var->xres_virtual * bytespp; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 45162306a36Sopenharmony_ci /* One needs to check for both VRFB and OMAPFB limitations. */ 45262306a36Sopenharmony_ci if (check_vrfb_fb_size(max_frame_size, var)) 45362306a36Sopenharmony_ci shrink_height(omap_vrfb_max_height( 45462306a36Sopenharmony_ci max_frame_size, var->xres_virtual, bytespp) * 45562306a36Sopenharmony_ci line_size, var); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (check_vrfb_fb_size(max_frame_size, var)) { 45862306a36Sopenharmony_ci DBG("cannot fit FB to memory\n"); 45962306a36Sopenharmony_ci return -EINVAL; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (line_size * var->yres_virtual > max_frame_size) 46862306a36Sopenharmony_ci shrink_height(max_frame_size, var); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (line_size * var->yres_virtual > max_frame_size) { 47162306a36Sopenharmony_ci shrink_width(max_frame_size, var); 47262306a36Sopenharmony_ci line_size = var->xres_virtual * bytespp; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (line_size * var->yres_virtual > max_frame_size) { 47662306a36Sopenharmony_ci DBG("cannot fit FB to memory\n"); 47762306a36Sopenharmony_ci return -EINVAL; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/* 48462306a36Sopenharmony_ci * Consider if VRFB assisted rotation is in use and if the virtual space for 48562306a36Sopenharmony_ci * the zero degree view needs to be mapped. The need for mapping also acts as 48662306a36Sopenharmony_ci * the trigger for setting up the hardware on the context in question. This 48762306a36Sopenharmony_ci * ensures that one does not attempt to access the virtual view before the 48862306a36Sopenharmony_ci * hardware is serving the address translations. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic int setup_vrfb_rotation(struct fb_info *fbi) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 49362306a36Sopenharmony_ci struct omapfb2_mem_region *rg = ofbi->region; 49462306a36Sopenharmony_ci struct vrfb *vrfb = &rg->vrfb; 49562306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 49662306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 49762306a36Sopenharmony_ci unsigned bytespp; 49862306a36Sopenharmony_ci bool yuv_mode; 49962306a36Sopenharmony_ci enum omap_color_mode mode; 50062306a36Sopenharmony_ci int r; 50162306a36Sopenharmony_ci bool reconf; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB) 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci DBG("setup_vrfb_rotation\n"); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci r = fb_mode_to_dss_mode(var, &mode); 50962306a36Sopenharmony_ci if (r) 51062306a36Sopenharmony_ci return r; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci bytespp = var->bits_per_pixel >> 3; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* We need to reconfigure VRFB if the resolution changes, if yuv mode 51762306a36Sopenharmony_ci * is enabled/disabled, or if bytes per pixel changes */ 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* XXX we shouldn't allow this when framebuffer is mmapped */ 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci reconf = false; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci if (yuv_mode != vrfb->yuv_mode) 52462306a36Sopenharmony_ci reconf = true; 52562306a36Sopenharmony_ci else if (bytespp != vrfb->bytespp) 52662306a36Sopenharmony_ci reconf = true; 52762306a36Sopenharmony_ci else if (vrfb->xres != var->xres_virtual || 52862306a36Sopenharmony_ci vrfb->yres != var->yres_virtual) 52962306a36Sopenharmony_ci reconf = true; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (vrfb->vaddr[0] && reconf) { 53262306a36Sopenharmony_ci fbi->screen_base = NULL; 53362306a36Sopenharmony_ci fix->smem_start = 0; 53462306a36Sopenharmony_ci fix->smem_len = 0; 53562306a36Sopenharmony_ci iounmap(vrfb->vaddr[0]); 53662306a36Sopenharmony_ci vrfb->vaddr[0] = NULL; 53762306a36Sopenharmony_ci DBG("setup_vrfb_rotation: reset fb\n"); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (vrfb->vaddr[0]) 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci omap_vrfb_setup(&rg->vrfb, rg->paddr, 54462306a36Sopenharmony_ci var->xres_virtual, 54562306a36Sopenharmony_ci var->yres_virtual, 54662306a36Sopenharmony_ci bytespp, yuv_mode); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Now one can ioremap the 0 angle view */ 54962306a36Sopenharmony_ci r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0); 55062306a36Sopenharmony_ci if (r) 55162306a36Sopenharmony_ci return r; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* used by open/write in fbmem.c */ 55462306a36Sopenharmony_ci fbi->screen_base = ofbi->region->vrfb.vaddr[0]; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci fix->smem_start = ofbi->region->vrfb.paddr[0]; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci switch (var->nonstd) { 55962306a36Sopenharmony_ci case OMAPFB_COLOR_YUV422: 56062306a36Sopenharmony_ci case OMAPFB_COLOR_YUY422: 56162306a36Sopenharmony_ci fix->line_length = 56262306a36Sopenharmony_ci (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; 56362306a36Sopenharmony_ci break; 56462306a36Sopenharmony_ci default: 56562306a36Sopenharmony_ci fix->line_length = 56662306a36Sopenharmony_ci (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci fix->smem_len = var->yres_virtual * fix->line_length; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci return 0; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ciint dss_mode_to_fb_mode(enum omap_color_mode dssmode, 57662306a36Sopenharmony_ci struct fb_var_screeninfo *var) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci int i; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { 58162306a36Sopenharmony_ci struct omapfb_colormode *mode = &omapfb_colormodes[i]; 58262306a36Sopenharmony_ci if (dssmode == mode->dssmode) { 58362306a36Sopenharmony_ci assign_colormode_to_var(var, mode); 58462306a36Sopenharmony_ci return 0; 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci return -ENOENT; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_civoid set_fb_fix(struct fb_info *fbi) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 59362306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 59462306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 59562306a36Sopenharmony_ci struct omapfb2_mem_region *rg = ofbi->region; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci DBG("set_fb_fix\n"); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* used by open/write in fbmem.c */ 60062306a36Sopenharmony_ci fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* used by mmap in fbmem.c */ 60362306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 60462306a36Sopenharmony_ci switch (var->nonstd) { 60562306a36Sopenharmony_ci case OMAPFB_COLOR_YUV422: 60662306a36Sopenharmony_ci case OMAPFB_COLOR_YUY422: 60762306a36Sopenharmony_ci fix->line_length = 60862306a36Sopenharmony_ci (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; 60962306a36Sopenharmony_ci break; 61062306a36Sopenharmony_ci default: 61162306a36Sopenharmony_ci fix->line_length = 61262306a36Sopenharmony_ci (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci fix->smem_len = var->yres_virtual * fix->line_length; 61762306a36Sopenharmony_ci } else { 61862306a36Sopenharmony_ci fix->line_length = 61962306a36Sopenharmony_ci (var->xres_virtual * var->bits_per_pixel) >> 3; 62062306a36Sopenharmony_ci fix->smem_len = rg->size; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci fix->smem_start = omapfb_get_region_paddr(ofbi); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci fix->type = FB_TYPE_PACKED_PIXELS; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (var->nonstd) 62862306a36Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 62962306a36Sopenharmony_ci else { 63062306a36Sopenharmony_ci switch (var->bits_per_pixel) { 63162306a36Sopenharmony_ci case 32: 63262306a36Sopenharmony_ci case 24: 63362306a36Sopenharmony_ci case 16: 63462306a36Sopenharmony_ci case 12: 63562306a36Sopenharmony_ci fix->visual = FB_VISUAL_TRUECOLOR; 63662306a36Sopenharmony_ci /* 12bpp is stored in 16 bits */ 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci case 1: 63962306a36Sopenharmony_ci case 2: 64062306a36Sopenharmony_ci case 4: 64162306a36Sopenharmony_ci case 8: 64262306a36Sopenharmony_ci fix->visual = FB_VISUAL_PSEUDOCOLOR; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci fix->accel = FB_ACCEL_NONE; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci fix->xpanstep = 1; 65062306a36Sopenharmony_ci fix->ypanstep = 1; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/* check new var and possibly modify it to be ok */ 65462306a36Sopenharmony_ciint check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) 65562306a36Sopenharmony_ci{ 65662306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 65762306a36Sopenharmony_ci struct omap_dss_device *display = fb2display(fbi); 65862306a36Sopenharmony_ci enum omap_color_mode mode = 0; 65962306a36Sopenharmony_ci int i; 66062306a36Sopenharmony_ci int r; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci DBG("check_fb_var %d\n", ofbi->id); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci WARN_ON(!atomic_read(&ofbi->region->lock_count)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci r = fb_mode_to_dss_mode(var, &mode); 66762306a36Sopenharmony_ci if (r) { 66862306a36Sopenharmony_ci DBG("cannot convert var to omap dss mode\n"); 66962306a36Sopenharmony_ci return r; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci for (i = 0; i < ofbi->num_overlays; ++i) { 67362306a36Sopenharmony_ci if ((ofbi->overlays[i]->supported_modes & mode) == 0) { 67462306a36Sopenharmony_ci DBG("invalid mode\n"); 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (var->rotate > 3) 68062306a36Sopenharmony_ci return -EINVAL; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (check_fb_res_bounds(var)) 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* When no memory is allocated ignore the size check */ 68662306a36Sopenharmony_ci if (ofbi->region->size != 0 && check_fb_size(ofbi, var)) 68762306a36Sopenharmony_ci return -EINVAL; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (var->xres + var->xoffset > var->xres_virtual) 69062306a36Sopenharmony_ci var->xoffset = var->xres_virtual - var->xres; 69162306a36Sopenharmony_ci if (var->yres + var->yoffset > var->yres_virtual) 69262306a36Sopenharmony_ci var->yoffset = var->yres_virtual - var->yres; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", 69562306a36Sopenharmony_ci var->xres, var->yres, 69662306a36Sopenharmony_ci var->xres_virtual, var->yres_virtual); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (display && display->driver->get_dimensions) { 69962306a36Sopenharmony_ci u32 w, h; 70062306a36Sopenharmony_ci display->driver->get_dimensions(display, &w, &h); 70162306a36Sopenharmony_ci var->width = DIV_ROUND_CLOSEST(w, 1000); 70262306a36Sopenharmony_ci var->height = DIV_ROUND_CLOSEST(h, 1000); 70362306a36Sopenharmony_ci } else { 70462306a36Sopenharmony_ci var->height = -1; 70562306a36Sopenharmony_ci var->width = -1; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci var->grayscale = 0; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci if (display && display->driver->get_timings) { 71162306a36Sopenharmony_ci struct omap_video_timings timings; 71262306a36Sopenharmony_ci display->driver->get_timings(display, &timings); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* pixclock in ps, the rest in pixclock */ 71562306a36Sopenharmony_ci var->pixclock = timings.pixelclock != 0 ? 71662306a36Sopenharmony_ci KHZ2PICOS(timings.pixelclock / 1000) : 71762306a36Sopenharmony_ci 0; 71862306a36Sopenharmony_ci var->left_margin = timings.hbp; 71962306a36Sopenharmony_ci var->right_margin = timings.hfp; 72062306a36Sopenharmony_ci var->upper_margin = timings.vbp; 72162306a36Sopenharmony_ci var->lower_margin = timings.vfp; 72262306a36Sopenharmony_ci var->hsync_len = timings.hsw; 72362306a36Sopenharmony_ci var->vsync_len = timings.vsw; 72462306a36Sopenharmony_ci var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? 72562306a36Sopenharmony_ci FB_SYNC_HOR_HIGH_ACT : 0; 72662306a36Sopenharmony_ci var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? 72762306a36Sopenharmony_ci FB_SYNC_VERT_HIGH_ACT : 0; 72862306a36Sopenharmony_ci var->vmode = timings.interlace ? 72962306a36Sopenharmony_ci FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED; 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci var->pixclock = 0; 73262306a36Sopenharmony_ci var->left_margin = 0; 73362306a36Sopenharmony_ci var->right_margin = 0; 73462306a36Sopenharmony_ci var->upper_margin = 0; 73562306a36Sopenharmony_ci var->lower_margin = 0; 73662306a36Sopenharmony_ci var->hsync_len = 0; 73762306a36Sopenharmony_ci var->vsync_len = 0; 73862306a36Sopenharmony_ci var->sync = 0; 73962306a36Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci/* 74662306a36Sopenharmony_ci * --------------------------------------------------------------------------- 74762306a36Sopenharmony_ci * fbdev framework callbacks 74862306a36Sopenharmony_ci * --------------------------------------------------------------------------- 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_cistatic int omapfb_open(struct fb_info *fbi, int user) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int omapfb_release(struct fb_info *fbi, int user) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic unsigned calc_rotation_offset_dma(const struct fb_var_screeninfo *var, 76162306a36Sopenharmony_ci const struct fb_fix_screeninfo *fix, int rotation) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci unsigned offset; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci offset = var->yoffset * fix->line_length + 76662306a36Sopenharmony_ci var->xoffset * (var->bits_per_pixel >> 3); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return offset; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_cistatic unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, 77262306a36Sopenharmony_ci const struct fb_fix_screeninfo *fix, int rotation) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci unsigned offset; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (rotation == FB_ROTATE_UD) 77762306a36Sopenharmony_ci offset = (var->yres_virtual - var->yres) * 77862306a36Sopenharmony_ci fix->line_length; 77962306a36Sopenharmony_ci else if (rotation == FB_ROTATE_CW) 78062306a36Sopenharmony_ci offset = (var->yres_virtual - var->yres) * 78162306a36Sopenharmony_ci (var->bits_per_pixel >> 3); 78262306a36Sopenharmony_ci else 78362306a36Sopenharmony_ci offset = 0; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (rotation == FB_ROTATE_UR) 78662306a36Sopenharmony_ci offset += var->yoffset * fix->line_length + 78762306a36Sopenharmony_ci var->xoffset * (var->bits_per_pixel >> 3); 78862306a36Sopenharmony_ci else if (rotation == FB_ROTATE_UD) 78962306a36Sopenharmony_ci offset -= var->yoffset * fix->line_length + 79062306a36Sopenharmony_ci var->xoffset * (var->bits_per_pixel >> 3); 79162306a36Sopenharmony_ci else if (rotation == FB_ROTATE_CW) 79262306a36Sopenharmony_ci offset -= var->xoffset * fix->line_length + 79362306a36Sopenharmony_ci var->yoffset * (var->bits_per_pixel >> 3); 79462306a36Sopenharmony_ci else if (rotation == FB_ROTATE_CCW) 79562306a36Sopenharmony_ci offset += var->xoffset * fix->line_length + 79662306a36Sopenharmony_ci var->yoffset * (var->bits_per_pixel >> 3); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return offset; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_cistatic void omapfb_calc_addr(const struct omapfb_info *ofbi, 80262306a36Sopenharmony_ci const struct fb_var_screeninfo *var, 80362306a36Sopenharmony_ci const struct fb_fix_screeninfo *fix, 80462306a36Sopenharmony_ci int rotation, u32 *paddr) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci u32 data_start_p; 80762306a36Sopenharmony_ci int offset; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 81062306a36Sopenharmony_ci data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); 81162306a36Sopenharmony_ci else 81262306a36Sopenharmony_ci data_start_p = omapfb_get_region_paddr(ofbi); 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 81562306a36Sopenharmony_ci offset = calc_rotation_offset_vrfb(var, fix, rotation); 81662306a36Sopenharmony_ci else 81762306a36Sopenharmony_ci offset = calc_rotation_offset_dma(var, fix, rotation); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci data_start_p += offset; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci if (offset) 82262306a36Sopenharmony_ci DBG("offset %d, %d = %d\n", 82362306a36Sopenharmony_ci var->xoffset, var->yoffset, offset); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci DBG("paddr %x\n", data_start_p); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci *paddr = data_start_p; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci/* setup overlay according to the fb */ 83162306a36Sopenharmony_ciint omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, 83262306a36Sopenharmony_ci u16 posx, u16 posy, u16 outw, u16 outh) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci int r = 0; 83562306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 83662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 83762306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 83862306a36Sopenharmony_ci enum omap_color_mode mode = 0; 83962306a36Sopenharmony_ci u32 data_start_p = 0; 84062306a36Sopenharmony_ci struct omap_overlay_info info; 84162306a36Sopenharmony_ci int xres, yres; 84262306a36Sopenharmony_ci int screen_width; 84362306a36Sopenharmony_ci int mirror; 84462306a36Sopenharmony_ci int rotation = var->rotate; 84562306a36Sopenharmony_ci int i; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci WARN_ON(!atomic_read(&ofbi->region->lock_count)); 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci for (i = 0; i < ofbi->num_overlays; i++) { 85062306a36Sopenharmony_ci if (ovl != ofbi->overlays[i]) 85162306a36Sopenharmony_ci continue; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci rotation = (rotation + ofbi->rotation[i]) % 4; 85462306a36Sopenharmony_ci break; 85562306a36Sopenharmony_ci } 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, 85862306a36Sopenharmony_ci posx, posy, outw, outh); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { 86162306a36Sopenharmony_ci xres = var->yres; 86262306a36Sopenharmony_ci yres = var->xres; 86362306a36Sopenharmony_ci } else { 86462306a36Sopenharmony_ci xres = var->xres; 86562306a36Sopenharmony_ci yres = var->yres; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci if (ofbi->region->size) 86962306a36Sopenharmony_ci omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci r = fb_mode_to_dss_mode(var, &mode); 87262306a36Sopenharmony_ci if (r) { 87362306a36Sopenharmony_ci DBG("fb_mode_to_dss_mode failed"); 87462306a36Sopenharmony_ci goto err; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci switch (var->nonstd) { 87862306a36Sopenharmony_ci case OMAPFB_COLOR_YUV422: 87962306a36Sopenharmony_ci case OMAPFB_COLOR_YUY422: 88062306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 88162306a36Sopenharmony_ci screen_width = fix->line_length 88262306a36Sopenharmony_ci / (var->bits_per_pixel >> 2); 88362306a36Sopenharmony_ci break; 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci fallthrough; 88662306a36Sopenharmony_ci default: 88762306a36Sopenharmony_ci screen_width = fix->line_length / (var->bits_per_pixel >> 3); 88862306a36Sopenharmony_ci break; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci ovl->get_overlay_info(ovl, &info); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 89462306a36Sopenharmony_ci mirror = 0; 89562306a36Sopenharmony_ci else 89662306a36Sopenharmony_ci mirror = ofbi->mirror; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci info.paddr = data_start_p; 89962306a36Sopenharmony_ci info.screen_width = screen_width; 90062306a36Sopenharmony_ci info.width = xres; 90162306a36Sopenharmony_ci info.height = yres; 90262306a36Sopenharmony_ci info.color_mode = mode; 90362306a36Sopenharmony_ci info.rotation_type = ofbi->rotation_type; 90462306a36Sopenharmony_ci info.rotation = rotation; 90562306a36Sopenharmony_ci info.mirror = mirror; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci info.pos_x = posx; 90862306a36Sopenharmony_ci info.pos_y = posy; 90962306a36Sopenharmony_ci info.out_width = outw; 91062306a36Sopenharmony_ci info.out_height = outh; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci r = ovl->set_overlay_info(ovl, &info); 91362306a36Sopenharmony_ci if (r) { 91462306a36Sopenharmony_ci DBG("ovl->setup_overlay_info failed\n"); 91562306a36Sopenharmony_ci goto err; 91662306a36Sopenharmony_ci } 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cierr: 92162306a36Sopenharmony_ci DBG("setup_overlay failed\n"); 92262306a36Sopenharmony_ci return r; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci/* apply var to the overlay */ 92662306a36Sopenharmony_ciint omapfb_apply_changes(struct fb_info *fbi, int init) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci int r = 0; 92962306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 93062306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 93162306a36Sopenharmony_ci struct omap_overlay *ovl; 93262306a36Sopenharmony_ci u16 posx, posy; 93362306a36Sopenharmony_ci u16 outw, outh; 93462306a36Sopenharmony_ci int i; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci#ifdef DEBUG 93762306a36Sopenharmony_ci if (omapfb_test_pattern) 93862306a36Sopenharmony_ci fill_fb(fbi); 93962306a36Sopenharmony_ci#endif 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci WARN_ON(!atomic_read(&ofbi->region->lock_count)); 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci for (i = 0; i < ofbi->num_overlays; i++) { 94462306a36Sopenharmony_ci ovl = ofbi->overlays[i]; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (ofbi->region->size == 0) { 94962306a36Sopenharmony_ci /* the fb is not available. disable the overlay */ 95062306a36Sopenharmony_ci omapfb_overlay_enable(ovl, 0); 95162306a36Sopenharmony_ci if (!init && ovl->manager) 95262306a36Sopenharmony_ci ovl->manager->apply(ovl->manager); 95362306a36Sopenharmony_ci continue; 95462306a36Sopenharmony_ci } 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { 95762306a36Sopenharmony_ci int rotation = (var->rotate + ofbi->rotation[i]) % 4; 95862306a36Sopenharmony_ci if (rotation == FB_ROTATE_CW || 95962306a36Sopenharmony_ci rotation == FB_ROTATE_CCW) { 96062306a36Sopenharmony_ci outw = var->yres; 96162306a36Sopenharmony_ci outh = var->xres; 96262306a36Sopenharmony_ci } else { 96362306a36Sopenharmony_ci outw = var->xres; 96462306a36Sopenharmony_ci outh = var->yres; 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci } else { 96762306a36Sopenharmony_ci struct omap_overlay_info info; 96862306a36Sopenharmony_ci ovl->get_overlay_info(ovl, &info); 96962306a36Sopenharmony_ci outw = info.out_width; 97062306a36Sopenharmony_ci outh = info.out_height; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (init) { 97462306a36Sopenharmony_ci posx = 0; 97562306a36Sopenharmony_ci posy = 0; 97662306a36Sopenharmony_ci } else { 97762306a36Sopenharmony_ci struct omap_overlay_info info; 97862306a36Sopenharmony_ci ovl->get_overlay_info(ovl, &info); 97962306a36Sopenharmony_ci posx = info.pos_x; 98062306a36Sopenharmony_ci posy = info.pos_y; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); 98462306a36Sopenharmony_ci if (r) 98562306a36Sopenharmony_ci goto err; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (!init && ovl->manager) 98862306a36Sopenharmony_ci ovl->manager->apply(ovl->manager); 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci return 0; 99162306a36Sopenharmony_cierr: 99262306a36Sopenharmony_ci DBG("apply_changes failed\n"); 99362306a36Sopenharmony_ci return r; 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci/* checks var and eventually tweaks it to something supported, 99762306a36Sopenharmony_ci * DO NOT MODIFY PAR */ 99862306a36Sopenharmony_cistatic int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 100162306a36Sopenharmony_ci int r; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci DBG("check_var(%d)\n", FB2OFB(fbi)->id); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci omapfb_get_mem_region(ofbi->region); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci r = check_fb_var(fbi, var); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return r; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci/* set the video mode according to info->var */ 101562306a36Sopenharmony_cistatic int omapfb_set_par(struct fb_info *fbi) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 101862306a36Sopenharmony_ci int r; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci DBG("set_par(%d)\n", FB2OFB(fbi)->id); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci omapfb_get_mem_region(ofbi->region); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci set_fb_fix(fbi); 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci r = setup_vrfb_rotation(fbi); 102762306a36Sopenharmony_ci if (r) 102862306a36Sopenharmony_ci goto out; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci r = omapfb_apply_changes(fbi, 0); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci out: 103362306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci return r; 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic int omapfb_pan_display(struct fb_var_screeninfo *var, 103962306a36Sopenharmony_ci struct fb_info *fbi) 104062306a36Sopenharmony_ci{ 104162306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 104262306a36Sopenharmony_ci struct fb_var_screeninfo new_var; 104362306a36Sopenharmony_ci int r; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci DBG("pan_display(%d)\n", FB2OFB(fbi)->id); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci if (var->xoffset == fbi->var.xoffset && 104862306a36Sopenharmony_ci var->yoffset == fbi->var.yoffset) 104962306a36Sopenharmony_ci return 0; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci new_var = fbi->var; 105262306a36Sopenharmony_ci new_var.xoffset = var->xoffset; 105362306a36Sopenharmony_ci new_var.yoffset = var->yoffset; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci fbi->var = new_var; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci omapfb_get_mem_region(ofbi->region); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci r = omapfb_apply_changes(fbi, 0); 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return r; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic void mmap_user_open(struct vm_area_struct *vma) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct omapfb2_mem_region *rg = vma->vm_private_data; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci omapfb_get_mem_region(rg); 107162306a36Sopenharmony_ci atomic_inc(&rg->map_count); 107262306a36Sopenharmony_ci omapfb_put_mem_region(rg); 107362306a36Sopenharmony_ci} 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic void mmap_user_close(struct vm_area_struct *vma) 107662306a36Sopenharmony_ci{ 107762306a36Sopenharmony_ci struct omapfb2_mem_region *rg = vma->vm_private_data; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci omapfb_get_mem_region(rg); 108062306a36Sopenharmony_ci atomic_dec(&rg->map_count); 108162306a36Sopenharmony_ci omapfb_put_mem_region(rg); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic const struct vm_operations_struct mmap_user_ops = { 108562306a36Sopenharmony_ci .open = mmap_user_open, 108662306a36Sopenharmony_ci .close = mmap_user_close, 108762306a36Sopenharmony_ci}; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) 109062306a36Sopenharmony_ci{ 109162306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 109262306a36Sopenharmony_ci struct fb_fix_screeninfo *fix = &fbi->fix; 109362306a36Sopenharmony_ci struct omapfb2_mem_region *rg; 109462306a36Sopenharmony_ci unsigned long start; 109562306a36Sopenharmony_ci u32 len; 109662306a36Sopenharmony_ci int r; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci rg = omapfb_get_mem_region(ofbi->region); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci start = omapfb_get_region_paddr(ofbi); 110162306a36Sopenharmony_ci len = fix->smem_len; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci DBG("user mmap region start %lx, len %d, off %lx\n", start, len, 110462306a36Sopenharmony_ci vma->vm_pgoff << PAGE_SHIFT); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); 110762306a36Sopenharmony_ci vma->vm_ops = &mmap_user_ops; 110862306a36Sopenharmony_ci vma->vm_private_data = rg; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci r = vm_iomap_memory(vma, start, len); 111162306a36Sopenharmony_ci if (r) 111262306a36Sopenharmony_ci goto error; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* vm_ops.open won't be called for mmap itself. */ 111562306a36Sopenharmony_ci atomic_inc(&rg->map_count); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci omapfb_put_mem_region(rg); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci return 0; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cierror: 112262306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return r; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci/* Store a single color palette entry into a pseudo palette or the hardware 112862306a36Sopenharmony_ci * palette if one is available. For now we support only 16bpp and thus store 112962306a36Sopenharmony_ci * the entry only to the pseudo palette. 113062306a36Sopenharmony_ci */ 113162306a36Sopenharmony_cistatic int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, 113262306a36Sopenharmony_ci u_int blue, u_int transp, int update_hw_pal) 113362306a36Sopenharmony_ci{ 113462306a36Sopenharmony_ci /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ 113562306a36Sopenharmony_ci /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ 113662306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 113762306a36Sopenharmony_ci int r = 0; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /*switch (plane->color_mode) {*/ 114262306a36Sopenharmony_ci switch (mode) { 114362306a36Sopenharmony_ci case OMAPFB_COLOR_YUV422: 114462306a36Sopenharmony_ci case OMAPFB_COLOR_YUV420: 114562306a36Sopenharmony_ci case OMAPFB_COLOR_YUY422: 114662306a36Sopenharmony_ci r = -EINVAL; 114762306a36Sopenharmony_ci break; 114862306a36Sopenharmony_ci case OMAPFB_COLOR_CLUT_8BPP: 114962306a36Sopenharmony_ci case OMAPFB_COLOR_CLUT_4BPP: 115062306a36Sopenharmony_ci case OMAPFB_COLOR_CLUT_2BPP: 115162306a36Sopenharmony_ci case OMAPFB_COLOR_CLUT_1BPP: 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci if (fbdev->ctrl->setcolreg) 115462306a36Sopenharmony_ci r = fbdev->ctrl->setcolreg(regno, red, green, blue, 115562306a36Sopenharmony_ci transp, update_hw_pal); 115662306a36Sopenharmony_ci */ 115762306a36Sopenharmony_ci r = -EINVAL; 115862306a36Sopenharmony_ci break; 115962306a36Sopenharmony_ci case OMAPFB_COLOR_RGB565: 116062306a36Sopenharmony_ci case OMAPFB_COLOR_RGB444: 116162306a36Sopenharmony_ci case OMAPFB_COLOR_RGB24P: 116262306a36Sopenharmony_ci case OMAPFB_COLOR_RGB24U: 116362306a36Sopenharmony_ci if (regno < 16) { 116462306a36Sopenharmony_ci u32 pal; 116562306a36Sopenharmony_ci pal = ((red >> (16 - var->red.length)) << 116662306a36Sopenharmony_ci var->red.offset) | 116762306a36Sopenharmony_ci ((green >> (16 - var->green.length)) << 116862306a36Sopenharmony_ci var->green.offset) | 116962306a36Sopenharmony_ci (blue >> (16 - var->blue.length)); 117062306a36Sopenharmony_ci ((u32 *)(fbi->pseudo_palette))[regno] = pal; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci break; 117362306a36Sopenharmony_ci default: 117462306a36Sopenharmony_ci BUG(); 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci return r; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, 118062306a36Sopenharmony_ci u_int transp, struct fb_info *info) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci DBG("setcolreg\n"); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return _setcolreg(info, regno, red, green, blue, transp, 1); 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci int count, index, r; 119062306a36Sopenharmony_ci u16 *red, *green, *blue, *transp; 119162306a36Sopenharmony_ci u16 trans = 0xffff; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci DBG("setcmap\n"); 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci red = cmap->red; 119662306a36Sopenharmony_ci green = cmap->green; 119762306a36Sopenharmony_ci blue = cmap->blue; 119862306a36Sopenharmony_ci transp = cmap->transp; 119962306a36Sopenharmony_ci index = cmap->start; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci for (count = 0; count < cmap->len; count++) { 120262306a36Sopenharmony_ci if (transp) 120362306a36Sopenharmony_ci trans = *transp++; 120462306a36Sopenharmony_ci r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, 120562306a36Sopenharmony_ci count == cmap->len - 1); 120662306a36Sopenharmony_ci if (r != 0) 120762306a36Sopenharmony_ci return r; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci return 0; 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic int omapfb_blank(int blank, struct fb_info *fbi) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 121662306a36Sopenharmony_ci struct omapfb2_device *fbdev = ofbi->fbdev; 121762306a36Sopenharmony_ci struct omap_dss_device *display = fb2display(fbi); 121862306a36Sopenharmony_ci struct omapfb_display_data *d; 121962306a36Sopenharmony_ci int r = 0; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (!display) 122262306a36Sopenharmony_ci return -EINVAL; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci omapfb_lock(fbdev); 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci d = get_display_data(fbdev, display); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci switch (blank) { 122962306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 123062306a36Sopenharmony_ci if (display->state == OMAP_DSS_DISPLAY_ACTIVE) 123162306a36Sopenharmony_ci goto exit; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci r = display->driver->enable(display); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) && 123662306a36Sopenharmony_ci d->update_mode == OMAPFB_AUTO_UPDATE && 123762306a36Sopenharmony_ci !d->auto_update_work_enabled) 123862306a36Sopenharmony_ci omapfb_start_auto_update(fbdev, display); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci case FB_BLANK_NORMAL: 124362306a36Sopenharmony_ci /* FB_BLANK_NORMAL could be implemented. 124462306a36Sopenharmony_ci * Needs DSS additions. */ 124562306a36Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 124662306a36Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 124762306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 124862306a36Sopenharmony_ci if (display->state != OMAP_DSS_DISPLAY_ACTIVE) 124962306a36Sopenharmony_ci goto exit; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (d->auto_update_work_enabled) 125262306a36Sopenharmony_ci omapfb_stop_auto_update(fbdev, display); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci display->driver->disable(display); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci break; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci default: 125962306a36Sopenharmony_ci r = -EINVAL; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ciexit: 126362306a36Sopenharmony_ci omapfb_unlock(fbdev); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci return r; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci#if 0 126962306a36Sopenharmony_ci/* XXX fb_read and fb_write are needed for VRFB */ 127062306a36Sopenharmony_cissize_t omapfb_write(struct fb_info *info, const char __user *buf, 127162306a36Sopenharmony_ci size_t count, loff_t *ppos) 127262306a36Sopenharmony_ci{ 127362306a36Sopenharmony_ci DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); 127462306a36Sopenharmony_ci /* XXX needed for VRFB */ 127562306a36Sopenharmony_ci return count; 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci#endif 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic const struct fb_ops omapfb_ops = { 128062306a36Sopenharmony_ci .owner = THIS_MODULE, 128162306a36Sopenharmony_ci .fb_open = omapfb_open, 128262306a36Sopenharmony_ci .fb_release = omapfb_release, 128362306a36Sopenharmony_ci .fb_fillrect = cfb_fillrect, 128462306a36Sopenharmony_ci .fb_copyarea = cfb_copyarea, 128562306a36Sopenharmony_ci .fb_imageblit = cfb_imageblit, 128662306a36Sopenharmony_ci .fb_blank = omapfb_blank, 128762306a36Sopenharmony_ci .fb_ioctl = omapfb_ioctl, 128862306a36Sopenharmony_ci .fb_check_var = omapfb_check_var, 128962306a36Sopenharmony_ci .fb_set_par = omapfb_set_par, 129062306a36Sopenharmony_ci .fb_pan_display = omapfb_pan_display, 129162306a36Sopenharmony_ci .fb_mmap = omapfb_mmap, 129262306a36Sopenharmony_ci .fb_setcolreg = omapfb_setcolreg, 129362306a36Sopenharmony_ci .fb_setcmap = omapfb_setcmap, 129462306a36Sopenharmony_ci /*.fb_write = omapfb_write,*/ 129562306a36Sopenharmony_ci}; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic void omapfb_free_fbmem(struct fb_info *fbi) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 130062306a36Sopenharmony_ci struct omapfb2_device *fbdev = ofbi->fbdev; 130162306a36Sopenharmony_ci struct omapfb2_mem_region *rg; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci rg = ofbi->region; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci if (rg->token == NULL) 130662306a36Sopenharmony_ci return; 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci WARN_ON(atomic_read(&rg->map_count)); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 131162306a36Sopenharmony_ci /* unmap the 0 angle rotation */ 131262306a36Sopenharmony_ci if (rg->vrfb.vaddr[0]) { 131362306a36Sopenharmony_ci iounmap(rg->vrfb.vaddr[0]); 131462306a36Sopenharmony_ci rg->vrfb.vaddr[0] = NULL; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci omap_vrfb_release_ctx(&rg->vrfb); 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci dma_free_attrs(fbdev->dev, rg->size, rg->token, rg->dma_handle, 132162306a36Sopenharmony_ci rg->attrs); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci rg->token = NULL; 132462306a36Sopenharmony_ci rg->vaddr = NULL; 132562306a36Sopenharmony_ci rg->paddr = 0; 132662306a36Sopenharmony_ci rg->alloc = 0; 132762306a36Sopenharmony_ci rg->size = 0; 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic void clear_fb_info(struct fb_info *fbi) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci memset(&fbi->var, 0, sizeof(fbi->var)); 133362306a36Sopenharmony_ci memset(&fbi->fix, 0, sizeof(fbi->fix)); 133462306a36Sopenharmony_ci strscpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); 133562306a36Sopenharmony_ci} 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_cistatic int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) 133862306a36Sopenharmony_ci{ 133962306a36Sopenharmony_ci int i; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci DBG("free all fbmem\n"); 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 134462306a36Sopenharmony_ci struct fb_info *fbi = fbdev->fbs[i]; 134562306a36Sopenharmony_ci omapfb_free_fbmem(fbi); 134662306a36Sopenharmony_ci clear_fb_info(fbi); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, 135362306a36Sopenharmony_ci unsigned long paddr) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 135662306a36Sopenharmony_ci struct omapfb2_device *fbdev = ofbi->fbdev; 135762306a36Sopenharmony_ci struct omapfb2_mem_region *rg; 135862306a36Sopenharmony_ci void *token; 135962306a36Sopenharmony_ci unsigned long attrs; 136062306a36Sopenharmony_ci dma_addr_t dma_handle; 136162306a36Sopenharmony_ci int r; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci rg = ofbi->region; 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci rg->paddr = 0; 136662306a36Sopenharmony_ci rg->vaddr = NULL; 136762306a36Sopenharmony_ci memset(&rg->vrfb, 0, sizeof rg->vrfb); 136862306a36Sopenharmony_ci rg->size = 0; 136962306a36Sopenharmony_ci rg->type = 0; 137062306a36Sopenharmony_ci rg->alloc = false; 137162306a36Sopenharmony_ci rg->map = false; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci size = PAGE_ALIGN(size); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci attrs = DMA_ATTR_WRITE_COMBINE; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) 137862306a36Sopenharmony_ci attrs |= DMA_ATTR_NO_KERNEL_MAPPING; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci token = dma_alloc_attrs(fbdev->dev, size, &dma_handle, 138362306a36Sopenharmony_ci GFP_KERNEL, attrs); 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci if (token == NULL) { 138662306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to allocate framebuffer\n"); 138762306a36Sopenharmony_ci return -ENOMEM; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci DBG("allocated VRAM paddr %lx, vaddr %p\n", 139162306a36Sopenharmony_ci (unsigned long)dma_handle, token); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 139462306a36Sopenharmony_ci r = omap_vrfb_request_ctx(&rg->vrfb); 139562306a36Sopenharmony_ci if (r) { 139662306a36Sopenharmony_ci dma_free_attrs(fbdev->dev, size, token, dma_handle, 139762306a36Sopenharmony_ci attrs); 139862306a36Sopenharmony_ci dev_err(fbdev->dev, "vrfb create ctx failed\n"); 139962306a36Sopenharmony_ci return r; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci rg->attrs = attrs; 140462306a36Sopenharmony_ci rg->token = token; 140562306a36Sopenharmony_ci rg->dma_handle = dma_handle; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci rg->paddr = (unsigned long)dma_handle; 140862306a36Sopenharmony_ci rg->vaddr = (void __iomem *)token; 140962306a36Sopenharmony_ci rg->size = size; 141062306a36Sopenharmony_ci rg->alloc = 1; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci return 0; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci/* allocate fbmem using display resolution as reference */ 141662306a36Sopenharmony_cistatic int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, 141762306a36Sopenharmony_ci unsigned long paddr) 141862306a36Sopenharmony_ci{ 141962306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 142062306a36Sopenharmony_ci struct omapfb2_device *fbdev = ofbi->fbdev; 142162306a36Sopenharmony_ci struct omap_dss_device *display; 142262306a36Sopenharmony_ci int bytespp; 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci display = fb2display(fbi); 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (!display) 142762306a36Sopenharmony_ci return 0; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci switch (omapfb_get_recommended_bpp(fbdev, display)) { 143062306a36Sopenharmony_ci case 16: 143162306a36Sopenharmony_ci bytespp = 2; 143262306a36Sopenharmony_ci break; 143362306a36Sopenharmony_ci case 24: 143462306a36Sopenharmony_ci bytespp = 4; 143562306a36Sopenharmony_ci break; 143662306a36Sopenharmony_ci default: 143762306a36Sopenharmony_ci bytespp = 4; 143862306a36Sopenharmony_ci break; 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci if (!size) { 144262306a36Sopenharmony_ci u16 w, h; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci display->driver->get_resolution(display, &w, &h); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { 144762306a36Sopenharmony_ci size = max(omap_vrfb_min_phys_size(w, h, bytespp), 144862306a36Sopenharmony_ci omap_vrfb_min_phys_size(h, w, bytespp)); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci DBG("adjusting fb mem size for VRFB, %u -> %lu\n", 145162306a36Sopenharmony_ci w * h * bytespp, size); 145262306a36Sopenharmony_ci } else { 145362306a36Sopenharmony_ci size = w * h * bytespp; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci if (!size) 145862306a36Sopenharmony_ci return 0; 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci return omapfb_alloc_fbmem(fbi, size, paddr); 146162306a36Sopenharmony_ci} 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_cistatic int omapfb_parse_vram_param(const char *param, int max_entries, 146462306a36Sopenharmony_ci unsigned long *sizes, unsigned long *paddrs) 146562306a36Sopenharmony_ci{ 146662306a36Sopenharmony_ci unsigned int fbnum; 146762306a36Sopenharmony_ci unsigned long size; 146862306a36Sopenharmony_ci unsigned long paddr = 0; 146962306a36Sopenharmony_ci char *p, *start; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci start = (char *)param; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci while (1) { 147462306a36Sopenharmony_ci p = start; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci fbnum = simple_strtoul(p, &p, 10); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci if (p == start) 147962306a36Sopenharmony_ci return -EINVAL; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (*p != ':') 148262306a36Sopenharmony_ci return -EINVAL; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci if (fbnum >= max_entries) 148562306a36Sopenharmony_ci return -EINVAL; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci size = memparse(p + 1, &p); 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci if (!size) 149062306a36Sopenharmony_ci return -EINVAL; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci paddr = 0; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci if (*p == '@') { 149562306a36Sopenharmony_ci paddr = simple_strtoul(p + 1, &p, 16); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (!paddr) 149862306a36Sopenharmony_ci return -EINVAL; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci } 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci WARN_ONCE(paddr, 150362306a36Sopenharmony_ci "reserving memory at predefined address not supported\n"); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci paddrs[fbnum] = paddr; 150662306a36Sopenharmony_ci sizes[fbnum] = size; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci if (*p == 0) 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (*p != ',') 151262306a36Sopenharmony_ci return -EINVAL; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci ++p; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci start = p; 151762306a36Sopenharmony_ci } 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci} 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cistatic int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) 152362306a36Sopenharmony_ci{ 152462306a36Sopenharmony_ci int i, r; 152562306a36Sopenharmony_ci unsigned long vram_sizes[10]; 152662306a36Sopenharmony_ci unsigned long vram_paddrs[10]; 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci memset(&vram_sizes, 0, sizeof(vram_sizes)); 152962306a36Sopenharmony_ci memset(&vram_paddrs, 0, sizeof(vram_paddrs)); 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (def_vram && omapfb_parse_vram_param(def_vram, 10, 153262306a36Sopenharmony_ci vram_sizes, vram_paddrs)) { 153362306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to parse vram parameter\n"); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci memset(&vram_sizes, 0, sizeof(vram_sizes)); 153662306a36Sopenharmony_ci memset(&vram_paddrs, 0, sizeof(vram_paddrs)); 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 154062306a36Sopenharmony_ci /* allocate memory automatically only for fb0, or if 154162306a36Sopenharmony_ci * excplicitly defined with vram or plat data option */ 154262306a36Sopenharmony_ci if (i == 0 || vram_sizes[i] != 0) { 154362306a36Sopenharmony_ci r = omapfb_alloc_fbmem_display(fbdev->fbs[i], 154462306a36Sopenharmony_ci vram_sizes[i], vram_paddrs[i]); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci if (r) 154762306a36Sopenharmony_ci return r; 154862306a36Sopenharmony_ci } 154962306a36Sopenharmony_ci } 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 155262306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); 155362306a36Sopenharmony_ci struct omapfb2_mem_region *rg; 155462306a36Sopenharmony_ci rg = ofbi->region; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci DBG("region%d phys %08x virt %p size=%lu\n", 155762306a36Sopenharmony_ci i, 155862306a36Sopenharmony_ci rg->paddr, 155962306a36Sopenharmony_ci rg->vaddr, 156062306a36Sopenharmony_ci rg->size); 156162306a36Sopenharmony_ci } 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci return 0; 156462306a36Sopenharmony_ci} 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_cistatic void omapfb_clear_fb(struct fb_info *fbi) 156762306a36Sopenharmony_ci{ 156862306a36Sopenharmony_ci const struct fb_fillrect rect = { 156962306a36Sopenharmony_ci .dx = 0, 157062306a36Sopenharmony_ci .dy = 0, 157162306a36Sopenharmony_ci .width = fbi->var.xres_virtual, 157262306a36Sopenharmony_ci .height = fbi->var.yres_virtual, 157362306a36Sopenharmony_ci .color = 0, 157462306a36Sopenharmony_ci .rop = ROP_COPY, 157562306a36Sopenharmony_ci }; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci cfb_fillrect(fbi, &rect); 157862306a36Sopenharmony_ci} 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ciint omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) 158162306a36Sopenharmony_ci{ 158262306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 158362306a36Sopenharmony_ci struct omapfb2_device *fbdev = ofbi->fbdev; 158462306a36Sopenharmony_ci struct omapfb2_mem_region *rg = ofbi->region; 158562306a36Sopenharmony_ci unsigned long old_size = rg->size; 158662306a36Sopenharmony_ci unsigned long old_paddr = rg->paddr; 158762306a36Sopenharmony_ci int old_type = rg->type; 158862306a36Sopenharmony_ci int r; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (type != OMAPFB_MEMTYPE_SDRAM) 159162306a36Sopenharmony_ci return -EINVAL; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci size = PAGE_ALIGN(size); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci if (old_size == size && old_type == type) 159662306a36Sopenharmony_ci return 0; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci omapfb_free_fbmem(fbi); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci if (size == 0) { 160162306a36Sopenharmony_ci clear_fb_info(fbi); 160262306a36Sopenharmony_ci return 0; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci r = omapfb_alloc_fbmem(fbi, size, 0); 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci if (r) { 160862306a36Sopenharmony_ci if (old_size) 160962306a36Sopenharmony_ci omapfb_alloc_fbmem(fbi, old_size, old_paddr); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (rg->size == 0) 161262306a36Sopenharmony_ci clear_fb_info(fbi); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci return r; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci if (old_size == size) 161862306a36Sopenharmony_ci return 0; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci if (old_size == 0) { 162162306a36Sopenharmony_ci DBG("initializing fb %d\n", ofbi->id); 162262306a36Sopenharmony_ci r = omapfb_fb_init(fbdev, fbi); 162362306a36Sopenharmony_ci if (r) { 162462306a36Sopenharmony_ci DBG("omapfb_fb_init failed\n"); 162562306a36Sopenharmony_ci goto err; 162662306a36Sopenharmony_ci } 162762306a36Sopenharmony_ci r = omapfb_apply_changes(fbi, 1); 162862306a36Sopenharmony_ci if (r) { 162962306a36Sopenharmony_ci DBG("omapfb_apply_changes failed\n"); 163062306a36Sopenharmony_ci goto err; 163162306a36Sopenharmony_ci } 163262306a36Sopenharmony_ci } else { 163362306a36Sopenharmony_ci struct fb_var_screeninfo new_var; 163462306a36Sopenharmony_ci memcpy(&new_var, &fbi->var, sizeof(new_var)); 163562306a36Sopenharmony_ci r = check_fb_var(fbi, &new_var); 163662306a36Sopenharmony_ci if (r) 163762306a36Sopenharmony_ci goto err; 163862306a36Sopenharmony_ci memcpy(&fbi->var, &new_var, sizeof(fbi->var)); 163962306a36Sopenharmony_ci set_fb_fix(fbi); 164062306a36Sopenharmony_ci r = setup_vrfb_rotation(fbi); 164162306a36Sopenharmony_ci if (r) 164262306a36Sopenharmony_ci goto err; 164362306a36Sopenharmony_ci } 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci omapfb_clear_fb(fbi); 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci return 0; 164862306a36Sopenharmony_cierr: 164962306a36Sopenharmony_ci omapfb_free_fbmem(fbi); 165062306a36Sopenharmony_ci clear_fb_info(fbi); 165162306a36Sopenharmony_ci return r; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic void omapfb_auto_update_work(struct work_struct *work) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci struct omap_dss_device *dssdev; 165762306a36Sopenharmony_ci struct omap_dss_driver *dssdrv; 165862306a36Sopenharmony_ci struct omapfb_display_data *d; 165962306a36Sopenharmony_ci u16 w, h; 166062306a36Sopenharmony_ci unsigned int freq; 166162306a36Sopenharmony_ci struct omapfb2_device *fbdev; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci d = container_of(work, struct omapfb_display_data, 166462306a36Sopenharmony_ci auto_update_work.work); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci dssdev = d->dssdev; 166762306a36Sopenharmony_ci dssdrv = dssdev->driver; 166862306a36Sopenharmony_ci fbdev = d->fbdev; 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci if (!dssdrv || !dssdrv->update) 167162306a36Sopenharmony_ci return; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci if (dssdrv->sync) 167462306a36Sopenharmony_ci dssdrv->sync(dssdev); 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_ci dssdrv->get_resolution(dssdev, &w, &h); 167762306a36Sopenharmony_ci dssdrv->update(dssdev, 0, 0, w, h); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci freq = auto_update_freq; 168062306a36Sopenharmony_ci if (freq == 0) 168162306a36Sopenharmony_ci freq = 20; 168262306a36Sopenharmony_ci queue_delayed_work(fbdev->auto_update_wq, 168362306a36Sopenharmony_ci &d->auto_update_work, HZ / freq); 168462306a36Sopenharmony_ci} 168562306a36Sopenharmony_ci 168662306a36Sopenharmony_civoid omapfb_start_auto_update(struct omapfb2_device *fbdev, 168762306a36Sopenharmony_ci struct omap_dss_device *display) 168862306a36Sopenharmony_ci{ 168962306a36Sopenharmony_ci struct omapfb_display_data *d; 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci if (fbdev->auto_update_wq == NULL) { 169262306a36Sopenharmony_ci struct workqueue_struct *wq; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci wq = create_singlethread_workqueue("omapfb_auto_update"); 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (wq == NULL) { 169762306a36Sopenharmony_ci dev_err(fbdev->dev, "Failed to create workqueue for " 169862306a36Sopenharmony_ci "auto-update\n"); 169962306a36Sopenharmony_ci return; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci fbdev->auto_update_wq = wq; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci d = get_display_data(fbdev, display); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci d->auto_update_work_enabled = true; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci omapfb_auto_update_work(&d->auto_update_work.work); 171262306a36Sopenharmony_ci} 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_civoid omapfb_stop_auto_update(struct omapfb2_device *fbdev, 171562306a36Sopenharmony_ci struct omap_dss_device *display) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci struct omapfb_display_data *d; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci d = get_display_data(fbdev, display); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci cancel_delayed_work_sync(&d->auto_update_work); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci d->auto_update_work_enabled = false; 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci/* initialize fb_info, var, fix to something sane based on the display */ 172762306a36Sopenharmony_cistatic int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) 172862306a36Sopenharmony_ci{ 172962306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->var; 173062306a36Sopenharmony_ci struct omap_dss_device *display = fb2display(fbi); 173162306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 173262306a36Sopenharmony_ci int r = 0; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci fbi->fbops = &omapfb_ops; 173562306a36Sopenharmony_ci fbi->pseudo_palette = fbdev->pseudo_palette; 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci if (ofbi->region->size == 0) { 173862306a36Sopenharmony_ci clear_fb_info(fbi); 173962306a36Sopenharmony_ci return 0; 174062306a36Sopenharmony_ci } 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci var->nonstd = 0; 174362306a36Sopenharmony_ci var->bits_per_pixel = 0; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci var->rotate = def_rotate; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (display) { 174862306a36Sopenharmony_ci u16 w, h; 174962306a36Sopenharmony_ci int rotation = (var->rotate + ofbi->rotation[0]) % 4; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci display->driver->get_resolution(display, &w, &h); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (rotation == FB_ROTATE_CW || 175462306a36Sopenharmony_ci rotation == FB_ROTATE_CCW) { 175562306a36Sopenharmony_ci var->xres = h; 175662306a36Sopenharmony_ci var->yres = w; 175762306a36Sopenharmony_ci } else { 175862306a36Sopenharmony_ci var->xres = w; 175962306a36Sopenharmony_ci var->yres = h; 176062306a36Sopenharmony_ci } 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci var->xres_virtual = var->xres; 176362306a36Sopenharmony_ci var->yres_virtual = var->yres; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci if (!var->bits_per_pixel) { 176662306a36Sopenharmony_ci switch (omapfb_get_recommended_bpp(fbdev, display)) { 176762306a36Sopenharmony_ci case 16: 176862306a36Sopenharmony_ci var->bits_per_pixel = 16; 176962306a36Sopenharmony_ci break; 177062306a36Sopenharmony_ci case 24: 177162306a36Sopenharmony_ci var->bits_per_pixel = 32; 177262306a36Sopenharmony_ci break; 177362306a36Sopenharmony_ci default: 177462306a36Sopenharmony_ci dev_err(fbdev->dev, "illegal display " 177562306a36Sopenharmony_ci "bpp\n"); 177662306a36Sopenharmony_ci return -EINVAL; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci } 177962306a36Sopenharmony_ci } else { 178062306a36Sopenharmony_ci /* if there's no display, let's just guess some basic values */ 178162306a36Sopenharmony_ci var->xres = 320; 178262306a36Sopenharmony_ci var->yres = 240; 178362306a36Sopenharmony_ci var->xres_virtual = var->xres; 178462306a36Sopenharmony_ci var->yres_virtual = var->yres; 178562306a36Sopenharmony_ci if (!var->bits_per_pixel) 178662306a36Sopenharmony_ci var->bits_per_pixel = 16; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci r = check_fb_var(fbi, var); 179062306a36Sopenharmony_ci if (r) 179162306a36Sopenharmony_ci goto err; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci set_fb_fix(fbi); 179462306a36Sopenharmony_ci r = setup_vrfb_rotation(fbi); 179562306a36Sopenharmony_ci if (r) 179662306a36Sopenharmony_ci goto err; 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci r = fb_alloc_cmap(&fbi->cmap, 256, 0); 179962306a36Sopenharmony_ci if (r) 180062306a36Sopenharmony_ci dev_err(fbdev->dev, "unable to allocate color map memory\n"); 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cierr: 180362306a36Sopenharmony_ci return r; 180462306a36Sopenharmony_ci} 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_cistatic void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) 180762306a36Sopenharmony_ci{ 180862306a36Sopenharmony_ci fb_dealloc_cmap(&fbi->cmap); 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_cistatic void omapfb_free_resources(struct omapfb2_device *fbdev) 181362306a36Sopenharmony_ci{ 181462306a36Sopenharmony_ci int i; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci DBG("free_resources\n"); 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci if (fbdev == NULL) 181962306a36Sopenharmony_ci return; 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci for (i = 0; i < fbdev->num_overlays; i++) { 182262306a36Sopenharmony_ci struct omap_overlay *ovl = fbdev->overlays[i]; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci ovl->disable(ovl); 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci if (ovl->manager) 182762306a36Sopenharmony_ci ovl->unset_manager(ovl); 182862306a36Sopenharmony_ci } 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) 183162306a36Sopenharmony_ci unregister_framebuffer(fbdev->fbs[i]); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* free the reserved fbmem */ 183462306a36Sopenharmony_ci omapfb_free_all_fbmem(fbdev); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 183762306a36Sopenharmony_ci fbinfo_cleanup(fbdev, fbdev->fbs[i]); 183862306a36Sopenharmony_ci framebuffer_release(fbdev->fbs[i]); 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci for (i = 0; i < fbdev->num_displays; i++) { 184262306a36Sopenharmony_ci struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci if (fbdev->displays[i].auto_update_work_enabled) 184562306a36Sopenharmony_ci omapfb_stop_auto_update(fbdev, dssdev); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) 184862306a36Sopenharmony_ci dssdev->driver->disable(dssdev); 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci dssdev->driver->disconnect(dssdev); 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci omap_dss_put_device(dssdev); 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (fbdev->auto_update_wq != NULL) { 185662306a36Sopenharmony_ci destroy_workqueue(fbdev->auto_update_wq); 185762306a36Sopenharmony_ci fbdev->auto_update_wq = NULL; 185862306a36Sopenharmony_ci } 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci dev_set_drvdata(fbdev->dev, NULL); 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic int omapfb_create_framebuffers(struct omapfb2_device *fbdev) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci int r, i; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci fbdev->num_fbs = 0; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* allocate fb_infos */ 187262306a36Sopenharmony_ci for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { 187362306a36Sopenharmony_ci struct fb_info *fbi; 187462306a36Sopenharmony_ci struct omapfb_info *ofbi; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci fbi = framebuffer_alloc(sizeof(struct omapfb_info), 187762306a36Sopenharmony_ci fbdev->dev); 187862306a36Sopenharmony_ci if (!fbi) 187962306a36Sopenharmony_ci return -ENOMEM; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci clear_fb_info(fbi); 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci fbdev->fbs[i] = fbi; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci ofbi = FB2OFB(fbi); 188662306a36Sopenharmony_ci ofbi->fbdev = fbdev; 188762306a36Sopenharmony_ci ofbi->id = i; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci ofbi->region = &fbdev->regions[i]; 189062306a36Sopenharmony_ci ofbi->region->id = i; 189162306a36Sopenharmony_ci init_rwsem(&ofbi->region->lock); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci /* assign these early, so that fb alloc can use them */ 189462306a36Sopenharmony_ci ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : 189562306a36Sopenharmony_ci OMAP_DSS_ROT_DMA; 189662306a36Sopenharmony_ci ofbi->mirror = def_mirror; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci fbdev->num_fbs++; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci DBG("fb_infos allocated\n"); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci /* assign overlays for the fbs */ 190462306a36Sopenharmony_ci for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { 190562306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci ofbi->overlays[0] = fbdev->overlays[i]; 190862306a36Sopenharmony_ci ofbi->num_overlays = 1; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci /* allocate fb memories */ 191262306a36Sopenharmony_ci r = omapfb_allocate_all_fbs(fbdev); 191362306a36Sopenharmony_ci if (r) { 191462306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to allocate fbmem\n"); 191562306a36Sopenharmony_ci return r; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci DBG("fbmems allocated\n"); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci /* setup fb_infos */ 192162306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 192262306a36Sopenharmony_ci struct fb_info *fbi = fbdev->fbs[i]; 192362306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci omapfb_get_mem_region(ofbi->region); 192662306a36Sopenharmony_ci r = omapfb_fb_init(fbdev, fbi); 192762306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci if (r) { 193062306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to setup fb_info\n"); 193162306a36Sopenharmony_ci return r; 193262306a36Sopenharmony_ci } 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 193662306a36Sopenharmony_ci struct fb_info *fbi = fbdev->fbs[i]; 193762306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (ofbi->region->size == 0) 194062306a36Sopenharmony_ci continue; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci omapfb_clear_fb(fbi); 194362306a36Sopenharmony_ci } 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci DBG("fb_infos initialized\n"); 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 194862306a36Sopenharmony_ci r = register_framebuffer(fbdev->fbs[i]); 194962306a36Sopenharmony_ci if (r != 0) { 195062306a36Sopenharmony_ci dev_err(fbdev->dev, 195162306a36Sopenharmony_ci "registering framebuffer %d failed\n", i); 195262306a36Sopenharmony_ci return r; 195362306a36Sopenharmony_ci } 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci DBG("framebuffers registered\n"); 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci for (i = 0; i < fbdev->num_fbs; i++) { 195962306a36Sopenharmony_ci struct fb_info *fbi = fbdev->fbs[i]; 196062306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbi); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci omapfb_get_mem_region(ofbi->region); 196362306a36Sopenharmony_ci r = omapfb_apply_changes(fbi, 1); 196462306a36Sopenharmony_ci omapfb_put_mem_region(ofbi->region); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci if (r) { 196762306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to change mode\n"); 196862306a36Sopenharmony_ci return r; 196962306a36Sopenharmony_ci } 197062306a36Sopenharmony_ci } 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci /* Enable fb0 */ 197362306a36Sopenharmony_ci if (fbdev->num_fbs > 0) { 197462306a36Sopenharmony_ci struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci if (ofbi->num_overlays > 0) { 197762306a36Sopenharmony_ci struct omap_overlay *ovl = ofbi->overlays[0]; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci ovl->manager->apply(ovl->manager); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci r = omapfb_overlay_enable(ovl, 1); 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci if (r) { 198462306a36Sopenharmony_ci dev_err(fbdev->dev, 198562306a36Sopenharmony_ci "failed to enable overlay\n"); 198662306a36Sopenharmony_ci return r; 198762306a36Sopenharmony_ci } 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci } 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci DBG("create_framebuffers done\n"); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci return 0; 199462306a36Sopenharmony_ci} 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_cistatic int omapfb_mode_to_timings(const char *mode_str, 199762306a36Sopenharmony_ci struct omap_dss_device *display, 199862306a36Sopenharmony_ci struct omap_video_timings *timings, u8 *bpp) 199962306a36Sopenharmony_ci{ 200062306a36Sopenharmony_ci struct fb_info *fbi; 200162306a36Sopenharmony_ci struct fb_var_screeninfo *var; 200262306a36Sopenharmony_ci struct fb_ops *fbops; 200362306a36Sopenharmony_ci int r; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci#ifdef CONFIG_OMAP2_DSS_VENC 200662306a36Sopenharmony_ci if (strcmp(mode_str, "pal") == 0) { 200762306a36Sopenharmony_ci *timings = omap_dss_pal_timings; 200862306a36Sopenharmony_ci *bpp = 24; 200962306a36Sopenharmony_ci return 0; 201062306a36Sopenharmony_ci } else if (strcmp(mode_str, "ntsc") == 0) { 201162306a36Sopenharmony_ci *timings = omap_dss_ntsc_timings; 201262306a36Sopenharmony_ci *bpp = 24; 201362306a36Sopenharmony_ci return 0; 201462306a36Sopenharmony_ci } 201562306a36Sopenharmony_ci#endif 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* this is quite a hack, but I wanted to use the modedb and for 201862306a36Sopenharmony_ci * that we need fb_info and var, so we create dummy ones */ 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci *bpp = 0; 202162306a36Sopenharmony_ci fbi = NULL; 202262306a36Sopenharmony_ci var = NULL; 202362306a36Sopenharmony_ci fbops = NULL; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci fbi = kzalloc(sizeof(*fbi), GFP_KERNEL); 202662306a36Sopenharmony_ci if (fbi == NULL) { 202762306a36Sopenharmony_ci r = -ENOMEM; 202862306a36Sopenharmony_ci goto err; 202962306a36Sopenharmony_ci } 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci var = kzalloc(sizeof(*var), GFP_KERNEL); 203262306a36Sopenharmony_ci if (var == NULL) { 203362306a36Sopenharmony_ci r = -ENOMEM; 203462306a36Sopenharmony_ci goto err; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); 203862306a36Sopenharmony_ci if (fbops == NULL) { 203962306a36Sopenharmony_ci r = -ENOMEM; 204062306a36Sopenharmony_ci goto err; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci fbi->fbops = fbops; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci r = fb_find_mode(var, fbi, mode_str, NULL, 0, NULL, 24); 204662306a36Sopenharmony_ci if (r == 0) { 204762306a36Sopenharmony_ci r = -EINVAL; 204862306a36Sopenharmony_ci goto err; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci if (display->driver->get_timings) { 205262306a36Sopenharmony_ci display->driver->get_timings(display, timings); 205362306a36Sopenharmony_ci } else { 205462306a36Sopenharmony_ci timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 205562306a36Sopenharmony_ci timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; 205662306a36Sopenharmony_ci timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; 205762306a36Sopenharmony_ci } 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci timings->pixelclock = PICOS2KHZ(var->pixclock) * 1000; 206062306a36Sopenharmony_ci timings->hbp = var->left_margin; 206162306a36Sopenharmony_ci timings->hfp = var->right_margin; 206262306a36Sopenharmony_ci timings->vbp = var->upper_margin; 206362306a36Sopenharmony_ci timings->vfp = var->lower_margin; 206462306a36Sopenharmony_ci timings->hsw = var->hsync_len; 206562306a36Sopenharmony_ci timings->vsw = var->vsync_len; 206662306a36Sopenharmony_ci timings->x_res = var->xres; 206762306a36Sopenharmony_ci timings->y_res = var->yres; 206862306a36Sopenharmony_ci timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ? 206962306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_HIGH : 207062306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_LOW; 207162306a36Sopenharmony_ci timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ? 207262306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_HIGH : 207362306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_LOW; 207462306a36Sopenharmony_ci timings->interlace = var->vmode & FB_VMODE_INTERLACED; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci switch (var->bits_per_pixel) { 207762306a36Sopenharmony_ci case 16: 207862306a36Sopenharmony_ci *bpp = 16; 207962306a36Sopenharmony_ci break; 208062306a36Sopenharmony_ci case 24: 208162306a36Sopenharmony_ci case 32: 208262306a36Sopenharmony_ci default: 208362306a36Sopenharmony_ci *bpp = 24; 208462306a36Sopenharmony_ci break; 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci r = 0; 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_cierr: 209062306a36Sopenharmony_ci kfree(fbi); 209162306a36Sopenharmony_ci kfree(var); 209262306a36Sopenharmony_ci kfree(fbops); 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_ci return r; 209562306a36Sopenharmony_ci} 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_cistatic int omapfb_set_def_mode(struct omapfb2_device *fbdev, 209862306a36Sopenharmony_ci struct omap_dss_device *display, char *mode_str) 209962306a36Sopenharmony_ci{ 210062306a36Sopenharmony_ci int r; 210162306a36Sopenharmony_ci u8 bpp; 210262306a36Sopenharmony_ci struct omap_video_timings timings, temp_timings; 210362306a36Sopenharmony_ci struct omapfb_display_data *d; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp); 210662306a36Sopenharmony_ci if (r) 210762306a36Sopenharmony_ci return r; 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_ci d = get_display_data(fbdev, display); 211062306a36Sopenharmony_ci d->bpp_override = bpp; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci if (display->driver->check_timings) { 211362306a36Sopenharmony_ci r = display->driver->check_timings(display, &timings); 211462306a36Sopenharmony_ci if (r) 211562306a36Sopenharmony_ci return r; 211662306a36Sopenharmony_ci } else { 211762306a36Sopenharmony_ci /* If check_timings is not present compare xres and yres */ 211862306a36Sopenharmony_ci if (display->driver->get_timings) { 211962306a36Sopenharmony_ci display->driver->get_timings(display, &temp_timings); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci if (temp_timings.x_res != timings.x_res || 212262306a36Sopenharmony_ci temp_timings.y_res != timings.y_res) 212362306a36Sopenharmony_ci return -EINVAL; 212462306a36Sopenharmony_ci } 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci 212762306a36Sopenharmony_ci if (display->driver->set_timings) 212862306a36Sopenharmony_ci display->driver->set_timings(display, &timings); 212962306a36Sopenharmony_ci 213062306a36Sopenharmony_ci return 0; 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_cistatic int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, 213462306a36Sopenharmony_ci struct omap_dss_device *dssdev) 213562306a36Sopenharmony_ci{ 213662306a36Sopenharmony_ci struct omapfb_display_data *d; 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_ci BUG_ON(dssdev->driver->get_recommended_bpp == NULL); 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci d = get_display_data(fbdev, dssdev); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci if (d->bpp_override != 0) 214362306a36Sopenharmony_ci return d->bpp_override; 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci return dssdev->driver->get_recommended_bpp(dssdev); 214662306a36Sopenharmony_ci} 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_cistatic int omapfb_parse_def_modes(struct omapfb2_device *fbdev) 214962306a36Sopenharmony_ci{ 215062306a36Sopenharmony_ci char *str, *options, *this_opt; 215162306a36Sopenharmony_ci int r = 0; 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_ci str = kstrdup(def_mode, GFP_KERNEL); 215462306a36Sopenharmony_ci if (!str) 215562306a36Sopenharmony_ci return -ENOMEM; 215662306a36Sopenharmony_ci options = str; 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci while (!r && (this_opt = strsep(&options, ",")) != NULL) { 215962306a36Sopenharmony_ci char *p, *display_str, *mode_str; 216062306a36Sopenharmony_ci struct omap_dss_device *display; 216162306a36Sopenharmony_ci int i; 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci p = strchr(this_opt, ':'); 216462306a36Sopenharmony_ci if (!p) { 216562306a36Sopenharmony_ci r = -EINVAL; 216662306a36Sopenharmony_ci break; 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci *p = 0; 217062306a36Sopenharmony_ci display_str = this_opt; 217162306a36Sopenharmony_ci mode_str = p + 1; 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci display = NULL; 217462306a36Sopenharmony_ci for (i = 0; i < fbdev->num_displays; ++i) { 217562306a36Sopenharmony_ci if (strcmp(fbdev->displays[i].dssdev->name, 217662306a36Sopenharmony_ci display_str) == 0) { 217762306a36Sopenharmony_ci display = fbdev->displays[i].dssdev; 217862306a36Sopenharmony_ci break; 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci } 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci if (!display) { 218362306a36Sopenharmony_ci r = -EINVAL; 218462306a36Sopenharmony_ci break; 218562306a36Sopenharmony_ci } 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci r = omapfb_set_def_mode(fbdev, display, mode_str); 218862306a36Sopenharmony_ci if (r) 218962306a36Sopenharmony_ci break; 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci kfree(str); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci return r; 219562306a36Sopenharmony_ci} 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_cistatic void fb_videomode_to_omap_timings(struct fb_videomode *m, 219862306a36Sopenharmony_ci struct omap_dss_device *display, 219962306a36Sopenharmony_ci struct omap_video_timings *t) 220062306a36Sopenharmony_ci{ 220162306a36Sopenharmony_ci if (display->driver->get_timings) { 220262306a36Sopenharmony_ci display->driver->get_timings(display, t); 220362306a36Sopenharmony_ci } else { 220462306a36Sopenharmony_ci t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; 220562306a36Sopenharmony_ci t->de_level = OMAPDSS_SIG_ACTIVE_HIGH; 220662306a36Sopenharmony_ci t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci t->x_res = m->xres; 221062306a36Sopenharmony_ci t->y_res = m->yres; 221162306a36Sopenharmony_ci t->pixelclock = PICOS2KHZ(m->pixclock) * 1000; 221262306a36Sopenharmony_ci t->hsw = m->hsync_len; 221362306a36Sopenharmony_ci t->hfp = m->right_margin; 221462306a36Sopenharmony_ci t->hbp = m->left_margin; 221562306a36Sopenharmony_ci t->vsw = m->vsync_len; 221662306a36Sopenharmony_ci t->vfp = m->lower_margin; 221762306a36Sopenharmony_ci t->vbp = m->upper_margin; 221862306a36Sopenharmony_ci t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ? 221962306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_HIGH : 222062306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_LOW; 222162306a36Sopenharmony_ci t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ? 222262306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_HIGH : 222362306a36Sopenharmony_ci OMAPDSS_SIG_ACTIVE_LOW; 222462306a36Sopenharmony_ci t->interlace = m->vmode & FB_VMODE_INTERLACED; 222562306a36Sopenharmony_ci} 222662306a36Sopenharmony_ci 222762306a36Sopenharmony_cistatic int omapfb_find_best_mode(struct omap_dss_device *display, 222862306a36Sopenharmony_ci struct omap_video_timings *timings) 222962306a36Sopenharmony_ci{ 223062306a36Sopenharmony_ci struct fb_monspecs *specs; 223162306a36Sopenharmony_ci u8 *edid; 223262306a36Sopenharmony_ci int r, i, best_idx, len; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci if (!display->driver->read_edid) 223562306a36Sopenharmony_ci return -ENODEV; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci len = 0x80 * 2; 223862306a36Sopenharmony_ci edid = kmalloc(len, GFP_KERNEL); 223962306a36Sopenharmony_ci if (edid == NULL) 224062306a36Sopenharmony_ci return -ENOMEM; 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci r = display->driver->read_edid(display, edid, len); 224362306a36Sopenharmony_ci if (r < 0) 224462306a36Sopenharmony_ci goto err1; 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci specs = kzalloc(sizeof(*specs), GFP_KERNEL); 224762306a36Sopenharmony_ci if (specs == NULL) { 224862306a36Sopenharmony_ci r = -ENOMEM; 224962306a36Sopenharmony_ci goto err1; 225062306a36Sopenharmony_ci } 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci fb_edid_to_monspecs(edid, specs); 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci best_idx = -1; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci for (i = 0; i < specs->modedb_len; ++i) { 225762306a36Sopenharmony_ci struct fb_videomode *m; 225862306a36Sopenharmony_ci struct omap_video_timings t; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci m = &specs->modedb[i]; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci if (m->pixclock == 0) 226362306a36Sopenharmony_ci continue; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* skip repeated pixel modes */ 226662306a36Sopenharmony_ci if (m->xres == 2880 || m->xres == 1440) 226762306a36Sopenharmony_ci continue; 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci if (m->vmode & FB_VMODE_INTERLACED || 227062306a36Sopenharmony_ci m->vmode & FB_VMODE_DOUBLE) 227162306a36Sopenharmony_ci continue; 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci fb_videomode_to_omap_timings(m, display, &t); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci r = display->driver->check_timings(display, &t); 227662306a36Sopenharmony_ci if (r == 0) { 227762306a36Sopenharmony_ci best_idx = i; 227862306a36Sopenharmony_ci break; 227962306a36Sopenharmony_ci } 228062306a36Sopenharmony_ci } 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (best_idx == -1) { 228362306a36Sopenharmony_ci r = -ENOENT; 228462306a36Sopenharmony_ci goto err2; 228562306a36Sopenharmony_ci } 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci fb_videomode_to_omap_timings(&specs->modedb[best_idx], display, 228862306a36Sopenharmony_ci timings); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci r = 0; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_cierr2: 229362306a36Sopenharmony_ci fb_destroy_modedb(specs->modedb); 229462306a36Sopenharmony_ci kfree(specs); 229562306a36Sopenharmony_cierr1: 229662306a36Sopenharmony_ci kfree(edid); 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci return r; 229962306a36Sopenharmony_ci} 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_cistatic int omapfb_init_display(struct omapfb2_device *fbdev, 230262306a36Sopenharmony_ci struct omap_dss_device *dssdev) 230362306a36Sopenharmony_ci{ 230462306a36Sopenharmony_ci struct omap_dss_driver *dssdrv = dssdev->driver; 230562306a36Sopenharmony_ci struct omapfb_display_data *d; 230662306a36Sopenharmony_ci int r; 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci r = dssdrv->enable(dssdev); 230962306a36Sopenharmony_ci if (r) { 231062306a36Sopenharmony_ci dev_warn(fbdev->dev, "Failed to enable display '%s'\n", 231162306a36Sopenharmony_ci dssdev->name); 231262306a36Sopenharmony_ci return r; 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci d = get_display_data(fbdev, dssdev); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci d->fbdev = fbdev; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { 232062306a36Sopenharmony_ci u16 w, h; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci if (auto_update) { 232362306a36Sopenharmony_ci omapfb_start_auto_update(fbdev, dssdev); 232462306a36Sopenharmony_ci d->update_mode = OMAPFB_AUTO_UPDATE; 232562306a36Sopenharmony_ci } else { 232662306a36Sopenharmony_ci d->update_mode = OMAPFB_MANUAL_UPDATE; 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (dssdrv->enable_te) { 233062306a36Sopenharmony_ci r = dssdrv->enable_te(dssdev, 1); 233162306a36Sopenharmony_ci if (r) { 233262306a36Sopenharmony_ci dev_err(fbdev->dev, "Failed to set TE\n"); 233362306a36Sopenharmony_ci return r; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci } 233662306a36Sopenharmony_ci 233762306a36Sopenharmony_ci dssdrv->get_resolution(dssdev, &w, &h); 233862306a36Sopenharmony_ci r = dssdrv->update(dssdev, 0, 0, w, h); 233962306a36Sopenharmony_ci if (r) { 234062306a36Sopenharmony_ci dev_err(fbdev->dev, 234162306a36Sopenharmony_ci "Failed to update display\n"); 234262306a36Sopenharmony_ci return r; 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci } else { 234562306a36Sopenharmony_ci d->update_mode = OMAPFB_AUTO_UPDATE; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci return 0; 234962306a36Sopenharmony_ci} 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_cistatic int omapfb_init_connections(struct omapfb2_device *fbdev, 235262306a36Sopenharmony_ci struct omap_dss_device *def_dssdev) 235362306a36Sopenharmony_ci{ 235462306a36Sopenharmony_ci int i, r; 235562306a36Sopenharmony_ci struct omap_overlay_manager *mgr; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci r = def_dssdev->driver->connect(def_dssdev); 235862306a36Sopenharmony_ci if (r) { 235962306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to connect default display\n"); 236062306a36Sopenharmony_ci return r; 236162306a36Sopenharmony_ci } 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci for (i = 0; i < fbdev->num_displays; ++i) { 236462306a36Sopenharmony_ci struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (dssdev == def_dssdev) 236762306a36Sopenharmony_ci continue; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci /* 237062306a36Sopenharmony_ci * We don't care if the connect succeeds or not. We just want to 237162306a36Sopenharmony_ci * connect as many displays as possible. 237262306a36Sopenharmony_ci */ 237362306a36Sopenharmony_ci dssdev->driver->connect(dssdev); 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci mgr = omapdss_find_mgr_from_display(def_dssdev); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci if (!mgr) { 237962306a36Sopenharmony_ci dev_err(fbdev->dev, "no ovl manager for the default display\n"); 238062306a36Sopenharmony_ci return -EINVAL; 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci for (i = 0; i < fbdev->num_overlays; i++) { 238462306a36Sopenharmony_ci struct omap_overlay *ovl = fbdev->overlays[i]; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (ovl->manager) 238762306a36Sopenharmony_ci ovl->unset_manager(ovl); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci r = ovl->set_manager(ovl, mgr); 239062306a36Sopenharmony_ci if (r) 239162306a36Sopenharmony_ci dev_warn(fbdev->dev, 239262306a36Sopenharmony_ci "failed to connect overlay %s to manager %s\n", 239362306a36Sopenharmony_ci ovl->name, mgr->name); 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci return 0; 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_cistatic struct omap_dss_device * 240062306a36Sopenharmony_ciomapfb_find_default_display(struct omapfb2_device *fbdev) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci const char *def_name; 240362306a36Sopenharmony_ci int i; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci /* 240662306a36Sopenharmony_ci * Search with the display name from the user or the board file, 240762306a36Sopenharmony_ci * comparing to display names and aliases 240862306a36Sopenharmony_ci */ 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci def_name = omapdss_get_default_display_name(); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci if (def_name) { 241362306a36Sopenharmony_ci for (i = 0; i < fbdev->num_displays; ++i) { 241462306a36Sopenharmony_ci struct omap_dss_device *dssdev; 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci dssdev = fbdev->displays[i].dssdev; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci if (dssdev->name && strcmp(def_name, dssdev->name) == 0) 241962306a36Sopenharmony_ci return dssdev; 242062306a36Sopenharmony_ci 242162306a36Sopenharmony_ci if (strcmp(def_name, dssdev->alias) == 0) 242262306a36Sopenharmony_ci return dssdev; 242362306a36Sopenharmony_ci } 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci /* def_name given but not found */ 242662306a36Sopenharmony_ci return NULL; 242762306a36Sopenharmony_ci } 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci /* then look for DT alias display0 */ 243062306a36Sopenharmony_ci for (i = 0; i < fbdev->num_displays; ++i) { 243162306a36Sopenharmony_ci struct omap_dss_device *dssdev; 243262306a36Sopenharmony_ci int id; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci dssdev = fbdev->displays[i].dssdev; 243562306a36Sopenharmony_ci 243662306a36Sopenharmony_ci if (dssdev->dev->of_node == NULL) 243762306a36Sopenharmony_ci continue; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci id = of_alias_get_id(dssdev->dev->of_node, "display"); 244062306a36Sopenharmony_ci if (id == 0) 244162306a36Sopenharmony_ci return dssdev; 244262306a36Sopenharmony_ci } 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_ci /* return the first display we have in the list */ 244562306a36Sopenharmony_ci return fbdev->displays[0].dssdev; 244662306a36Sopenharmony_ci} 244762306a36Sopenharmony_ci 244862306a36Sopenharmony_cistatic int omapfb_probe(struct platform_device *pdev) 244962306a36Sopenharmony_ci{ 245062306a36Sopenharmony_ci struct omapfb2_device *fbdev = NULL; 245162306a36Sopenharmony_ci int r = 0; 245262306a36Sopenharmony_ci int i; 245362306a36Sopenharmony_ci struct omap_dss_device *def_display; 245462306a36Sopenharmony_ci struct omap_dss_device *dssdev; 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci DBG("omapfb_probe\n"); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci if (omapdss_is_initialized() == false) 245962306a36Sopenharmony_ci return -EPROBE_DEFER; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci if (pdev->num_resources != 0) { 246262306a36Sopenharmony_ci dev_err(&pdev->dev, "probed for an unknown device\n"); 246362306a36Sopenharmony_ci r = -ENODEV; 246462306a36Sopenharmony_ci goto err0; 246562306a36Sopenharmony_ci } 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci fbdev = devm_kzalloc(&pdev->dev, sizeof(struct omapfb2_device), 246862306a36Sopenharmony_ci GFP_KERNEL); 246962306a36Sopenharmony_ci if (fbdev == NULL) { 247062306a36Sopenharmony_ci r = -ENOMEM; 247162306a36Sopenharmony_ci goto err0; 247262306a36Sopenharmony_ci } 247362306a36Sopenharmony_ci 247462306a36Sopenharmony_ci if (def_vrfb && !omap_vrfb_supported()) { 247562306a36Sopenharmony_ci def_vrfb = 0; 247662306a36Sopenharmony_ci dev_warn(&pdev->dev, "VRFB is not supported on this hardware, " 247762306a36Sopenharmony_ci "ignoring the module parameter vrfb=y\n"); 247862306a36Sopenharmony_ci } 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci r = omapdss_compat_init(); 248162306a36Sopenharmony_ci if (r) 248262306a36Sopenharmony_ci goto err0; 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci mutex_init(&fbdev->mtx); 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci fbdev->dev = &pdev->dev; 248762306a36Sopenharmony_ci platform_set_drvdata(pdev, fbdev); 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci fbdev->num_displays = 0; 249062306a36Sopenharmony_ci dssdev = NULL; 249162306a36Sopenharmony_ci for_each_dss_dev(dssdev) { 249262306a36Sopenharmony_ci struct omapfb_display_data *d; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci omap_dss_get_device(dssdev); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci if (!dssdev->driver) { 249762306a36Sopenharmony_ci dev_warn(&pdev->dev, "no driver for display: %s\n", 249862306a36Sopenharmony_ci dssdev->name); 249962306a36Sopenharmony_ci omap_dss_put_device(dssdev); 250062306a36Sopenharmony_ci continue; 250162306a36Sopenharmony_ci } 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci d = &fbdev->displays[fbdev->num_displays++]; 250462306a36Sopenharmony_ci d->dssdev = dssdev; 250562306a36Sopenharmony_ci if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) 250662306a36Sopenharmony_ci d->update_mode = OMAPFB_MANUAL_UPDATE; 250762306a36Sopenharmony_ci else 250862306a36Sopenharmony_ci d->update_mode = OMAPFB_AUTO_UPDATE; 250962306a36Sopenharmony_ci } 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci if (fbdev->num_displays == 0) { 251262306a36Sopenharmony_ci dev_err(&pdev->dev, "no displays\n"); 251362306a36Sopenharmony_ci r = -EPROBE_DEFER; 251462306a36Sopenharmony_ci goto cleanup; 251562306a36Sopenharmony_ci } 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci fbdev->num_overlays = omap_dss_get_num_overlays(); 251862306a36Sopenharmony_ci for (i = 0; i < fbdev->num_overlays; i++) 251962306a36Sopenharmony_ci fbdev->overlays[i] = omap_dss_get_overlay(i); 252062306a36Sopenharmony_ci 252162306a36Sopenharmony_ci fbdev->num_managers = omap_dss_get_num_overlay_managers(); 252262306a36Sopenharmony_ci for (i = 0; i < fbdev->num_managers; i++) 252362306a36Sopenharmony_ci fbdev->managers[i] = omap_dss_get_overlay_manager(i); 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci def_display = omapfb_find_default_display(fbdev); 252662306a36Sopenharmony_ci if (def_display == NULL) { 252762306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to find default display\n"); 252862306a36Sopenharmony_ci r = -EPROBE_DEFER; 252962306a36Sopenharmony_ci goto cleanup; 253062306a36Sopenharmony_ci } 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci r = omapfb_init_connections(fbdev, def_display); 253362306a36Sopenharmony_ci if (r) { 253462306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to init overlay connections\n"); 253562306a36Sopenharmony_ci goto cleanup; 253662306a36Sopenharmony_ci } 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci if (def_mode && strlen(def_mode) > 0) { 253962306a36Sopenharmony_ci if (omapfb_parse_def_modes(fbdev)) 254062306a36Sopenharmony_ci dev_warn(&pdev->dev, "cannot parse default modes\n"); 254162306a36Sopenharmony_ci } else if (def_display && def_display->driver->set_timings && 254262306a36Sopenharmony_ci def_display->driver->check_timings) { 254362306a36Sopenharmony_ci struct omap_video_timings t; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci r = omapfb_find_best_mode(def_display, &t); 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci if (r == 0) 254862306a36Sopenharmony_ci def_display->driver->set_timings(def_display, &t); 254962306a36Sopenharmony_ci } 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci r = omapfb_create_framebuffers(fbdev); 255262306a36Sopenharmony_ci if (r) 255362306a36Sopenharmony_ci goto cleanup; 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_ci for (i = 0; i < fbdev->num_managers; i++) { 255662306a36Sopenharmony_ci struct omap_overlay_manager *mgr; 255762306a36Sopenharmony_ci mgr = fbdev->managers[i]; 255862306a36Sopenharmony_ci r = mgr->apply(mgr); 255962306a36Sopenharmony_ci if (r) 256062306a36Sopenharmony_ci dev_warn(fbdev->dev, "failed to apply dispc config\n"); 256162306a36Sopenharmony_ci } 256262306a36Sopenharmony_ci 256362306a36Sopenharmony_ci DBG("mgr->apply'ed\n"); 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci if (def_display) { 256662306a36Sopenharmony_ci r = omapfb_init_display(fbdev, def_display); 256762306a36Sopenharmony_ci if (r) { 256862306a36Sopenharmony_ci dev_err(fbdev->dev, 256962306a36Sopenharmony_ci "failed to initialize default " 257062306a36Sopenharmony_ci "display\n"); 257162306a36Sopenharmony_ci goto cleanup; 257262306a36Sopenharmony_ci } 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci 257562306a36Sopenharmony_ci DBG("create sysfs for fbs\n"); 257662306a36Sopenharmony_ci r = omapfb_create_sysfs(fbdev); 257762306a36Sopenharmony_ci if (r) { 257862306a36Sopenharmony_ci dev_err(fbdev->dev, "failed to create sysfs entries\n"); 257962306a36Sopenharmony_ci goto cleanup; 258062306a36Sopenharmony_ci } 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci if (def_display) { 258362306a36Sopenharmony_ci u16 w, h; 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci def_display->driver->get_resolution(def_display, &w, &h); 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci dev_info(fbdev->dev, "using display '%s' mode %dx%d\n", 258862306a36Sopenharmony_ci def_display->name, w, h); 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci 259162306a36Sopenharmony_ci return 0; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_cicleanup: 259462306a36Sopenharmony_ci omapfb_free_resources(fbdev); 259562306a36Sopenharmony_ci omapdss_compat_uninit(); 259662306a36Sopenharmony_cierr0: 259762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to setup omapfb\n"); 259862306a36Sopenharmony_ci return r; 259962306a36Sopenharmony_ci} 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_cistatic void omapfb_remove(struct platform_device *pdev) 260262306a36Sopenharmony_ci{ 260362306a36Sopenharmony_ci struct omapfb2_device *fbdev = platform_get_drvdata(pdev); 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci /* FIXME: wait till completion of pending events */ 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci omapfb_remove_sysfs(fbdev); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci omapfb_free_resources(fbdev); 261062306a36Sopenharmony_ci 261162306a36Sopenharmony_ci omapdss_compat_uninit(); 261262306a36Sopenharmony_ci} 261362306a36Sopenharmony_ci 261462306a36Sopenharmony_cistatic struct platform_driver omapfb_driver = { 261562306a36Sopenharmony_ci .probe = omapfb_probe, 261662306a36Sopenharmony_ci .remove_new = omapfb_remove, 261762306a36Sopenharmony_ci .driver = { 261862306a36Sopenharmony_ci .name = "omapfb", 261962306a36Sopenharmony_ci }, 262062306a36Sopenharmony_ci}; 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_cimodule_param_named(mode, def_mode, charp, 0); 262362306a36Sopenharmony_cimodule_param_named(vram, def_vram, charp, 0); 262462306a36Sopenharmony_cimodule_param_named(rotate, def_rotate, int, 0); 262562306a36Sopenharmony_cimodule_param_named(vrfb, def_vrfb, bool, 0); 262662306a36Sopenharmony_cimodule_param_named(mirror, def_mirror, bool, 0); 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_cimodule_platform_driver(omapfb_driver); 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ciMODULE_ALIAS("platform:omapfb"); 263162306a36Sopenharmony_ciMODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); 263262306a36Sopenharmony_ciMODULE_DESCRIPTION("OMAP2/3 Framebuffer"); 263362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2634