// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/drivers/video/omap2/omapfb-ioctl.c
*
* Copyright (C) 2008 Nokia Corporation
* Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
*
* Some code and ideas taken from drivers/video/omap/ driver
* by Imre Deak.
*/
#include <linux/fb.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/mm.h>
#include <linux/omapfb.h>
#include <linux/vmalloc.h>
#include <linux/export.h>
#include <linux/sizes.h>
#include <video/omapfb_dss.h>
#include <video/omapvrfb.h>
#include "omapfb.h"
static u8 get_mem_idx(struct omapfb_info *ofbi)
{
if (ofbi->id == ofbi->region->id)
return 0;
return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id;
}
static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi,
u8 mem_idx)
{
struct omapfb2_device *fbdev = ofbi->fbdev;
if (mem_idx & OMAPFB_MEM_IDX_ENABLED)
mem_idx &= OMAPFB_MEM_IDX_MASK;
else
mem_idx = ofbi->id;
if (mem_idx >= fbdev->num_fbs)
return NULL;
return &fbdev->regions[mem_idx];
}
static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_overlay *ovl;
struct omap_overlay_info old_info;
struct omapfb2_mem_region *old_rg, *new_rg;
int r = 0;
DBG("omapfb_setup_plane\n");
if (ofbi->num_overlays == 0) {
r = -EINVAL;
goto out;
}
/* XXX uses only the first overlay */
ovl = ofbi->overlays[0];
old_rg = ofbi->region;
new_rg = get_mem_region(ofbi, pi->mem_idx);
if (!new_rg) {
r = -EINVAL;
goto out;
}
/* Take the locks in a specific order to keep lockdep happy */
if (old_rg->id < new_rg->id) {
omapfb_get_mem_region(old_rg);
omapfb_get_mem_region(new_rg);
} else if (new_rg->id < old_rg->id) {
omapfb_get_mem_region(new_rg);
omapfb_get_mem_region(old_rg);
} else
omapfb_get_mem_region(old_rg);
if (pi->enabled && !new_rg->size) {
/*
* This plane's memory was freed, can't enable it
* until it's reallocated.
*/
r = -EINVAL;
goto put_mem;
}
ovl->get_overlay_info(ovl, &old_info);
if (old_rg != new_rg) {
ofbi->region = new_rg;
set_fb_fix(fbi);
}
if (!pi->enabled) {
r = ovl->disable(ovl);
if (r)
goto undo;
}
if (pi->enabled) {
r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y,
pi->out_width, pi->out_height);
if (r)
goto undo;
} else {
struct omap_overlay_info info;
ovl->get_overlay_info(ovl, &info);
info.pos_x = pi->pos_x;
info.pos_y = pi->pos_y;
info.out_width = pi->out_width;
info.out_height = pi->out_height;
r = ovl->set_overlay_info(ovl, &info);
if (r)
goto undo;
}
if (ovl->manager) {
r = ovl->manager->apply(ovl->manager);
if (r)
goto undo;
}
if (pi->enabled) {
r = ovl->enable(ovl);
if (r)
goto undo;
}
/* Release the locks in a specific order to keep lockdep happy */
if (old_rg->id > new_rg->id) {
omapfb_put_mem_region(old_rg);
omapfb_put_mem_region(new_rg);
} else if (new_rg->id > old_rg->id) {
omapfb_put_mem_region(new_rg);
omapfb_put_mem_region(old_rg);
} else
omapfb_put_mem_region(old_rg);
return 0;
undo:
if (old_rg != new_rg) {
ofbi->region = old_rg;
set_fb_fix(fbi);
}
ovl->set_overlay_info(ovl, &old_info);
put_mem:
/* Release the locks in a specific order to keep lockdep happy */
if (old_rg->id > new_rg->id) {
omapfb_put_mem_region(old_rg);
omapfb_put_mem_region(new_rg);
} else if (new_rg->id > old_rg->id) {
omapfb_put_mem_region(new_rg);
omapfb_put_mem_region(old_rg);
} else
omapfb_put_mem_region(old_rg);
out:
dev_err(fbdev->dev, "setup_plane failed\n");
return r;
}
static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
if (ofbi->num_overlays == 0) {
memset(pi, 0, sizeof(*pi));
} else {
struct omap_overlay *ovl;
struct omap_overlay_info ovli;
ovl = ofbi->overlays[0];
ovl->get_overlay_info(ovl, &ovli);
pi->pos_x = ovli.pos_x;
pi->pos_y = ovli.pos_y;
pi->enabled = ovl->is_enabled(ovl);
pi->channel_out = 0; /* xxx */
pi->mirror = 0;
pi->mem_idx = get_mem_idx(ofbi);
pi->out_width = ovli.out_width;
pi->out_height = ovli.out_height;
}
return 0;
}
static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
{
struct omapfb_info *ofbi = FB2OFB(fbi);
struct omapfb2_device *fbdev = ofbi->fbdev;
struct omap_dss_device *display = fb2display(fbi);
struct omapfb2_mem_region *rg;
int r = 0, i;
size_t size;
if (mi->type != OMAPFB_MEMTYPE_SDRAM)
return -EINVAL