// SPDX-License-Identifier: GPL-2.0-or-later /* * Framebuffer driver for TI OMAP boards * * Copyright (C) 2004 Nokia Corporation * Author: Imre Deak <imre.deak@nokia.com> * * Acknowledgements: * Alex McMains <aam@ridgerun.com> - Original driver * Juha Yrjola <juha.yrjola@nokia.com> - Original driver and improvements * Dirk Behme <dirk.behme@de.bosch.com> - changes for 2.6 kernel API * Texas Instruments - H3 support
*/
/* * --------------------------------------------------------------------------- * LCD controller and LCD DMA * ---------------------------------------------------------------------------
*/ /* * Allocate resources needed for LCD controller and LCD DMA operations. Video * memory is allocated from system memory according to the virtual display * size, except if a bigger memory size is specified explicitly as a kernel * parameter.
*/ staticint ctrl_init(struct omapfb_device *fbdev)
{ int r; int i;
/* kernel/module vram parameters override boot tags/board config */ if (def_vram_cnt) { for (i = 0; i < def_vram_cnt; i++)
fbdev->mem_desc.region[i].size =
PAGE_ALIGN(def_vram[i]);
fbdev->mem_desc.region_cnt = i;
}
if (!fbdev->mem_desc.region_cnt) { struct lcd_panel *panel = fbdev->panel; int def_size; int bpp = panel->bpp;
if (fbdev->ctrl->sync)
fbdev->ctrl->sync();
r = fbdev->ctrl->setup_plane(plane->idx, plane->info.channel_out,
offset, var->xres_virtual,
plane->info.pos_x, plane->info.pos_y,
var->xres, var->yres, plane->color_mode); if (r < 0) return r;
if (fbdev->ctrl->set_rotate != NULL) {
r = fbdev->ctrl->set_rotate(var->rotate); if (r < 0) return r;
}
if (fbdev->ctrl->set_scale != NULL)
r = fbdev->ctrl->set_scale(plane->idx,
var->xres, var->yres,
plane->info.out_width,
plane->info.out_height);
return r;
}
/* * --------------------------------------------------------------------------- * fbdev framework callbacks and the ioctl interface * ---------------------------------------------------------------------------
*/ /* Called each time the omapfb device is opened */ staticint omapfb_open(struct fb_info *info, int user)
{ return 0;
}
staticvoid omapfb_sync(struct fb_info *info);
/* Called when the omapfb device is closed. We make sure that any pending
* gfx DMA operations are ended, before we return. */ staticint omapfb_release(struct fb_info *info, int user)
{
omapfb_sync(info); return 0;
}
/* Store a single color palette entry into a pseudo palette or the hardware * palette if one is available. For now we support only 16bpp and thus store * the entry only to the pseudo palette.
*/ staticint _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green,
u_int blue, u_int transp, int update_hw_pal)
{ struct omapfb_plane_struct *plane = info->par; struct omapfb_device *fbdev = plane->fbdev; struct fb_var_screeninfo *var = &info->var; int r = 0;
switch (plane->color_mode) { case OMAPFB_COLOR_YUV422: case OMAPFB_COLOR_YUV420: case OMAPFB_COLOR_YUY422:
r = -EINVAL; break; case OMAPFB_COLOR_CLUT_8BPP: case OMAPFB_COLOR_CLUT_4BPP: case OMAPFB_COLOR_CLUT_2BPP: case OMAPFB_COLOR_CLUT_1BPP: if (fbdev->ctrl->setcolreg)
r = fbdev->ctrl->setcolreg(regno, red, green, blue,
transp, update_hw_pal);
fallthrough; case OMAPFB_COLOR_RGB565: case OMAPFB_COLOR_RGB444: if (r != 0) break;
omapfb_rqueue_lock(fbdev); if (fbdev->ctrl->sync)
fbdev->ctrl->sync();
omapfb_rqueue_unlock(fbdev);
}
/* * Set fb_info.fix fields and also updates fbdev. * When calling this fb_info.var must be set up already.
*/ staticvoid set_fb_fix(struct fb_info *fbi, int from_init)
{ struct fb_fix_screeninfo *fix = &fbi->fix; struct fb_var_screeninfo *var = &fbi->var; struct omapfb_plane_struct *plane = fbi->par; struct omapfb_mem_region *rg; int bpp;
switch (var->bits_per_pixel) { case 1:
plane->color_mode = OMAPFB_COLOR_CLUT_1BPP; return 0; case 2:
plane->color_mode = OMAPFB_COLOR_CLUT_2BPP; return 0; case 4:
plane->color_mode = OMAPFB_COLOR_CLUT_4BPP; return 0; case 8:
plane->color_mode = OMAPFB_COLOR_CLUT_8BPP; return 0; case 12:
var->bits_per_pixel = 16;
fallthrough; case 16: if (plane->fbdev->panel->bpp == 12)
plane->color_mode = OMAPFB_COLOR_RGB444; else
plane->color_mode = OMAPFB_COLOR_RGB565; return 0; default: return -EINVAL;
}
}
/* * Check the values in var against our capabilities and in case of out of * bound values try to adjust them.
*/ staticint set_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var)
{ int bpp; unsignedlong max_frame_size; unsignedlong line_size; int xres_min, xres_max; int yres_min, yres_max; struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; struct lcd_panel *panel = fbdev->panel;
if (set_color_mode(plane, var) < 0) return -EINVAL;
bpp = var->bits_per_pixel; if (plane->color_mode == OMAPFB_COLOR_RGB444)
bpp = 16;
/* TODO: get these from panel->config */
var->vmode = FB_VMODE_NONINTERLACED;
var->sync = 0;
return 0;
}
/* * Set new x,y offsets in the virtual display for the visible area and switch * to the new mode.
*/ staticint omapfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fbi)
{ struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; int r = 0;
/* Set mirror to vertical axis and switch to the new mode. */ staticint omapfb_mirror(struct fb_info *fbi, int mirror)
{ struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; int r = 0;
omapfb_rqueue_lock(fbdev);
mirror = mirror ? 1 : 0; if (cpu_is_omap15xx())
r = -EINVAL; elseif (mirror != plane->info.mirror) {
plane->info.mirror = mirror;
r = ctrl_change_mode(fbi);
}
omapfb_rqueue_unlock(fbdev);
return r;
}
/* * Check values in var, try to adjust them in case of out of bound values if * possible, or return error.
*/ staticint omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi)
{ struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; int r;
omapfb_rqueue_lock(fbdev); if (fbdev->ctrl->sync != NULL)
fbdev->ctrl->sync();
r = set_fb_var(fbi, var);
omapfb_rqueue_unlock(fbdev);
return r;
}
/* * Switch to a new mode. The parameters for it has been check already by * omapfb_check_var.
*/ staticint omapfb_set_par(struct fb_info *fbi)
{ struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; int r = 0;
omapfb_rqueue_lock(fbdev);
set_fb_fix(fbi, 0);
r = ctrl_change_mode(fbi);
omapfb_rqueue_unlock(fbdev);
rg->size = size;
rg->type = mi->type; /* * size == 0 is a special case, for which we * don't check / adjust the screen parameters. * This isn't a problem since the plane can't * be reenabled unless its size is > 0.
*/ if (old_size != size && size) { if (size) {
memcpy(new_var, &fbi->var, sizeof(*new_var));
r = set_fb_var(fbi, new_var); if (r < 0) goto out;
}
}
if (fbdev->ctrl->sync)
fbdev->ctrl->sync();
r = fbdev->ctrl->setup_mem(plane->idx, size, mi->type, &paddr); if (r < 0) { /* Revert changes. */
rg->size = old_size;
rg->type = old_type; goto out;
}
rg->paddr = paddr;
if (old_size != size) { if (size) {
memcpy(&fbi->var, new_var, sizeof(fbi->var));
set_fb_fix(fbi, 0);
} else { /* * Set these explicitly to indicate that the * plane memory is dealloce'd, the other * screen parameters in var / fix are invalid.
*/
mutex_lock(&fbi->mm_lock);
fbi->fix.smem_start = 0;
fbi->fix.smem_len = 0;
mutex_unlock(&fbi->mm_lock);
}
}
}
out:
omapfb_rqueue_unlock(fbdev);
void omapfb_notify_clients(struct omapfb_device *fbdev, unsignedlong event)
{ int i;
if (!notifier_inited) /* no client registered yet */ return;
for (i = 0; i < OMAPFB_PLANE_NUM; i++)
blocking_notifier_call_chain(&omapfb_client_list[i], event,
fbdev->fb_info[i]);
}
EXPORT_SYMBOL(omapfb_notify_clients);
staticint omapfb_set_update_mode(struct omapfb_device *fbdev, enum omapfb_update_mode mode)
{ int r;
omapfb_rqueue_lock(fbdev);
r = fbdev->ctrl->set_update_mode(mode);
omapfb_rqueue_unlock(fbdev);
return r;
}
staticenum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev)
{ int r;
omapfb_rqueue_lock(fbdev);
r = fbdev->ctrl->get_update_mode();
omapfb_rqueue_unlock(fbdev);
/* * Ioctl interface. Part of the kernel mode frame buffer API is duplicated * here to be accessible by user mode code.
*/ staticint omapfb_ioctl(struct fb_info *fbi, unsignedint cmd, unsignedlong arg)
{ struct omapfb_plane_struct *plane = fbi->par; struct omapfb_device *fbdev = plane->fbdev; conststruct fb_ops *ops = fbi->fbops; union { struct omapfb_update_window update_window; struct omapfb_plane_info plane_info; struct omapfb_mem_info mem_info; struct omapfb_color_key color_key; enum omapfb_update_mode update_mode; struct omapfb_caps caps; unsignedint mirror; int plane_out; int enable_plane;
} p; int r = 0;
BUG_ON(!ops); switch (cmd) { case OMAPFB_MIRROR: if (get_user(p.mirror, (int __user *)arg))
r = -EFAULT; else
omapfb_mirror(fbi, p.mirror); break; case OMAPFB_SYNC_GFX:
omapfb_sync(fbi); break; case OMAPFB_VSYNC: break; case OMAPFB_SET_UPDATE_MODE: if (get_user(p.update_mode, (int __user *)arg))
r = -EFAULT; else
r = omapfb_set_update_mode(fbdev, p.update_mode); break; case OMAPFB_GET_UPDATE_MODE:
p.update_mode = omapfb_get_update_mode(fbdev); if (put_user(p.update_mode,
(enum omapfb_update_mode __user *)arg))
r = -EFAULT; break; case OMAPFB_UPDATE_WINDOW_OLD: if (copy_from_user(&p.update_window, (void __user *)arg, sizeof(struct omapfb_update_window_old)))
r = -EFAULT; else { struct omapfb_update_window *u = &p.update_window;
u->out_x = u->x;
u->out_y = u->y;
u->out_width = u->width;
u->out_height = u->height;
memset(u->reserved, 0, sizeof(u->reserved));
r = omapfb_update_win(fbi, u);
} break; case OMAPFB_UPDATE_WINDOW: if (copy_from_user(&p.update_window, (void __user *)arg, sizeof(p.update_window)))
r = -EFAULT; else
r = omapfb_update_win(fbi, &p.update_window); break; case OMAPFB_SETUP_PLANE: if (copy_from_user(&p.plane_info, (void __user *)arg, sizeof(p.plane_info)))
r = -EFAULT; else
r = omapfb_setup_plane(fbi, &p.plane_info); break; case OMAPFB_QUERY_PLANE: if ((r = omapfb_query_plane(fbi, &p.plane_info)) < 0) break; if (copy_to_user((void __user *)arg, &p.plane_info, sizeof(p.plane_info)))
r = -EFAULT; break; case OMAPFB_SETUP_MEM: if (copy_from_user(&p.mem_info, (void __user *)arg, sizeof(p.mem_info)))
r = -EFAULT; else
r = omapfb_setup_mem(fbi, &p.mem_info); break; case OMAPFB_QUERY_MEM: if ((r = omapfb_query_mem(fbi, &p.mem_info)) < 0) break; if (copy_to_user((void __user *)arg, &p.mem_info, sizeof(p.mem_info)))
r = -EFAULT; break; case OMAPFB_SET_COLOR_KEY: if (copy_from_user(&p.color_key, (void __user *)arg, sizeof(p.color_key)))
r = -EFAULT; else
r = omapfb_set_color_key(fbdev, &p.color_key); break; case OMAPFB_GET_COLOR_KEY: if ((r = omapfb_get_color_key(fbdev, &p.color_key)) < 0) break; if (copy_to_user((void __user *)arg, &p.color_key, sizeof(p.color_key)))
r = -EFAULT; break; case OMAPFB_GET_CAPS:
omapfb_get_caps(fbdev, plane->idx, &p.caps); if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps)))
r = -EFAULT; break; case OMAPFB_LCD_TEST:
{ int test_num;
if (get_user(test_num, (int __user *)arg)) {
r = -EFAULT; break;
} if (!fbdev->panel->run_test) {
r = -EINVAL; break;
}
r = fbdev->panel->run_test(fbdev->panel, test_num); break;
} case OMAPFB_CTRL_TEST:
{ int test_num;
if (get_user(test_num, (int __user *)arg)) {
r = -EFAULT; break;
} if (!fbdev->ctrl->run_test) {
r = -EINVAL; break;
}
r = fbdev->ctrl->run_test(test_num); break;
} default:
r = -EINVAL;
}
if (fbdev->panel->set_bklight_level) { unsignedint level;
if (sscanf(buf, "%10d", &level) == 1) {
r = fbdev->panel->set_bklight_level(fbdev->panel,
level);
} else
r = -EINVAL;
} else
r = -ENODEV; return r ? r : size;
}
staticvoid planes_cleanup(struct omapfb_device *fbdev)
{ int i;
for (i = 0; i < fbdev->mem_desc.region_cnt; i++) { if (fbdev->fb_info[i] == NULL) break;
fbinfo_cleanup(fbdev, fbdev->fb_info[i]);
framebuffer_release(fbdev->fb_info[i]);
}
}
staticint planes_init(struct omapfb_device *fbdev)
{ struct fb_info *fbi; int i; int r;
/* * Free driver resources. Can be called to rollback an aborted initialization * sequence.
*/ staticvoid omapfb_free_resources(struct omapfb_device *fbdev, int state)
{ int i;
switch (state) { case OMAPFB_ACTIVE: for (i = 0; i < fbdev->mem_desc.region_cnt; i++)
unregister_framebuffer(fbdev->fb_info[i]);
fallthrough; case 7:
omapfb_unregister_sysfs(fbdev);
fallthrough; case 6: if (fbdev->panel->disable)
fbdev->panel->disable(fbdev->panel);
fallthrough; case 5:
omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED);
fallthrough; case 4:
planes_cleanup(fbdev);
fallthrough; case 3:
ctrl_cleanup(fbdev);
fallthrough; case 2: if (fbdev->panel->cleanup)
fbdev->panel->cleanup(fbdev->panel);
fallthrough; case 1:
dev_set_drvdata(fbdev->dev, NULL);
kfree(fbdev); break; case 0: /* nothing to free */ break; default:
BUG();
}
}
for (i = 0; i < ARRAY_SIZE(ctrls); i++) {
dev_dbg(fbdev->dev, "ctrl %s\n", ctrls[i]->name); if (strcmp(ctrls[i]->name, name) == 0) {
fbdev->ctrl = ctrls[i]; break;
}
}
if (fbdev->ctrl == NULL) {
dev_dbg(fbdev->dev, "ctrl %s not supported\n", name); return -1;
}
return 0;
}
/* * Called by LDM binding to probe and attach a new device. * Initialization sequence: * 1. allocate system omapfb_device structure * 2. select controller type according to platform configuration * init LCD panel * 3. init LCD controller and LCD DMA * 4. init system fb_info structure for all planes * 5. setup video mode for first plane and enable it * 6. enable LCD panel * 7. register sysfs attributes * OMAPFB_ACTIVE: register system fb_info structure for all planes
*/ staticint omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel)
{ struct omapfb_device *fbdev = NULL; int init_state; unsignedlong phz, hhz, vhz; unsignedlong vram; int i; int r = 0;
init_state = 0;
if (pdev->num_resources != 2) {
dev_err(&pdev->dev, "probed for an unknown device\n");
r = -ENODEV; goto cleanup;
}
if (dev_get_platdata(&pdev->dev) == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
r = -ENOENT; goto cleanup;
}
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); if (fbdev == NULL) {
dev_err(&pdev->dev, "unable to allocate memory for device info\n");
r = -ENOMEM; goto cleanup;
}
r = platform_get_irq(pdev, 0); if (r < 0) goto cleanup;
fbdev->int_irq = r;
r = platform_get_irq(pdev, 1); if (r < 0) goto cleanup;
fbdev->ext_irq = r;
staticint omapfb_probe(struct platform_device *pdev)
{ int r;
BUG_ON(fbdev_pdev != NULL);
r = platform_device_register(&omapdss_device); if (r) {
dev_err(&pdev->dev, "can't register omapdss device\n"); return r;
}
/* Delay actual initialization until the LCD is registered */
fbdev_pdev = pdev; if (fbdev_panel != NULL)
omapfb_do_probe(fbdev_pdev, fbdev_panel); return 0;
}
/* Called when the device is being detached from the driver */ staticvoid omapfb_remove(struct platform_device *pdev)
{ struct omapfb_device *fbdev = platform_get_drvdata(pdev); enum omapfb_state saved_state = fbdev->state;
/* FIXME: wait till completion of pending events */
/* Register both the driver and the device */ staticint __init omapfb_init(void)
{ #ifndef MODULE char *option;
if (fb_get_options("omapfb", &option)) return -ENODEV;
omapfb_setup(option); #endif /* Register the driver with LDM */ if (platform_driver_register(&omapfb_driver)) {
pr_debug("failed to register omapfb driver\n"); return -ENODEV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.