// SPDX-License-Identifier: GPL-2.0-only /* * A framebuffer driver for VBE 2.0+ compliant video cards * * (c) 2007 Michal Januszewski <spock@gentoo.org> * Loosely based upon the vesafb driver. *
*/
/* * A handler for replies from userspace. * * Make sure each message passes consistency checks and if it does, * find the kernel part of the task struct, copy the registers and * the buffer contents and then complete the task.
*/ staticvoid uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
{ struct uvesafb_task *utask; struct uvesafb_ktask *task;
/* * Execute a uvesafb task. * * Returns 0 if the task is executed successfully. * * A message sent to the userspace consists of the uvesafb_task * struct and (optionally) a buffer. The uvesafb_task struct is * a simplified version of uvesafb_ktask (its kernel counterpart) * containing only the register values, flags and the length of * the buffer. * * Each message is assigned a sequence number (increased linearly) * and a random ack number. The sequence number is used as a key * for the uvfb_tasks array which holds pointers to uvesafb_ktask * structs for all requests.
*/ staticint uvesafb_exec(struct uvesafb_ktask *task)
{ staticint seq; struct cn_msg *m; int err; int len = sizeof(task->t) + task->t.buf_len;
/* * Check whether the message isn't longer than the maximum * allowed by connector.
*/ if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) {
pr_warn("message too long (%d), can't execute task\n",
(int)(sizeof(*m) + len)); return -E2BIG;
}
m = kzalloc(sizeof(*m) + len, GFP_KERNEL); if (!m) return -ENOMEM;
staticint uvesafb_vbe_find_mode(struct uvesafb_par *par, int xres, int yres, int depth, unsignedchar flags)
{ int i, match = -1, h = 0, d = 0x7fffffff;
for (i = 0; i < par->vbe_modes_cnt; i++) {
h = abs(par->vbe_modes[i].x_res - xres) +
abs(par->vbe_modes[i].y_res - yres) +
abs(depth - par->vbe_modes[i].depth);
/* * We have an exact match in terms of resolution * and depth.
*/ if (h == 0) return i;
if (h < d || (h == d && par->vbe_modes[i].depth > depth)) {
d = h;
match = i;
}
}
i = 1;
if (flags & UVESAFB_EXACT_DEPTH &&
par->vbe_modes[match].depth != depth)
i = 0;
if (par->vbe_ib.vbe_version < 0x0200) {
pr_err("Sorry, pre-VBE 2.0 cards are not supported\n"); return -EINVAL;
}
if (!par->vbe_ib.mode_list_ptr) {
pr_err("Missing mode list!\n"); return -EINVAL;
}
pr_info("");
/* * Convert string pointers and the mode list pointer into * usable addresses. Print informational messages about the * video adapter and its vendor.
*/ if (par->vbe_ib.oem_vendor_name_ptr)
pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr);
if (par->vbe_ib.oem_product_name_ptr)
pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_product_name_ptr);
if (par->vbe_ib.oem_product_rev_ptr)
pr_cont("%s, ",
((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr);
if (par->vbe_ib.oem_string_ptr)
pr_cont("OEM: %s, ",
((char *)task->buf) + par->vbe_ib.oem_string_ptr);
par->vbe_modes = kcalloc(par->vbe_modes_cnt, sizeof(struct vbe_mode_ib),
GFP_KERNEL); if (!par->vbe_modes) return -ENOMEM;
/* Get info about all available modes. */
mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); while (*mode != 0xffff) { struct vbe_mode_ib *mib;
err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
pr_warn("Getting mode info block for mode 0x%x failed (eax=0x%x, err=%d)\n",
*mode, (u32)task->t.regs.eax, err);
mode++;
par->vbe_modes_cnt--; continue;
}
mib = task->buf;
mib->mode_id = *mode;
/* * We only want modes that are supported with the current * hardware configuration, color, graphics and that have * support for the LFB.
*/ if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK &&
mib->bits_per_pixel >= 8)
off++; else
par->vbe_modes_cnt--;
/* * Handle 8bpp modes and modes with broken color component * lengths.
*/ if (mib->depth == 0 || (mib->depth == 24 &&
mib->bits_per_pixel == 32))
mib->depth = mib->bits_per_pixel;
}
if (par->vbe_modes_cnt > 0) return 0; else return -EINVAL;
}
/* * The Protected Mode Interface is 32-bit x86 code, so we only run it on * x86 and not x86_64.
*/ #ifdef CONFIG_X86_32 staticint uvesafb_vbe_getpmi(struct uvesafb_ktask *task, struct uvesafb_par *par)
{ int i, err;
/* * Check whether a video mode is supported by the Video BIOS and is * compatible with the monitor limits.
*/ staticint uvesafb_is_valid_mode(struct fb_videomode *mode, struct fb_info *info)
{ if (info->monspecs.gtf) {
fb_videomode_to_var(&info->var, mode); if (fb_validate_mode(&info->var, info)) return 0;
}
if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8,
UVESAFB_EXACT_RES) == -1) return 0;
if (info->monspecs.vfmax && info->monspecs.hfmax) { /* * If the maximum pixel clock wasn't specified in * the EDID block, set it to 300 MHz.
*/ if (info->monspecs.dclkmax == 0)
info->monspecs.dclkmax = 300 * 1000000;
info->monspecs.gtf = 1;
}
} else {
err = -EINVAL;
}
/* * If we don't get all necessary data from the EDID block, * mark it as incompatible with the GTF and set nocrtc so * that we always use the default BIOS refresh rate.
*/ if (uvesafb_vbe_getedid(task, info)) {
info->monspecs.gtf = 0;
par->nocrtc = 1;
}
/* Kernel command line overrides. */ if (maxclk)
info->monspecs.dclkmax = maxclk * 1000000; if (maxvf)
info->monspecs.vfmax = maxvf; if (maxhf)
info->monspecs.hfmax = maxhf * 1000;
/* * In case DDC transfers are not supported, the user can provide * monitor limits manually. Lower limits are set to "safe" values.
*/ if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) {
info->monspecs.dclkmin = 0;
info->monspecs.vfmin = 60;
info->monspecs.hfmin = 29000;
info->monspecs.gtf = 1;
par->nocrtc = 0;
}
if (info->monspecs.gtf)
pr_info("monitor limits: vf = %d Hz, hf = %d kHz, clk = %d MHz\n",
info->monspecs.vfmax,
(int)(info->monspecs.hfmax / 1000),
(int)(info->monspecs.dclkmax / 1000000)); else
pr_info("no monitor limits have been set, default refresh rate will be used\n");
/* Add VBE modes to the modelist. */ for (i = 0; i < par->vbe_modes_cnt; i++) { struct fb_var_screeninfo var; struct vbe_mode_ib *mode; struct fb_videomode vmode;
/* Add valid VESA modes to our modelist. */ for (i = 0; i < VESA_MODEDB_SIZE; i++) { if (uvesafb_is_valid_mode((struct fb_videomode *)
&vesa_modes[i], info))
fb_add_videomode(&vesa_modes[i], &info->modelist);
}
for (i = 0; i < info->monspecs.modedb_len; i++) { if (uvesafb_is_valid_mode(&info->monspecs.modedb[i], info))
fb_add_videomode(&info->monspecs.modedb[i],
&info->modelist);
}
return;
}
staticvoid uvesafb_vbe_getstatesize(struct uvesafb_ktask *task, struct uvesafb_par *par)
{ int err;
uvesafb_reset(task);
/* * Get the VBE state buffer size. We want all available * hardware state data (CL = 0x0f).
*/
task->t.regs.eax = 0x4f04;
task->t.regs.ecx = 0x000f;
task->t.regs.edx = 0x0000;
task->t.flags = 0;
err = uvesafb_exec(task);
if (err || (task->t.regs.eax & 0xffff) != 0x004f) {
pr_warn("VBE state buffer size cannot be determined (eax=0x%x, err=%d)\n",
task->t.regs.eax, err);
par->vbe_state_size = 0; return;
}
if (par->pmi_setpal || par->ypan) { if (__supported_pte_mask & _PAGE_NX) {
par->pmi_setpal = par->ypan = 0;
pr_warn("NX protection is active, better not use the PMI\n");
} else {
uvesafb_vbe_getpmi(task, par);
}
} #else /* The protected mode interface is not available on non-x86. */
par->pmi_setpal = par->ypan = 0; #endif
/* Has the user requested a specific VESA mode? */ if (vbemode) { for (i = 0; i < par->vbe_modes_cnt; i++) { if (par->vbe_modes[i].mode_id == vbemode) {
modeid = i;
uvesafb_setup_var(&info->var, info,
&par->vbe_modes[modeid]);
fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
&info->var, info); /* * With pixclock set to 0, the default BIOS * timings will be used in set_par().
*/
info->var.pixclock = 0; goto gotmode;
}
}
pr_info("requested VBE mode 0x%x is unavailable\n", vbemode);
vbemode = 0;
}
/* Count the modes in the modelist */
i = 0;
list_for_each(pos, &info->modelist)
i++;
/* * Convert the modelist into a modedb so that we can use it with * fb_find_mode().
*/
mode = kcalloc(i, sizeof(*mode), GFP_KERNEL); if (mode) {
i = 0;
list_for_each(pos, &info->modelist) {
modelist = list_entry(pos, struct fb_modelist, list);
mode[i] = modelist->mode;
i++;
}
if (!mode_option)
mode_option = UVESAFB_DEFAULT_MODE;
i = fb_find_mode(&info->var, info, mode_option, mode, i,
NULL, 8);
gotmode: /* * If we are not VBE3.0+ compliant, we're done -- the BIOS will * ignore our timings anyway.
*/ if (par->vbe_ib.vbe_version < 0x0300 || par->nocrtc)
fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60,
&info->var, info);
return modeid;
}
staticint uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count, int start, struct fb_info *info)
{ struct uvesafb_ktask *task; #ifdef CONFIG_X86 struct uvesafb_par *par = info->par; int i = par->mode_idx; #endif int err = 0;
/* * We support palette modifications for 8 bpp modes only, so * there can never be more than 256 entries.
*/ if (start + count > 256) return -EINVAL;
#ifdef CONFIG_X86 /* Use VGA registers if mode is VGA-compatible. */ if (i >= 0 && i < par->vbe_modes_cnt &&
par->vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) { for (i = 0; i < count; i++) {
outb_p(start + i, dac_reg);
outb_p(entries[i].red, dac_val);
outb_p(entries[i].green, dac_val);
outb_p(entries[i].blue, dac_val);
}
} #ifdef CONFIG_X86_32 elseif (par->pmi_setpal) {
__asm__ __volatile__( "call *(%%esi)"
: /* no return value */
: "a" (0x4f09), /* EAX */ "b" (0), /* EBX */ "c" (count), /* ECX */ "d" (start), /* EDX */ "D" (entries), /* EDI */ "S" (&par->pmi_pal)); /* ESI */
} #endif/* CONFIG_X86_32 */ else #endif/* CONFIG_X86 */
{
task = uvesafb_prep(); if (!task) return -ENOMEM;
case 24: case 32:
red >>= 8;
green >>= 8;
blue >>= 8;
((u32 *)(info->pseudo_palette))[regno] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset); break;
}
} return err;
}
staticint uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info)
{ struct uvesafb_pal_entry *entries; int shift = 16 - dac_width; int i, err = 0;
if (info->var.bits_per_pixel == 8) { if (cmap->start + cmap->len > info->cmap.start +
info->cmap.len || cmap->start < info->cmap.start) return -EINVAL;
entries = kmalloc_array(cmap->len, sizeof(*entries),
GFP_KERNEL); if (!entries) return -ENOMEM;
for (i = 0; i < cmap->len; i++) {
entries[i].red = cmap->red[i] >> shift;
entries[i].green = cmap->green[i] >> shift;
entries[i].blue = cmap->blue[i] >> shift;
entries[i].pad = 0;
}
err = uvesafb_setpalette(entries, cmap->len, cmap->start, info);
kfree(entries);
} else { /* * For modes with bpp > 8, we only set the pseudo palette in * the fb_info struct. We rely on uvesafb_setcolreg to do all * sanity checking.
*/ for (i = 0; i < cmap->len; i++) {
err |= uvesafb_setcolreg(cmap->start + i, cmap->red[i],
cmap->green[i], cmap->blue[i],
0, info);
}
} return err;
}
/* * It turns out it's not the best idea to do panning via vm86, * so we only allow it if we have a PMI.
*/ if (par->pmi_start) {
__asm__ __volatile__( "call *(%%edi)"
: /* no return value */
: "a" (0x4f07), /* EAX */ "b" (0), /* EBX */ "c" (offset), /* ECX */ "d" (offset >> 16), /* EDX */ "D" (&par->pmi_start)); /* EDI */
} #endif return 0;
}
/* First, try to set the standard 80x25 text mode. */
task->t.regs.eax = 0x0003;
uvesafb_exec(task);
/* * Now try to restore whatever hardware state we might have * saved when the fb device was first opened.
*/
uvesafb_vbe_state_restore(par, par->vbe_state_orig);
out:
atomic_dec(&par->ref_count);
uvesafb_free(task); return 0;
}
err = uvesafb_exec(task); if (err || (task->t.regs.eax & 0xffff) != 0x004f) { /* * The mode switch might have failed because we tried to * use our own timings. Try again with the default timings.
*/ if (crtc != NULL) {
pr_warn("mode switch failed (eax=0x%x, err=%d) - trying again with default timings\n",
task->t.regs.eax, err);
uvesafb_reset(task);
kfree(crtc);
crtc = NULL;
info->var.pixclock = 0; goto setmode;
} else {
pr_err("mode switch failed (eax=0x%x, err=%d)\n",
task->t.regs.eax, err);
err = -EINVAL; goto out;
}
}
par->mode_idx = i;
/* For 8bpp modes, always try to set the DAC to 8 bits. */ if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC &&
mode->bits_per_pixel <= 8) {
uvesafb_reset(task);
task->t.regs.eax = 0x4f08;
task->t.regs.ebx = 0x0800;
if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) return; /* Use default refresh rate */
var->pixclock = 0;
}
staticint uvesafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{ struct uvesafb_par *par = info->par; struct vbe_mode_ib *mode = NULL; int match = -1; int depth = var->red.length + var->green.length + var->blue.length;
/* * Various apps will use bits_per_pixel to set the color depth, * which is theoretically incorrect, but which we'll try to handle * here.
*/ if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8)
depth = var->bits_per_pixel;
match = uvesafb_vbe_find_mode(par, var->xres, var->yres, depth,
UVESAFB_EXACT_RES); if (match == -1) return -EINVAL;
/* * Check whether we have remapped enough memory for this mode. * We might be called at an early stage, when we haven't remapped * any memory yet, in which case we simply skip the check.
*/ if (var->yres * mode->bytes_per_scan_line > info->fix.smem_len
&& info->fix.smem_len) return -EINVAL;
/* Disable blanking if the user requested so. */ if (!blank)
uvesafb_ops.fb_blank = NULL;
/* * Find out how much IO memory is required for the mode with * the highest resolution.
*/
size_remap = 0; for (i = 0; i < par->vbe_modes_cnt; i++) {
h = par->vbe_modes[i].bytes_per_scan_line *
par->vbe_modes[i].y_res; if (h > size_remap)
size_remap = h;
}
size_remap *= 2;
/* * size_vmode -- that is the amount of memory needed for the * used video mode, i.e. the minimum amount of * memory we need.
*/
size_vmode = info->var.yres * mode->bytes_per_scan_line;
/* * size_total -- all video memory we have. Used for mtrr * entries, resource allocation and bounds * checking.
*/
size_total = par->vbe_ib.total_memory * 65536; if (vram_total)
size_total = vram_total * 1024 * 1024; if (size_total < size_vmode)
size_total = size_vmode;
/* * size_remap -- the amount of video memory we are going to * use for vesafb. With modern cards it is no * option to simply use size_total as th * wastes plenty of kernel address space.
*/ if (vram_remap)
size_remap = vram_remap * 1024 * 1024; if (size_remap < size_vmode)
size_remap = size_vmode; if (size_remap > size_total)
size_remap = size_total;
/* * We have to set yres_virtual here because when setup_var() was * called, smem_len wasn't defined yet.
*/
info->var.yres_virtual = info->fix.smem_len /
mode->bytes_per_scan_line;
if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { int temp_size = info->fix.smem_len;
int rc;
/* Find the largest power-of-two */
temp_size = roundup_pow_of_two(temp_size);
/* Try and find a power of two to add */ do {
rc = arch_phys_wc_add(info->fix.smem_start, temp_size);
temp_size >>= 1;
} while (temp_size >= PAGE_SIZE && rc == -EINVAL);
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.