162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * A framebuffer driver for VBE 2.0+ compliant video cards 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) 2007 Michal Januszewski <spock@gentoo.org> 662306a36Sopenharmony_ci * Loosely based upon the vesafb driver. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/moduleparam.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/timer.h> 1762306a36Sopenharmony_ci#include <linux/completion.h> 1862306a36Sopenharmony_ci#include <linux/connector.h> 1962306a36Sopenharmony_ci#include <linux/random.h> 2062306a36Sopenharmony_ci#include <linux/platform_device.h> 2162306a36Sopenharmony_ci#include <linux/limits.h> 2262306a36Sopenharmony_ci#include <linux/fb.h> 2362306a36Sopenharmony_ci#include <linux/io.h> 2462306a36Sopenharmony_ci#include <linux/mutex.h> 2562306a36Sopenharmony_ci#include <linux/slab.h> 2662306a36Sopenharmony_ci#include <video/edid.h> 2762306a36Sopenharmony_ci#include <video/uvesafb.h> 2862306a36Sopenharmony_ci#ifdef CONFIG_X86 2962306a36Sopenharmony_ci#include <video/vga.h> 3062306a36Sopenharmony_ci#endif 3162306a36Sopenharmony_ci#include "edid.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct cb_id uvesafb_cn_id = { 3462306a36Sopenharmony_ci .idx = CN_IDX_V86D, 3562306a36Sopenharmony_ci .val = CN_VAL_V86D_UVESAFB 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_cistatic char v86d_path[PATH_MAX] = "/sbin/v86d"; 3862306a36Sopenharmony_cistatic char v86d_started; /* has v86d been started by uvesafb? */ 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic const struct fb_fix_screeninfo uvesafb_fix = { 4162306a36Sopenharmony_ci .id = "VESA VGA", 4262306a36Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 4362306a36Sopenharmony_ci .accel = FB_ACCEL_NONE, 4462306a36Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic int mtrr = 3; /* enable mtrr by default */ 4862306a36Sopenharmony_cistatic bool blank = true; /* enable blanking by default */ 4962306a36Sopenharmony_cistatic int ypan = 1; /* 0: scroll, 1: ypan, 2: ywrap */ 5062306a36Sopenharmony_cistatic bool pmi_setpal = true; /* use PMI for palette changes */ 5162306a36Sopenharmony_cistatic bool nocrtc; /* ignore CRTC settings */ 5262306a36Sopenharmony_cistatic bool noedid; /* don't try DDC transfers */ 5362306a36Sopenharmony_cistatic int vram_remap; /* set amt. of memory to be used */ 5462306a36Sopenharmony_cistatic int vram_total; /* set total amount of memory */ 5562306a36Sopenharmony_cistatic u16 maxclk; /* maximum pixel clock */ 5662306a36Sopenharmony_cistatic u16 maxvf; /* maximum vertical frequency */ 5762306a36Sopenharmony_cistatic u16 maxhf; /* maximum horizontal frequency */ 5862306a36Sopenharmony_cistatic u16 vbemode; /* force use of a specific VBE mode */ 5962306a36Sopenharmony_cistatic char *mode_option; 6062306a36Sopenharmony_cistatic u8 dac_width = 6; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX]; 6362306a36Sopenharmony_cistatic DEFINE_MUTEX(uvfb_lock); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * A handler for replies from userspace. 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * Make sure each message passes consistency checks and if it does, 6962306a36Sopenharmony_ci * find the kernel part of the task struct, copy the registers and 7062306a36Sopenharmony_ci * the buffer contents and then complete the task. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_cistatic void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct uvesafb_task *utask; 7562306a36Sopenharmony_ci struct uvesafb_ktask *task; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 7862306a36Sopenharmony_ci return; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (msg->seq >= UVESAFB_TASKS_MAX) 8162306a36Sopenharmony_ci return; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci mutex_lock(&uvfb_lock); 8462306a36Sopenharmony_ci task = uvfb_tasks[msg->seq]; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (!task || msg->ack != task->ack) { 8762306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci utask = (struct uvesafb_task *)msg->data; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Sanity checks for the buffer length. */ 9462306a36Sopenharmony_ci if (task->t.buf_len < utask->buf_len || 9562306a36Sopenharmony_ci utask->buf_len > msg->len - sizeof(*utask)) { 9662306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 9762306a36Sopenharmony_ci return; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci uvfb_tasks[msg->seq] = NULL; 10162306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci memcpy(&task->t, utask, sizeof(*utask)); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (task->t.buf_len && task->buf) 10662306a36Sopenharmony_ci memcpy(task->buf, utask + 1, task->t.buf_len); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci complete(task->done); 10962306a36Sopenharmony_ci return; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int uvesafb_helper_start(void) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci char *envp[] = { 11562306a36Sopenharmony_ci "HOME=/", 11662306a36Sopenharmony_ci "PATH=/sbin:/bin", 11762306a36Sopenharmony_ci NULL, 11862306a36Sopenharmony_ci }; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci char *argv[] = { 12162306a36Sopenharmony_ci v86d_path, 12262306a36Sopenharmony_ci NULL, 12362306a36Sopenharmony_ci }; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * Execute a uvesafb task. 13062306a36Sopenharmony_ci * 13162306a36Sopenharmony_ci * Returns 0 if the task is executed successfully. 13262306a36Sopenharmony_ci * 13362306a36Sopenharmony_ci * A message sent to the userspace consists of the uvesafb_task 13462306a36Sopenharmony_ci * struct and (optionally) a buffer. The uvesafb_task struct is 13562306a36Sopenharmony_ci * a simplified version of uvesafb_ktask (its kernel counterpart) 13662306a36Sopenharmony_ci * containing only the register values, flags and the length of 13762306a36Sopenharmony_ci * the buffer. 13862306a36Sopenharmony_ci * 13962306a36Sopenharmony_ci * Each message is assigned a sequence number (increased linearly) 14062306a36Sopenharmony_ci * and a random ack number. The sequence number is used as a key 14162306a36Sopenharmony_ci * for the uvfb_tasks array which holds pointers to uvesafb_ktask 14262306a36Sopenharmony_ci * structs for all requests. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic int uvesafb_exec(struct uvesafb_ktask *task) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci static int seq; 14762306a36Sopenharmony_ci struct cn_msg *m; 14862306a36Sopenharmony_ci int err; 14962306a36Sopenharmony_ci int len = sizeof(task->t) + task->t.buf_len; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* 15262306a36Sopenharmony_ci * Check whether the message isn't longer than the maximum 15362306a36Sopenharmony_ci * allowed by connector. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) { 15662306a36Sopenharmony_ci pr_warn("message too long (%d), can't execute task\n", 15762306a36Sopenharmony_ci (int)(sizeof(*m) + len)); 15862306a36Sopenharmony_ci return -E2BIG; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci m = kzalloc(sizeof(*m) + len, GFP_KERNEL); 16262306a36Sopenharmony_ci if (!m) 16362306a36Sopenharmony_ci return -ENOMEM; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci init_completion(task->done); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci memcpy(&m->id, &uvesafb_cn_id, sizeof(m->id)); 16862306a36Sopenharmony_ci m->seq = seq; 16962306a36Sopenharmony_ci m->len = len; 17062306a36Sopenharmony_ci m->ack = get_random_u32(); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* uvesafb_task structure */ 17362306a36Sopenharmony_ci memcpy(m + 1, &task->t, sizeof(task->t)); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* Buffer */ 17662306a36Sopenharmony_ci memcpy((u8 *)(m + 1) + sizeof(task->t), task->buf, task->t.buf_len); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Save the message ack number so that we can find the kernel 18062306a36Sopenharmony_ci * part of this task when a reply is received from userspace. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci task->ack = m->ack; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mutex_lock(&uvfb_lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* If all slots are taken -- bail out. */ 18762306a36Sopenharmony_ci if (uvfb_tasks[seq]) { 18862306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 18962306a36Sopenharmony_ci err = -EBUSY; 19062306a36Sopenharmony_ci goto out; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Save a pointer to the kernel part of the task struct. */ 19462306a36Sopenharmony_ci uvfb_tasks[seq] = task; 19562306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci err = cn_netlink_send(m, 0, 0, GFP_KERNEL); 19862306a36Sopenharmony_ci if (err == -ESRCH) { 19962306a36Sopenharmony_ci /* 20062306a36Sopenharmony_ci * Try to start the userspace helper if sending 20162306a36Sopenharmony_ci * the request failed the first time. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci err = uvesafb_helper_start(); 20462306a36Sopenharmony_ci if (err) { 20562306a36Sopenharmony_ci pr_err("failed to execute %s\n", v86d_path); 20662306a36Sopenharmony_ci pr_err("make sure that the v86d helper is installed and executable\n"); 20762306a36Sopenharmony_ci } else { 20862306a36Sopenharmony_ci v86d_started = 1; 20962306a36Sopenharmony_ci err = cn_netlink_send(m, 0, 0, gfp_any()); 21062306a36Sopenharmony_ci if (err == -ENOBUFS) 21162306a36Sopenharmony_ci err = 0; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } else if (err == -ENOBUFS) 21462306a36Sopenharmony_ci err = 0; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (!err && !(task->t.flags & TF_EXIT)) 21762306a36Sopenharmony_ci err = !wait_for_completion_timeout(task->done, 21862306a36Sopenharmony_ci msecs_to_jiffies(UVESAFB_TIMEOUT)); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci mutex_lock(&uvfb_lock); 22162306a36Sopenharmony_ci uvfb_tasks[seq] = NULL; 22262306a36Sopenharmony_ci mutex_unlock(&uvfb_lock); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci seq++; 22562306a36Sopenharmony_ci if (seq >= UVESAFB_TASKS_MAX) 22662306a36Sopenharmony_ci seq = 0; 22762306a36Sopenharmony_ciout: 22862306a36Sopenharmony_ci kfree(m); 22962306a36Sopenharmony_ci return err; 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* 23362306a36Sopenharmony_ci * Free a uvesafb_ktask struct. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_cistatic void uvesafb_free(struct uvesafb_ktask *task) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci if (task) { 23862306a36Sopenharmony_ci kfree(task->done); 23962306a36Sopenharmony_ci kfree(task); 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * Prepare a uvesafb_ktask struct to be used again. 24562306a36Sopenharmony_ci */ 24662306a36Sopenharmony_cistatic void uvesafb_reset(struct uvesafb_ktask *task) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct completion *cpl = task->done; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci memset(task, 0, sizeof(*task)); 25162306a36Sopenharmony_ci task->done = cpl; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci/* 25562306a36Sopenharmony_ci * Allocate and prepare a uvesafb_ktask struct. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_cistatic struct uvesafb_ktask *uvesafb_prep(void) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct uvesafb_ktask *task; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci task = kzalloc(sizeof(*task), GFP_KERNEL); 26262306a36Sopenharmony_ci if (task) { 26362306a36Sopenharmony_ci task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); 26462306a36Sopenharmony_ci if (!task->done) { 26562306a36Sopenharmony_ci kfree(task); 26662306a36Sopenharmony_ci task = NULL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci return task; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic void uvesafb_setup_var(struct fb_var_screeninfo *var, 27362306a36Sopenharmony_ci struct fb_info *info, struct vbe_mode_ib *mode) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 27862306a36Sopenharmony_ci var->sync = FB_SYNC_VERT_HIGH_ACT; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci var->xres = mode->x_res; 28162306a36Sopenharmony_ci var->yres = mode->y_res; 28262306a36Sopenharmony_ci var->xres_virtual = mode->x_res; 28362306a36Sopenharmony_ci var->yres_virtual = (par->ypan) ? 28462306a36Sopenharmony_ci info->fix.smem_len / mode->bytes_per_scan_line : 28562306a36Sopenharmony_ci mode->y_res; 28662306a36Sopenharmony_ci var->xoffset = 0; 28762306a36Sopenharmony_ci var->yoffset = 0; 28862306a36Sopenharmony_ci var->bits_per_pixel = mode->bits_per_pixel; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (var->bits_per_pixel == 15) 29162306a36Sopenharmony_ci var->bits_per_pixel = 16; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (var->bits_per_pixel > 8) { 29462306a36Sopenharmony_ci var->red.offset = mode->red_off; 29562306a36Sopenharmony_ci var->red.length = mode->red_len; 29662306a36Sopenharmony_ci var->green.offset = mode->green_off; 29762306a36Sopenharmony_ci var->green.length = mode->green_len; 29862306a36Sopenharmony_ci var->blue.offset = mode->blue_off; 29962306a36Sopenharmony_ci var->blue.length = mode->blue_len; 30062306a36Sopenharmony_ci var->transp.offset = mode->rsvd_off; 30162306a36Sopenharmony_ci var->transp.length = mode->rsvd_len; 30262306a36Sopenharmony_ci } else { 30362306a36Sopenharmony_ci var->red.offset = 0; 30462306a36Sopenharmony_ci var->green.offset = 0; 30562306a36Sopenharmony_ci var->blue.offset = 0; 30662306a36Sopenharmony_ci var->transp.offset = 0; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci var->red.length = 8; 30962306a36Sopenharmony_ci var->green.length = 8; 31062306a36Sopenharmony_ci var->blue.length = 8; 31162306a36Sopenharmony_ci var->transp.length = 0; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int uvesafb_vbe_find_mode(struct uvesafb_par *par, 31662306a36Sopenharmony_ci int xres, int yres, int depth, unsigned char flags) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci int i, match = -1, h = 0, d = 0x7fffffff; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 32162306a36Sopenharmony_ci h = abs(par->vbe_modes[i].x_res - xres) + 32262306a36Sopenharmony_ci abs(par->vbe_modes[i].y_res - yres) + 32362306a36Sopenharmony_ci abs(depth - par->vbe_modes[i].depth); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* 32662306a36Sopenharmony_ci * We have an exact match in terms of resolution 32762306a36Sopenharmony_ci * and depth. 32862306a36Sopenharmony_ci */ 32962306a36Sopenharmony_ci if (h == 0) 33062306a36Sopenharmony_ci return i; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (h < d || (h == d && par->vbe_modes[i].depth > depth)) { 33362306a36Sopenharmony_ci d = h; 33462306a36Sopenharmony_ci match = i; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci i = 1; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (flags & UVESAFB_EXACT_DEPTH && 34062306a36Sopenharmony_ci par->vbe_modes[match].depth != depth) 34162306a36Sopenharmony_ci i = 0; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (flags & UVESAFB_EXACT_RES && d > 24) 34462306a36Sopenharmony_ci i = 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (i != 0) 34762306a36Sopenharmony_ci return match; 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci return -1; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct uvesafb_ktask *task; 35562306a36Sopenharmony_ci u8 *state; 35662306a36Sopenharmony_ci int err; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!par->vbe_state_size) 35962306a36Sopenharmony_ci return NULL; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci state = kmalloc(par->vbe_state_size, GFP_KERNEL); 36262306a36Sopenharmony_ci if (!state) 36362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci task = uvesafb_prep(); 36662306a36Sopenharmony_ci if (!task) { 36762306a36Sopenharmony_ci kfree(state); 36862306a36Sopenharmony_ci return NULL; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci task->t.regs.eax = 0x4f04; 37262306a36Sopenharmony_ci task->t.regs.ecx = 0x000f; 37362306a36Sopenharmony_ci task->t.regs.edx = 0x0001; 37462306a36Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESBX; 37562306a36Sopenharmony_ci task->t.buf_len = par->vbe_state_size; 37662306a36Sopenharmony_ci task->buf = state; 37762306a36Sopenharmony_ci err = uvesafb_exec(task); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 38062306a36Sopenharmony_ci pr_warn("VBE get state call failed (eax=0x%x, err=%d)\n", 38162306a36Sopenharmony_ci task->t.regs.eax, err); 38262306a36Sopenharmony_ci kfree(state); 38362306a36Sopenharmony_ci state = NULL; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci uvesafb_free(task); 38762306a36Sopenharmony_ci return state; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct uvesafb_ktask *task; 39362306a36Sopenharmony_ci int err; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (!state_buf) 39662306a36Sopenharmony_ci return; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci task = uvesafb_prep(); 39962306a36Sopenharmony_ci if (!task) 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci task->t.regs.eax = 0x4f04; 40362306a36Sopenharmony_ci task->t.regs.ecx = 0x000f; 40462306a36Sopenharmony_ci task->t.regs.edx = 0x0002; 40562306a36Sopenharmony_ci task->t.buf_len = par->vbe_state_size; 40662306a36Sopenharmony_ci task->t.flags = TF_BUF_ESBX; 40762306a36Sopenharmony_ci task->buf = state_buf; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci err = uvesafb_exec(task); 41062306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) 41162306a36Sopenharmony_ci pr_warn("VBE state restore call failed (eax=0x%x, err=%d)\n", 41262306a36Sopenharmony_ci task->t.regs.eax, err); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci uvesafb_free(task); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int uvesafb_vbe_getinfo(struct uvesafb_ktask *task, 41862306a36Sopenharmony_ci struct uvesafb_par *par) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int err; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci task->t.regs.eax = 0x4f00; 42362306a36Sopenharmony_ci task->t.flags = TF_VBEIB; 42462306a36Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_ib); 42562306a36Sopenharmony_ci task->buf = &par->vbe_ib; 42662306a36Sopenharmony_ci memcpy(par->vbe_ib.vbe_signature, "VBE2", 4); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci err = uvesafb_exec(task); 42962306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 43062306a36Sopenharmony_ci pr_err("Getting VBE info block failed (eax=0x%x, err=%d)\n", 43162306a36Sopenharmony_ci (u32)task->t.regs.eax, err); 43262306a36Sopenharmony_ci return -EINVAL; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0200) { 43662306a36Sopenharmony_ci pr_err("Sorry, pre-VBE 2.0 cards are not supported\n"); 43762306a36Sopenharmony_ci return -EINVAL; 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!par->vbe_ib.mode_list_ptr) { 44162306a36Sopenharmony_ci pr_err("Missing mode list!\n"); 44262306a36Sopenharmony_ci return -EINVAL; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci pr_info(""); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* 44862306a36Sopenharmony_ci * Convert string pointers and the mode list pointer into 44962306a36Sopenharmony_ci * usable addresses. Print informational messages about the 45062306a36Sopenharmony_ci * video adapter and its vendor. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci if (par->vbe_ib.oem_vendor_name_ptr) 45362306a36Sopenharmony_ci pr_cont("%s, ", 45462306a36Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci if (par->vbe_ib.oem_product_name_ptr) 45762306a36Sopenharmony_ci pr_cont("%s, ", 45862306a36Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_product_name_ptr); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (par->vbe_ib.oem_product_rev_ptr) 46162306a36Sopenharmony_ci pr_cont("%s, ", 46262306a36Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (par->vbe_ib.oem_string_ptr) 46562306a36Sopenharmony_ci pr_cont("OEM: %s, ", 46662306a36Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_string_ptr); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci pr_cont("VBE v%d.%d\n", 46962306a36Sopenharmony_ci (par->vbe_ib.vbe_version & 0xff00) >> 8, 47062306a36Sopenharmony_ci par->vbe_ib.vbe_version & 0xff); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int uvesafb_vbe_getmodes(struct uvesafb_ktask *task, 47662306a36Sopenharmony_ci struct uvesafb_par *par) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci int off = 0, err; 47962306a36Sopenharmony_ci u16 *mode; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci par->vbe_modes_cnt = 0; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* Count available modes. */ 48462306a36Sopenharmony_ci mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); 48562306a36Sopenharmony_ci while (*mode != 0xffff) { 48662306a36Sopenharmony_ci par->vbe_modes_cnt++; 48762306a36Sopenharmony_ci mode++; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci par->vbe_modes = kcalloc(par->vbe_modes_cnt, 49162306a36Sopenharmony_ci sizeof(struct vbe_mode_ib), 49262306a36Sopenharmony_ci GFP_KERNEL); 49362306a36Sopenharmony_ci if (!par->vbe_modes) 49462306a36Sopenharmony_ci return -ENOMEM; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* Get info about all available modes. */ 49762306a36Sopenharmony_ci mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); 49862306a36Sopenharmony_ci while (*mode != 0xffff) { 49962306a36Sopenharmony_ci struct vbe_mode_ib *mib; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci uvesafb_reset(task); 50262306a36Sopenharmony_ci task->t.regs.eax = 0x4f01; 50362306a36Sopenharmony_ci task->t.regs.ecx = (u32) *mode; 50462306a36Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESDI; 50562306a36Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_mode_ib); 50662306a36Sopenharmony_ci task->buf = par->vbe_modes + off; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci err = uvesafb_exec(task); 50962306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 51062306a36Sopenharmony_ci pr_warn("Getting mode info block for mode 0x%x failed (eax=0x%x, err=%d)\n", 51162306a36Sopenharmony_ci *mode, (u32)task->t.regs.eax, err); 51262306a36Sopenharmony_ci mode++; 51362306a36Sopenharmony_ci par->vbe_modes_cnt--; 51462306a36Sopenharmony_ci continue; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci mib = task->buf; 51862306a36Sopenharmony_ci mib->mode_id = *mode; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* 52162306a36Sopenharmony_ci * We only want modes that are supported with the current 52262306a36Sopenharmony_ci * hardware configuration, color, graphics and that have 52362306a36Sopenharmony_ci * support for the LFB. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK && 52662306a36Sopenharmony_ci mib->bits_per_pixel >= 8) 52762306a36Sopenharmony_ci off++; 52862306a36Sopenharmony_ci else 52962306a36Sopenharmony_ci par->vbe_modes_cnt--; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci mode++; 53262306a36Sopenharmony_ci mib->depth = mib->red_len + mib->green_len + mib->blue_len; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* 53562306a36Sopenharmony_ci * Handle 8bpp modes and modes with broken color component 53662306a36Sopenharmony_ci * lengths. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (mib->depth == 0 || (mib->depth == 24 && 53962306a36Sopenharmony_ci mib->bits_per_pixel == 32)) 54062306a36Sopenharmony_ci mib->depth = mib->bits_per_pixel; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (par->vbe_modes_cnt > 0) 54462306a36Sopenharmony_ci return 0; 54562306a36Sopenharmony_ci else 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * The Protected Mode Interface is 32-bit x86 code, so we only run it on 55162306a36Sopenharmony_ci * x86 and not x86_64. 55262306a36Sopenharmony_ci */ 55362306a36Sopenharmony_ci#ifdef CONFIG_X86_32 55462306a36Sopenharmony_cistatic int uvesafb_vbe_getpmi(struct uvesafb_ktask *task, 55562306a36Sopenharmony_ci struct uvesafb_par *par) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci int i, err; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci uvesafb_reset(task); 56062306a36Sopenharmony_ci task->t.regs.eax = 0x4f0a; 56162306a36Sopenharmony_ci task->t.regs.ebx = 0x0; 56262306a36Sopenharmony_ci err = uvesafb_exec(task); 56362306a36Sopenharmony_ci if (err) 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x4f || task->t.regs.es < 0xc000) { 56762306a36Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 56862306a36Sopenharmony_ci } else { 56962306a36Sopenharmony_ci par->pmi_base = (u16 *)phys_to_virt(((u32)task->t.regs.es << 4) 57062306a36Sopenharmony_ci + task->t.regs.edi); 57162306a36Sopenharmony_ci par->pmi_start = (u8 *)par->pmi_base + par->pmi_base[1]; 57262306a36Sopenharmony_ci par->pmi_pal = (u8 *)par->pmi_base + par->pmi_base[2]; 57362306a36Sopenharmony_ci pr_info("protected mode interface info at %04x:%04x\n", 57462306a36Sopenharmony_ci (u16)task->t.regs.es, (u16)task->t.regs.edi); 57562306a36Sopenharmony_ci pr_info("pmi: set display start = %p, set palette = %p\n", 57662306a36Sopenharmony_ci par->pmi_start, par->pmi_pal); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (par->pmi_base[3]) { 57962306a36Sopenharmony_ci pr_info("pmi: ports ="); 58062306a36Sopenharmony_ci for (i = par->pmi_base[3]/2; 58162306a36Sopenharmony_ci par->pmi_base[i] != 0xffff; i++) 58262306a36Sopenharmony_ci pr_cont(" %x", par->pmi_base[i]); 58362306a36Sopenharmony_ci pr_cont("\n"); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (par->pmi_base[i] != 0xffff) { 58662306a36Sopenharmony_ci pr_info("can't handle memory requests, pmi disabled\n"); 58762306a36Sopenharmony_ci par->ypan = par->pmi_setpal = 0; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci return 0; 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci#endif /* CONFIG_X86_32 */ 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci/* 59662306a36Sopenharmony_ci * Check whether a video mode is supported by the Video BIOS and is 59762306a36Sopenharmony_ci * compatible with the monitor limits. 59862306a36Sopenharmony_ci */ 59962306a36Sopenharmony_cistatic int uvesafb_is_valid_mode(struct fb_videomode *mode, 60062306a36Sopenharmony_ci struct fb_info *info) 60162306a36Sopenharmony_ci{ 60262306a36Sopenharmony_ci if (info->monspecs.gtf) { 60362306a36Sopenharmony_ci fb_videomode_to_var(&info->var, mode); 60462306a36Sopenharmony_ci if (fb_validate_mode(&info->var, info)) 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8, 60962306a36Sopenharmony_ci UVESAFB_EXACT_RES) == -1) 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return 1; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic int uvesafb_vbe_getedid(struct uvesafb_ktask *task, struct fb_info *info) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 61862306a36Sopenharmony_ci int err = 0; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (noedid || par->vbe_ib.vbe_version < 0x0300) 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci task->t.regs.eax = 0x4f15; 62462306a36Sopenharmony_ci task->t.regs.ebx = 0; 62562306a36Sopenharmony_ci task->t.regs.ecx = 0; 62662306a36Sopenharmony_ci task->t.buf_len = 0; 62762306a36Sopenharmony_ci task->t.flags = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci err = uvesafb_exec(task); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x004f || err) 63262306a36Sopenharmony_ci return -EINVAL; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if ((task->t.regs.ebx & 0x3) == 3) { 63562306a36Sopenharmony_ci pr_info("VBIOS/hardware supports both DDC1 and DDC2 transfers\n"); 63662306a36Sopenharmony_ci } else if ((task->t.regs.ebx & 0x3) == 2) { 63762306a36Sopenharmony_ci pr_info("VBIOS/hardware supports DDC2 transfers\n"); 63862306a36Sopenharmony_ci } else if ((task->t.regs.ebx & 0x3) == 1) { 63962306a36Sopenharmony_ci pr_info("VBIOS/hardware supports DDC1 transfers\n"); 64062306a36Sopenharmony_ci } else { 64162306a36Sopenharmony_ci pr_info("VBIOS/hardware doesn't support DDC transfers\n"); 64262306a36Sopenharmony_ci return -EINVAL; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci task->t.regs.eax = 0x4f15; 64662306a36Sopenharmony_ci task->t.regs.ebx = 1; 64762306a36Sopenharmony_ci task->t.regs.ecx = task->t.regs.edx = 0; 64862306a36Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESDI; 64962306a36Sopenharmony_ci task->t.buf_len = EDID_LENGTH; 65062306a36Sopenharmony_ci task->buf = kzalloc(EDID_LENGTH, GFP_KERNEL); 65162306a36Sopenharmony_ci if (!task->buf) 65262306a36Sopenharmony_ci return -ENOMEM; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci err = uvesafb_exec(task); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if ((task->t.regs.eax & 0xffff) == 0x004f && !err) { 65762306a36Sopenharmony_ci fb_edid_to_monspecs(task->buf, &info->monspecs); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci if (info->monspecs.vfmax && info->monspecs.hfmax) { 66062306a36Sopenharmony_ci /* 66162306a36Sopenharmony_ci * If the maximum pixel clock wasn't specified in 66262306a36Sopenharmony_ci * the EDID block, set it to 300 MHz. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci if (info->monspecs.dclkmax == 0) 66562306a36Sopenharmony_ci info->monspecs.dclkmax = 300 * 1000000; 66662306a36Sopenharmony_ci info->monspecs.gtf = 1; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci } else { 66962306a36Sopenharmony_ci err = -EINVAL; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci kfree(task->buf); 67362306a36Sopenharmony_ci return err; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task, 67762306a36Sopenharmony_ci struct fb_info *info) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 68062306a36Sopenharmony_ci int i; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci memset(&info->monspecs, 0, sizeof(info->monspecs)); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* 68562306a36Sopenharmony_ci * If we don't get all necessary data from the EDID block, 68662306a36Sopenharmony_ci * mark it as incompatible with the GTF and set nocrtc so 68762306a36Sopenharmony_ci * that we always use the default BIOS refresh rate. 68862306a36Sopenharmony_ci */ 68962306a36Sopenharmony_ci if (uvesafb_vbe_getedid(task, info)) { 69062306a36Sopenharmony_ci info->monspecs.gtf = 0; 69162306a36Sopenharmony_ci par->nocrtc = 1; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci /* Kernel command line overrides. */ 69562306a36Sopenharmony_ci if (maxclk) 69662306a36Sopenharmony_ci info->monspecs.dclkmax = maxclk * 1000000; 69762306a36Sopenharmony_ci if (maxvf) 69862306a36Sopenharmony_ci info->monspecs.vfmax = maxvf; 69962306a36Sopenharmony_ci if (maxhf) 70062306a36Sopenharmony_ci info->monspecs.hfmax = maxhf * 1000; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* 70362306a36Sopenharmony_ci * In case DDC transfers are not supported, the user can provide 70462306a36Sopenharmony_ci * monitor limits manually. Lower limits are set to "safe" values. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_ci if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) { 70762306a36Sopenharmony_ci info->monspecs.dclkmin = 0; 70862306a36Sopenharmony_ci info->monspecs.vfmin = 60; 70962306a36Sopenharmony_ci info->monspecs.hfmin = 29000; 71062306a36Sopenharmony_ci info->monspecs.gtf = 1; 71162306a36Sopenharmony_ci par->nocrtc = 0; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (info->monspecs.gtf) 71562306a36Sopenharmony_ci pr_info("monitor limits: vf = %d Hz, hf = %d kHz, clk = %d MHz\n", 71662306a36Sopenharmony_ci info->monspecs.vfmax, 71762306a36Sopenharmony_ci (int)(info->monspecs.hfmax / 1000), 71862306a36Sopenharmony_ci (int)(info->monspecs.dclkmax / 1000000)); 71962306a36Sopenharmony_ci else 72062306a36Sopenharmony_ci pr_info("no monitor limits have been set, default refresh rate will be used\n"); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Add VBE modes to the modelist. */ 72362306a36Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 72462306a36Sopenharmony_ci struct fb_var_screeninfo var; 72562306a36Sopenharmony_ci struct vbe_mode_ib *mode; 72662306a36Sopenharmony_ci struct fb_videomode vmode; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci mode = &par->vbe_modes[i]; 72962306a36Sopenharmony_ci memset(&var, 0, sizeof(var)); 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci var.xres = mode->x_res; 73262306a36Sopenharmony_ci var.yres = mode->y_res; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, &var, info); 73562306a36Sopenharmony_ci fb_var_to_videomode(&vmode, &var); 73662306a36Sopenharmony_ci fb_add_videomode(&vmode, &info->modelist); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* Add valid VESA modes to our modelist. */ 74062306a36Sopenharmony_ci for (i = 0; i < VESA_MODEDB_SIZE; i++) { 74162306a36Sopenharmony_ci if (uvesafb_is_valid_mode((struct fb_videomode *) 74262306a36Sopenharmony_ci &vesa_modes[i], info)) 74362306a36Sopenharmony_ci fb_add_videomode(&vesa_modes[i], &info->modelist); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci for (i = 0; i < info->monspecs.modedb_len; i++) { 74762306a36Sopenharmony_ci if (uvesafb_is_valid_mode(&info->monspecs.modedb[i], info)) 74862306a36Sopenharmony_ci fb_add_videomode(&info->monspecs.modedb[i], 74962306a36Sopenharmony_ci &info->modelist); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void uvesafb_vbe_getstatesize(struct uvesafb_ktask *task, 75662306a36Sopenharmony_ci struct uvesafb_par *par) 75762306a36Sopenharmony_ci{ 75862306a36Sopenharmony_ci int err; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci uvesafb_reset(task); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* 76362306a36Sopenharmony_ci * Get the VBE state buffer size. We want all available 76462306a36Sopenharmony_ci * hardware state data (CL = 0x0f). 76562306a36Sopenharmony_ci */ 76662306a36Sopenharmony_ci task->t.regs.eax = 0x4f04; 76762306a36Sopenharmony_ci task->t.regs.ecx = 0x000f; 76862306a36Sopenharmony_ci task->t.regs.edx = 0x0000; 76962306a36Sopenharmony_ci task->t.flags = 0; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci err = uvesafb_exec(task); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 77462306a36Sopenharmony_ci pr_warn("VBE state buffer size cannot be determined (eax=0x%x, err=%d)\n", 77562306a36Sopenharmony_ci task->t.regs.eax, err); 77662306a36Sopenharmony_ci par->vbe_state_size = 0; 77762306a36Sopenharmony_ci return; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci par->vbe_state_size = 64 * (task->t.regs.ebx & 0xffff); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int uvesafb_vbe_init(struct fb_info *info) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci struct uvesafb_ktask *task = NULL; 78662306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 78762306a36Sopenharmony_ci int err; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci task = uvesafb_prep(); 79062306a36Sopenharmony_ci if (!task) 79162306a36Sopenharmony_ci return -ENOMEM; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci err = uvesafb_vbe_getinfo(task, par); 79462306a36Sopenharmony_ci if (err) 79562306a36Sopenharmony_ci goto out; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci err = uvesafb_vbe_getmodes(task, par); 79862306a36Sopenharmony_ci if (err) 79962306a36Sopenharmony_ci goto out; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci par->nocrtc = nocrtc; 80262306a36Sopenharmony_ci#ifdef CONFIG_X86_32 80362306a36Sopenharmony_ci par->pmi_setpal = pmi_setpal; 80462306a36Sopenharmony_ci par->ypan = ypan; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (par->pmi_setpal || par->ypan) { 80762306a36Sopenharmony_ci if (__supported_pte_mask & _PAGE_NX) { 80862306a36Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 80962306a36Sopenharmony_ci pr_warn("NX protection is active, better not use the PMI\n"); 81062306a36Sopenharmony_ci } else { 81162306a36Sopenharmony_ci uvesafb_vbe_getpmi(task, par); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci#else 81562306a36Sopenharmony_ci /* The protected mode interface is not available on non-x86. */ 81662306a36Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 81762306a36Sopenharmony_ci#endif 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 82062306a36Sopenharmony_ci uvesafb_vbe_getmonspecs(task, info); 82162306a36Sopenharmony_ci uvesafb_vbe_getstatesize(task, par); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ciout: uvesafb_free(task); 82462306a36Sopenharmony_ci return err; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int uvesafb_vbe_init_mode(struct fb_info *info) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct list_head *pos; 83062306a36Sopenharmony_ci struct fb_modelist *modelist; 83162306a36Sopenharmony_ci struct fb_videomode *mode; 83262306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 83362306a36Sopenharmony_ci int i, modeid; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Has the user requested a specific VESA mode? */ 83662306a36Sopenharmony_ci if (vbemode) { 83762306a36Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 83862306a36Sopenharmony_ci if (par->vbe_modes[i].mode_id == vbemode) { 83962306a36Sopenharmony_ci modeid = i; 84062306a36Sopenharmony_ci uvesafb_setup_var(&info->var, info, 84162306a36Sopenharmony_ci &par->vbe_modes[modeid]); 84262306a36Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 84362306a36Sopenharmony_ci &info->var, info); 84462306a36Sopenharmony_ci /* 84562306a36Sopenharmony_ci * With pixclock set to 0, the default BIOS 84662306a36Sopenharmony_ci * timings will be used in set_par(). 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_ci info->var.pixclock = 0; 84962306a36Sopenharmony_ci goto gotmode; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci pr_info("requested VBE mode 0x%x is unavailable\n", vbemode); 85362306a36Sopenharmony_ci vbemode = 0; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci /* Count the modes in the modelist */ 85762306a36Sopenharmony_ci i = 0; 85862306a36Sopenharmony_ci list_for_each(pos, &info->modelist) 85962306a36Sopenharmony_ci i++; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* 86262306a36Sopenharmony_ci * Convert the modelist into a modedb so that we can use it with 86362306a36Sopenharmony_ci * fb_find_mode(). 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci mode = kcalloc(i, sizeof(*mode), GFP_KERNEL); 86662306a36Sopenharmony_ci if (mode) { 86762306a36Sopenharmony_ci i = 0; 86862306a36Sopenharmony_ci list_for_each(pos, &info->modelist) { 86962306a36Sopenharmony_ci modelist = list_entry(pos, struct fb_modelist, list); 87062306a36Sopenharmony_ci mode[i] = modelist->mode; 87162306a36Sopenharmony_ci i++; 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci if (!mode_option) 87562306a36Sopenharmony_ci mode_option = UVESAFB_DEFAULT_MODE; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci i = fb_find_mode(&info->var, info, mode_option, mode, i, 87862306a36Sopenharmony_ci NULL, 8); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci kfree(mode); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* fb_find_mode() failed */ 88462306a36Sopenharmony_ci if (i == 0) { 88562306a36Sopenharmony_ci info->var.xres = 640; 88662306a36Sopenharmony_ci info->var.yres = 480; 88762306a36Sopenharmony_ci mode = (struct fb_videomode *) 88862306a36Sopenharmony_ci fb_find_best_mode(&info->var, &info->modelist); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci if (mode) { 89162306a36Sopenharmony_ci fb_videomode_to_var(&info->var, mode); 89262306a36Sopenharmony_ci } else { 89362306a36Sopenharmony_ci modeid = par->vbe_modes[0].mode_id; 89462306a36Sopenharmony_ci uvesafb_setup_var(&info->var, info, 89562306a36Sopenharmony_ci &par->vbe_modes[modeid]); 89662306a36Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 89762306a36Sopenharmony_ci &info->var, info); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci goto gotmode; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* Look for a matching VBE mode. */ 90462306a36Sopenharmony_ci modeid = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, 90562306a36Sopenharmony_ci info->var.bits_per_pixel, UVESAFB_EXACT_RES); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (modeid == -1) 90862306a36Sopenharmony_ci return -EINVAL; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cigotmode: 91362306a36Sopenharmony_ci /* 91462306a36Sopenharmony_ci * If we are not VBE3.0+ compliant, we're done -- the BIOS will 91562306a36Sopenharmony_ci * ignore our timings anyway. 91662306a36Sopenharmony_ci */ 91762306a36Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0300 || par->nocrtc) 91862306a36Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 91962306a36Sopenharmony_ci &info->var, info); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return modeid; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count, 92562306a36Sopenharmony_ci int start, struct fb_info *info) 92662306a36Sopenharmony_ci{ 92762306a36Sopenharmony_ci struct uvesafb_ktask *task; 92862306a36Sopenharmony_ci#ifdef CONFIG_X86 92962306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 93062306a36Sopenharmony_ci int i = par->mode_idx; 93162306a36Sopenharmony_ci#endif 93262306a36Sopenharmony_ci int err = 0; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* 93562306a36Sopenharmony_ci * We support palette modifications for 8 bpp modes only, so 93662306a36Sopenharmony_ci * there can never be more than 256 entries. 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_ci if (start + count > 256) 93962306a36Sopenharmony_ci return -EINVAL; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci#ifdef CONFIG_X86 94262306a36Sopenharmony_ci /* Use VGA registers if mode is VGA-compatible. */ 94362306a36Sopenharmony_ci if (i >= 0 && i < par->vbe_modes_cnt && 94462306a36Sopenharmony_ci par->vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) { 94562306a36Sopenharmony_ci for (i = 0; i < count; i++) { 94662306a36Sopenharmony_ci outb_p(start + i, dac_reg); 94762306a36Sopenharmony_ci outb_p(entries[i].red, dac_val); 94862306a36Sopenharmony_ci outb_p(entries[i].green, dac_val); 94962306a36Sopenharmony_ci outb_p(entries[i].blue, dac_val); 95062306a36Sopenharmony_ci } 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci#ifdef CONFIG_X86_32 95362306a36Sopenharmony_ci else if (par->pmi_setpal) { 95462306a36Sopenharmony_ci __asm__ __volatile__( 95562306a36Sopenharmony_ci "call *(%%esi)" 95662306a36Sopenharmony_ci : /* no return value */ 95762306a36Sopenharmony_ci : "a" (0x4f09), /* EAX */ 95862306a36Sopenharmony_ci "b" (0), /* EBX */ 95962306a36Sopenharmony_ci "c" (count), /* ECX */ 96062306a36Sopenharmony_ci "d" (start), /* EDX */ 96162306a36Sopenharmony_ci "D" (entries), /* EDI */ 96262306a36Sopenharmony_ci "S" (&par->pmi_pal)); /* ESI */ 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci#endif /* CONFIG_X86_32 */ 96562306a36Sopenharmony_ci else 96662306a36Sopenharmony_ci#endif /* CONFIG_X86 */ 96762306a36Sopenharmony_ci { 96862306a36Sopenharmony_ci task = uvesafb_prep(); 96962306a36Sopenharmony_ci if (!task) 97062306a36Sopenharmony_ci return -ENOMEM; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci task->t.regs.eax = 0x4f09; 97362306a36Sopenharmony_ci task->t.regs.ebx = 0x0; 97462306a36Sopenharmony_ci task->t.regs.ecx = count; 97562306a36Sopenharmony_ci task->t.regs.edx = start; 97662306a36Sopenharmony_ci task->t.flags = TF_BUF_ESDI; 97762306a36Sopenharmony_ci task->t.buf_len = sizeof(struct uvesafb_pal_entry) * count; 97862306a36Sopenharmony_ci task->buf = entries; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci err = uvesafb_exec(task); 98162306a36Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x004f) 98262306a36Sopenharmony_ci err = 1; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci uvesafb_free(task); 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci return err; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green, 99062306a36Sopenharmony_ci unsigned blue, unsigned transp, 99162306a36Sopenharmony_ci struct fb_info *info) 99262306a36Sopenharmony_ci{ 99362306a36Sopenharmony_ci struct uvesafb_pal_entry entry; 99462306a36Sopenharmony_ci int shift = 16 - dac_width; 99562306a36Sopenharmony_ci int err = 0; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (regno >= info->cmap.len) 99862306a36Sopenharmony_ci return -EINVAL; 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci if (info->var.bits_per_pixel == 8) { 100162306a36Sopenharmony_ci entry.red = red >> shift; 100262306a36Sopenharmony_ci entry.green = green >> shift; 100362306a36Sopenharmony_ci entry.blue = blue >> shift; 100462306a36Sopenharmony_ci entry.pad = 0; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci err = uvesafb_setpalette(&entry, 1, regno, info); 100762306a36Sopenharmony_ci } else if (regno < 16) { 100862306a36Sopenharmony_ci switch (info->var.bits_per_pixel) { 100962306a36Sopenharmony_ci case 16: 101062306a36Sopenharmony_ci if (info->var.red.offset == 10) { 101162306a36Sopenharmony_ci /* 1:5:5:5 */ 101262306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 101362306a36Sopenharmony_ci ((red & 0xf800) >> 1) | 101462306a36Sopenharmony_ci ((green & 0xf800) >> 6) | 101562306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 101662306a36Sopenharmony_ci } else { 101762306a36Sopenharmony_ci /* 0:5:6:5 */ 101862306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 101962306a36Sopenharmony_ci ((red & 0xf800) ) | 102062306a36Sopenharmony_ci ((green & 0xfc00) >> 5) | 102162306a36Sopenharmony_ci ((blue & 0xf800) >> 11); 102262306a36Sopenharmony_ci } 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci case 24: 102662306a36Sopenharmony_ci case 32: 102762306a36Sopenharmony_ci red >>= 8; 102862306a36Sopenharmony_ci green >>= 8; 102962306a36Sopenharmony_ci blue >>= 8; 103062306a36Sopenharmony_ci ((u32 *)(info->pseudo_palette))[regno] = 103162306a36Sopenharmony_ci (red << info->var.red.offset) | 103262306a36Sopenharmony_ci (green << info->var.green.offset) | 103362306a36Sopenharmony_ci (blue << info->var.blue.offset); 103462306a36Sopenharmony_ci break; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci return err; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci struct uvesafb_pal_entry *entries; 104362306a36Sopenharmony_ci int shift = 16 - dac_width; 104462306a36Sopenharmony_ci int i, err = 0; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (info->var.bits_per_pixel == 8) { 104762306a36Sopenharmony_ci if (cmap->start + cmap->len > info->cmap.start + 104862306a36Sopenharmony_ci info->cmap.len || cmap->start < info->cmap.start) 104962306a36Sopenharmony_ci return -EINVAL; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci entries = kmalloc_array(cmap->len, sizeof(*entries), 105262306a36Sopenharmony_ci GFP_KERNEL); 105362306a36Sopenharmony_ci if (!entries) 105462306a36Sopenharmony_ci return -ENOMEM; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci for (i = 0; i < cmap->len; i++) { 105762306a36Sopenharmony_ci entries[i].red = cmap->red[i] >> shift; 105862306a36Sopenharmony_ci entries[i].green = cmap->green[i] >> shift; 105962306a36Sopenharmony_ci entries[i].blue = cmap->blue[i] >> shift; 106062306a36Sopenharmony_ci entries[i].pad = 0; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci err = uvesafb_setpalette(entries, cmap->len, cmap->start, info); 106362306a36Sopenharmony_ci kfree(entries); 106462306a36Sopenharmony_ci } else { 106562306a36Sopenharmony_ci /* 106662306a36Sopenharmony_ci * For modes with bpp > 8, we only set the pseudo palette in 106762306a36Sopenharmony_ci * the fb_info struct. We rely on uvesafb_setcolreg to do all 106862306a36Sopenharmony_ci * sanity checking. 106962306a36Sopenharmony_ci */ 107062306a36Sopenharmony_ci for (i = 0; i < cmap->len; i++) { 107162306a36Sopenharmony_ci err |= uvesafb_setcolreg(cmap->start + i, cmap->red[i], 107262306a36Sopenharmony_ci cmap->green[i], cmap->blue[i], 107362306a36Sopenharmony_ci 0, info); 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci return err; 107762306a36Sopenharmony_ci} 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_cistatic int uvesafb_pan_display(struct fb_var_screeninfo *var, 108062306a36Sopenharmony_ci struct fb_info *info) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci#ifdef CONFIG_X86_32 108362306a36Sopenharmony_ci int offset; 108462306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* 108962306a36Sopenharmony_ci * It turns out it's not the best idea to do panning via vm86, 109062306a36Sopenharmony_ci * so we only allow it if we have a PMI. 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci if (par->pmi_start) { 109362306a36Sopenharmony_ci __asm__ __volatile__( 109462306a36Sopenharmony_ci "call *(%%edi)" 109562306a36Sopenharmony_ci : /* no return value */ 109662306a36Sopenharmony_ci : "a" (0x4f07), /* EAX */ 109762306a36Sopenharmony_ci "b" (0), /* EBX */ 109862306a36Sopenharmony_ci "c" (offset), /* ECX */ 109962306a36Sopenharmony_ci "d" (offset >> 16), /* EDX */ 110062306a36Sopenharmony_ci "D" (&par->pmi_start)); /* EDI */ 110162306a36Sopenharmony_ci } 110262306a36Sopenharmony_ci#endif 110362306a36Sopenharmony_ci return 0; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic int uvesafb_blank(int blank, struct fb_info *info) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci struct uvesafb_ktask *task; 110962306a36Sopenharmony_ci int err = 1; 111062306a36Sopenharmony_ci#ifdef CONFIG_X86 111162306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 111262306a36Sopenharmony_ci 111362306a36Sopenharmony_ci if (par->vbe_ib.capabilities & VBE_CAP_VGACOMPAT) { 111462306a36Sopenharmony_ci int loop = 10000; 111562306a36Sopenharmony_ci u8 seq = 0, crtc17 = 0; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (blank == FB_BLANK_POWERDOWN) { 111862306a36Sopenharmony_ci seq = 0x20; 111962306a36Sopenharmony_ci crtc17 = 0x00; 112062306a36Sopenharmony_ci err = 0; 112162306a36Sopenharmony_ci } else { 112262306a36Sopenharmony_ci seq = 0x00; 112362306a36Sopenharmony_ci crtc17 = 0x80; 112462306a36Sopenharmony_ci err = (blank == FB_BLANK_UNBLANK) ? 0 : -EINVAL; 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci vga_wseq(NULL, 0x00, 0x01); 112862306a36Sopenharmony_ci seq |= vga_rseq(NULL, 0x01) & ~0x20; 112962306a36Sopenharmony_ci vga_wseq(NULL, 0x00, seq); 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci crtc17 |= vga_rcrt(NULL, 0x17) & ~0x80; 113262306a36Sopenharmony_ci while (loop--); 113362306a36Sopenharmony_ci vga_wcrt(NULL, 0x17, crtc17); 113462306a36Sopenharmony_ci vga_wseq(NULL, 0x00, 0x03); 113562306a36Sopenharmony_ci } else 113662306a36Sopenharmony_ci#endif /* CONFIG_X86 */ 113762306a36Sopenharmony_ci { 113862306a36Sopenharmony_ci task = uvesafb_prep(); 113962306a36Sopenharmony_ci if (!task) 114062306a36Sopenharmony_ci return -ENOMEM; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci task->t.regs.eax = 0x4f10; 114362306a36Sopenharmony_ci switch (blank) { 114462306a36Sopenharmony_ci case FB_BLANK_UNBLANK: 114562306a36Sopenharmony_ci task->t.regs.ebx = 0x0001; 114662306a36Sopenharmony_ci break; 114762306a36Sopenharmony_ci case FB_BLANK_NORMAL: 114862306a36Sopenharmony_ci task->t.regs.ebx = 0x0101; /* standby */ 114962306a36Sopenharmony_ci break; 115062306a36Sopenharmony_ci case FB_BLANK_POWERDOWN: 115162306a36Sopenharmony_ci task->t.regs.ebx = 0x0401; /* powerdown */ 115262306a36Sopenharmony_ci break; 115362306a36Sopenharmony_ci default: 115462306a36Sopenharmony_ci goto out; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci err = uvesafb_exec(task); 115862306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) 115962306a36Sopenharmony_ci err = 1; 116062306a36Sopenharmony_ciout: uvesafb_free(task); 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci return err; 116362306a36Sopenharmony_ci} 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_cistatic int uvesafb_open(struct fb_info *info, int user) 116662306a36Sopenharmony_ci{ 116762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 116862306a36Sopenharmony_ci int cnt = atomic_read(&par->ref_count); 116962306a36Sopenharmony_ci u8 *buf = NULL; 117062306a36Sopenharmony_ci 117162306a36Sopenharmony_ci if (!cnt && par->vbe_state_size) { 117262306a36Sopenharmony_ci buf = uvesafb_vbe_state_save(par); 117362306a36Sopenharmony_ci if (IS_ERR(buf)) { 117462306a36Sopenharmony_ci pr_warn("save hardware state failed, error code is %ld!\n", 117562306a36Sopenharmony_ci PTR_ERR(buf)); 117662306a36Sopenharmony_ci } else { 117762306a36Sopenharmony_ci par->vbe_state_orig = buf; 117862306a36Sopenharmony_ci } 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci atomic_inc(&par->ref_count); 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int uvesafb_release(struct fb_info *info, int user) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct uvesafb_ktask *task = NULL; 118862306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 118962306a36Sopenharmony_ci int cnt = atomic_read(&par->ref_count); 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (!cnt) 119262306a36Sopenharmony_ci return -EINVAL; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci if (cnt != 1) 119562306a36Sopenharmony_ci goto out; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci task = uvesafb_prep(); 119862306a36Sopenharmony_ci if (!task) 119962306a36Sopenharmony_ci goto out; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci /* First, try to set the standard 80x25 text mode. */ 120262306a36Sopenharmony_ci task->t.regs.eax = 0x0003; 120362306a36Sopenharmony_ci uvesafb_exec(task); 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* 120662306a36Sopenharmony_ci * Now try to restore whatever hardware state we might have 120762306a36Sopenharmony_ci * saved when the fb device was first opened. 120862306a36Sopenharmony_ci */ 120962306a36Sopenharmony_ci uvesafb_vbe_state_restore(par, par->vbe_state_orig); 121062306a36Sopenharmony_ciout: 121162306a36Sopenharmony_ci atomic_dec(&par->ref_count); 121262306a36Sopenharmony_ci uvesafb_free(task); 121362306a36Sopenharmony_ci return 0; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int uvesafb_set_par(struct fb_info *info) 121762306a36Sopenharmony_ci{ 121862306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 121962306a36Sopenharmony_ci struct uvesafb_ktask *task = NULL; 122062306a36Sopenharmony_ci struct vbe_crtc_ib *crtc = NULL; 122162306a36Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 122262306a36Sopenharmony_ci int i, err = 0, depth = info->var.bits_per_pixel; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (depth > 8 && depth != 32) 122562306a36Sopenharmony_ci depth = info->var.red.length + info->var.green.length + 122662306a36Sopenharmony_ci info->var.blue.length; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci i = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, depth, 122962306a36Sopenharmony_ci UVESAFB_EXACT_RES | UVESAFB_EXACT_DEPTH); 123062306a36Sopenharmony_ci if (i >= 0) 123162306a36Sopenharmony_ci mode = &par->vbe_modes[i]; 123262306a36Sopenharmony_ci else 123362306a36Sopenharmony_ci return -EINVAL; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci task = uvesafb_prep(); 123662306a36Sopenharmony_ci if (!task) 123762306a36Sopenharmony_ci return -ENOMEM; 123862306a36Sopenharmony_cisetmode: 123962306a36Sopenharmony_ci task->t.regs.eax = 0x4f02; 124062306a36Sopenharmony_ci task->t.regs.ebx = mode->mode_id | 0x4000; /* use LFB */ 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (par->vbe_ib.vbe_version >= 0x0300 && !par->nocrtc && 124362306a36Sopenharmony_ci info->var.pixclock != 0) { 124462306a36Sopenharmony_ci task->t.regs.ebx |= 0x0800; /* use CRTC data */ 124562306a36Sopenharmony_ci task->t.flags = TF_BUF_ESDI; 124662306a36Sopenharmony_ci crtc = kzalloc(sizeof(struct vbe_crtc_ib), GFP_KERNEL); 124762306a36Sopenharmony_ci if (!crtc) { 124862306a36Sopenharmony_ci err = -ENOMEM; 124962306a36Sopenharmony_ci goto out; 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci crtc->horiz_start = info->var.xres + info->var.right_margin; 125262306a36Sopenharmony_ci crtc->horiz_end = crtc->horiz_start + info->var.hsync_len; 125362306a36Sopenharmony_ci crtc->horiz_total = crtc->horiz_end + info->var.left_margin; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci crtc->vert_start = info->var.yres + info->var.lower_margin; 125662306a36Sopenharmony_ci crtc->vert_end = crtc->vert_start + info->var.vsync_len; 125762306a36Sopenharmony_ci crtc->vert_total = crtc->vert_end + info->var.upper_margin; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci crtc->pixel_clock = PICOS2KHZ(info->var.pixclock) * 1000; 126062306a36Sopenharmony_ci crtc->refresh_rate = (u16)(100 * (crtc->pixel_clock / 126162306a36Sopenharmony_ci (crtc->vert_total * crtc->horiz_total))); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci if (info->var.vmode & FB_VMODE_DOUBLE) 126462306a36Sopenharmony_ci crtc->flags |= 0x1; 126562306a36Sopenharmony_ci if (info->var.vmode & FB_VMODE_INTERLACED) 126662306a36Sopenharmony_ci crtc->flags |= 0x2; 126762306a36Sopenharmony_ci if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) 126862306a36Sopenharmony_ci crtc->flags |= 0x4; 126962306a36Sopenharmony_ci if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) 127062306a36Sopenharmony_ci crtc->flags |= 0x8; 127162306a36Sopenharmony_ci memcpy(&par->crtc, crtc, sizeof(*crtc)); 127262306a36Sopenharmony_ci } else { 127362306a36Sopenharmony_ci memset(&par->crtc, 0, sizeof(*crtc)); 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_crtc_ib); 127762306a36Sopenharmony_ci task->buf = &par->crtc; 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci err = uvesafb_exec(task); 128062306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 128162306a36Sopenharmony_ci /* 128262306a36Sopenharmony_ci * The mode switch might have failed because we tried to 128362306a36Sopenharmony_ci * use our own timings. Try again with the default timings. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci if (crtc != NULL) { 128662306a36Sopenharmony_ci pr_warn("mode switch failed (eax=0x%x, err=%d) - trying again with default timings\n", 128762306a36Sopenharmony_ci task->t.regs.eax, err); 128862306a36Sopenharmony_ci uvesafb_reset(task); 128962306a36Sopenharmony_ci kfree(crtc); 129062306a36Sopenharmony_ci crtc = NULL; 129162306a36Sopenharmony_ci info->var.pixclock = 0; 129262306a36Sopenharmony_ci goto setmode; 129362306a36Sopenharmony_ci } else { 129462306a36Sopenharmony_ci pr_err("mode switch failed (eax=0x%x, err=%d)\n", 129562306a36Sopenharmony_ci task->t.regs.eax, err); 129662306a36Sopenharmony_ci err = -EINVAL; 129762306a36Sopenharmony_ci goto out; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci par->mode_idx = i; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* For 8bpp modes, always try to set the DAC to 8 bits. */ 130362306a36Sopenharmony_ci if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC && 130462306a36Sopenharmony_ci mode->bits_per_pixel <= 8) { 130562306a36Sopenharmony_ci uvesafb_reset(task); 130662306a36Sopenharmony_ci task->t.regs.eax = 0x4f08; 130762306a36Sopenharmony_ci task->t.regs.ebx = 0x0800; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci err = uvesafb_exec(task); 131062306a36Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f || 131162306a36Sopenharmony_ci ((task->t.regs.ebx & 0xff00) >> 8) != 8) { 131262306a36Sopenharmony_ci dac_width = 6; 131362306a36Sopenharmony_ci } else { 131462306a36Sopenharmony_ci dac_width = 8; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci info->fix.visual = (info->var.bits_per_pixel == 8) ? 131962306a36Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 132062306a36Sopenharmony_ci info->fix.line_length = mode->bytes_per_scan_line; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ciout: 132362306a36Sopenharmony_ci kfree(crtc); 132462306a36Sopenharmony_ci uvesafb_free(task); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci return err; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_cistatic void uvesafb_check_limits(struct fb_var_screeninfo *var, 133062306a36Sopenharmony_ci struct fb_info *info) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci const struct fb_videomode *mode; 133362306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* 133662306a36Sopenharmony_ci * If pixclock is set to 0, then we're using default BIOS timings 133762306a36Sopenharmony_ci * and thus don't have to perform any checks here. 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_ci if (!var->pixclock) 134062306a36Sopenharmony_ci return; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0300) { 134362306a36Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, var, info); 134462306a36Sopenharmony_ci return; 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_ci if (!fb_validate_mode(var, info)) 134862306a36Sopenharmony_ci return; 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci mode = fb_find_best_mode(var, &info->modelist); 135162306a36Sopenharmony_ci if (mode) { 135262306a36Sopenharmony_ci if (mode->xres == var->xres && mode->yres == var->yres && 135362306a36Sopenharmony_ci !(mode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))) { 135462306a36Sopenharmony_ci fb_videomode_to_var(var, mode); 135562306a36Sopenharmony_ci return; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) 136062306a36Sopenharmony_ci return; 136162306a36Sopenharmony_ci /* Use default refresh rate */ 136262306a36Sopenharmony_ci var->pixclock = 0; 136362306a36Sopenharmony_ci} 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_cistatic int uvesafb_check_var(struct fb_var_screeninfo *var, 136662306a36Sopenharmony_ci struct fb_info *info) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 136962306a36Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 137062306a36Sopenharmony_ci int match = -1; 137162306a36Sopenharmony_ci int depth = var->red.length + var->green.length + var->blue.length; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci /* 137462306a36Sopenharmony_ci * Various apps will use bits_per_pixel to set the color depth, 137562306a36Sopenharmony_ci * which is theoretically incorrect, but which we'll try to handle 137662306a36Sopenharmony_ci * here. 137762306a36Sopenharmony_ci */ 137862306a36Sopenharmony_ci if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8) 137962306a36Sopenharmony_ci depth = var->bits_per_pixel; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci match = uvesafb_vbe_find_mode(par, var->xres, var->yres, depth, 138262306a36Sopenharmony_ci UVESAFB_EXACT_RES); 138362306a36Sopenharmony_ci if (match == -1) 138462306a36Sopenharmony_ci return -EINVAL; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci mode = &par->vbe_modes[match]; 138762306a36Sopenharmony_ci uvesafb_setup_var(var, info, mode); 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* 139062306a36Sopenharmony_ci * Check whether we have remapped enough memory for this mode. 139162306a36Sopenharmony_ci * We might be called at an early stage, when we haven't remapped 139262306a36Sopenharmony_ci * any memory yet, in which case we simply skip the check. 139362306a36Sopenharmony_ci */ 139462306a36Sopenharmony_ci if (var->yres * mode->bytes_per_scan_line > info->fix.smem_len 139562306a36Sopenharmony_ci && info->fix.smem_len) 139662306a36Sopenharmony_ci return -EINVAL; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_DOUBLE) && 139962306a36Sopenharmony_ci !(par->vbe_modes[match].mode_attr & 0x100)) 140062306a36Sopenharmony_ci var->vmode &= ~FB_VMODE_DOUBLE; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if ((var->vmode & FB_VMODE_INTERLACED) && 140362306a36Sopenharmony_ci !(par->vbe_modes[match].mode_attr & 0x200)) 140462306a36Sopenharmony_ci var->vmode &= ~FB_VMODE_INTERLACED; 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci uvesafb_check_limits(var, info); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci var->xres_virtual = var->xres; 140962306a36Sopenharmony_ci var->yres_virtual = (par->ypan) ? 141062306a36Sopenharmony_ci info->fix.smem_len / mode->bytes_per_scan_line : 141162306a36Sopenharmony_ci var->yres; 141262306a36Sopenharmony_ci return 0; 141362306a36Sopenharmony_ci} 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cistatic struct fb_ops uvesafb_ops = { 141662306a36Sopenharmony_ci .owner = THIS_MODULE, 141762306a36Sopenharmony_ci .fb_open = uvesafb_open, 141862306a36Sopenharmony_ci .fb_release = uvesafb_release, 141962306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 142062306a36Sopenharmony_ci .fb_setcolreg = uvesafb_setcolreg, 142162306a36Sopenharmony_ci .fb_setcmap = uvesafb_setcmap, 142262306a36Sopenharmony_ci .fb_pan_display = uvesafb_pan_display, 142362306a36Sopenharmony_ci .fb_blank = uvesafb_blank, 142462306a36Sopenharmony_ci .fb_check_var = uvesafb_check_var, 142562306a36Sopenharmony_ci .fb_set_par = uvesafb_set_par, 142662306a36Sopenharmony_ci}; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_cistatic void uvesafb_init_info(struct fb_info *info, struct vbe_mode_ib *mode) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci unsigned int size_vmode; 143162306a36Sopenharmony_ci unsigned int size_remap; 143262306a36Sopenharmony_ci unsigned int size_total; 143362306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 143462306a36Sopenharmony_ci int i, h; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci info->pseudo_palette = ((u8 *)info->par + sizeof(struct uvesafb_par)); 143762306a36Sopenharmony_ci info->fix = uvesafb_fix; 143862306a36Sopenharmony_ci info->fix.ypanstep = par->ypan ? 1 : 0; 143962306a36Sopenharmony_ci info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* Disable blanking if the user requested so. */ 144262306a36Sopenharmony_ci if (!blank) 144362306a36Sopenharmony_ci uvesafb_ops.fb_blank = NULL; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* 144662306a36Sopenharmony_ci * Find out how much IO memory is required for the mode with 144762306a36Sopenharmony_ci * the highest resolution. 144862306a36Sopenharmony_ci */ 144962306a36Sopenharmony_ci size_remap = 0; 145062306a36Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 145162306a36Sopenharmony_ci h = par->vbe_modes[i].bytes_per_scan_line * 145262306a36Sopenharmony_ci par->vbe_modes[i].y_res; 145362306a36Sopenharmony_ci if (h > size_remap) 145462306a36Sopenharmony_ci size_remap = h; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci size_remap *= 2; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* 145962306a36Sopenharmony_ci * size_vmode -- that is the amount of memory needed for the 146062306a36Sopenharmony_ci * used video mode, i.e. the minimum amount of 146162306a36Sopenharmony_ci * memory we need. 146262306a36Sopenharmony_ci */ 146362306a36Sopenharmony_ci size_vmode = info->var.yres * mode->bytes_per_scan_line; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* 146662306a36Sopenharmony_ci * size_total -- all video memory we have. Used for mtrr 146762306a36Sopenharmony_ci * entries, resource allocation and bounds 146862306a36Sopenharmony_ci * checking. 146962306a36Sopenharmony_ci */ 147062306a36Sopenharmony_ci size_total = par->vbe_ib.total_memory * 65536; 147162306a36Sopenharmony_ci if (vram_total) 147262306a36Sopenharmony_ci size_total = vram_total * 1024 * 1024; 147362306a36Sopenharmony_ci if (size_total < size_vmode) 147462306a36Sopenharmony_ci size_total = size_vmode; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* 147762306a36Sopenharmony_ci * size_remap -- the amount of video memory we are going to 147862306a36Sopenharmony_ci * use for vesafb. With modern cards it is no 147962306a36Sopenharmony_ci * option to simply use size_total as th 148062306a36Sopenharmony_ci * wastes plenty of kernel address space. 148162306a36Sopenharmony_ci */ 148262306a36Sopenharmony_ci if (vram_remap) 148362306a36Sopenharmony_ci size_remap = vram_remap * 1024 * 1024; 148462306a36Sopenharmony_ci if (size_remap < size_vmode) 148562306a36Sopenharmony_ci size_remap = size_vmode; 148662306a36Sopenharmony_ci if (size_remap > size_total) 148762306a36Sopenharmony_ci size_remap = size_total; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci info->fix.smem_len = size_remap; 149062306a36Sopenharmony_ci info->fix.smem_start = mode->phys_base_ptr; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci /* 149362306a36Sopenharmony_ci * We have to set yres_virtual here because when setup_var() was 149462306a36Sopenharmony_ci * called, smem_len wasn't defined yet. 149562306a36Sopenharmony_ci */ 149662306a36Sopenharmony_ci info->var.yres_virtual = info->fix.smem_len / 149762306a36Sopenharmony_ci mode->bytes_per_scan_line; 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci if (par->ypan && info->var.yres_virtual > info->var.yres) { 150062306a36Sopenharmony_ci pr_info("scrolling: %s using protected mode interface, yres_virtual=%d\n", 150162306a36Sopenharmony_ci (par->ypan > 1) ? "ywrap" : "ypan", 150262306a36Sopenharmony_ci info->var.yres_virtual); 150362306a36Sopenharmony_ci } else { 150462306a36Sopenharmony_ci pr_info("scrolling: redraw\n"); 150562306a36Sopenharmony_ci info->var.yres_virtual = info->var.yres; 150662306a36Sopenharmony_ci par->ypan = 0; 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci info->flags = (par->ypan ? FBINFO_HWACCEL_YPAN : 0); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (!par->ypan) 151262306a36Sopenharmony_ci uvesafb_ops.fb_pan_display = NULL; 151362306a36Sopenharmony_ci} 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_cistatic void uvesafb_init_mtrr(struct fb_info *info) 151662306a36Sopenharmony_ci{ 151762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_ci if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { 152062306a36Sopenharmony_ci int temp_size = info->fix.smem_len; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci int rc; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* Find the largest power-of-two */ 152562306a36Sopenharmony_ci temp_size = roundup_pow_of_two(temp_size); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci /* Try and find a power of two to add */ 152862306a36Sopenharmony_ci do { 152962306a36Sopenharmony_ci rc = arch_phys_wc_add(info->fix.smem_start, temp_size); 153062306a36Sopenharmony_ci temp_size >>= 1; 153162306a36Sopenharmony_ci } while (temp_size >= PAGE_SIZE && rc == -EINVAL); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci if (rc >= 0) 153462306a36Sopenharmony_ci par->mtrr_handle = rc; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_cistatic void uvesafb_ioremap(struct fb_info *info) 153962306a36Sopenharmony_ci{ 154062306a36Sopenharmony_ci info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len); 154162306a36Sopenharmony_ci} 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_cistatic ssize_t uvesafb_show_vbe_ver(struct device *dev, 154462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 154562306a36Sopenharmony_ci{ 154662306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 154762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%.4x\n", par->vbe_ib.vbe_version); 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cistatic DEVICE_ATTR(vbe_version, S_IRUGO, uvesafb_show_vbe_ver, NULL); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic ssize_t uvesafb_show_vbe_modes(struct device *dev, 155562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 155862306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 155962306a36Sopenharmony_ci int ret = 0, i; 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt && ret < PAGE_SIZE; i++) { 156262306a36Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 156362306a36Sopenharmony_ci "%dx%d-%d, 0x%.4x\n", 156462306a36Sopenharmony_ci par->vbe_modes[i].x_res, par->vbe_modes[i].y_res, 156562306a36Sopenharmony_ci par->vbe_modes[i].depth, par->vbe_modes[i].mode_id); 156662306a36Sopenharmony_ci } 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci return ret; 156962306a36Sopenharmony_ci} 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_cistatic DEVICE_ATTR(vbe_modes, S_IRUGO, uvesafb_show_vbe_modes, NULL); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_cistatic ssize_t uvesafb_show_vendor(struct device *dev, 157462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 157762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (par->vbe_ib.oem_vendor_name_ptr) 158062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (char *) 158162306a36Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_vendor_name_ptr); 158262306a36Sopenharmony_ci else 158362306a36Sopenharmony_ci return 0; 158462306a36Sopenharmony_ci} 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_cistatic DEVICE_ATTR(oem_vendor, S_IRUGO, uvesafb_show_vendor, NULL); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cistatic ssize_t uvesafb_show_product_name(struct device *dev, 158962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 159062306a36Sopenharmony_ci{ 159162306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 159262306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci if (par->vbe_ib.oem_product_name_ptr) 159562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (char *) 159662306a36Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_product_name_ptr); 159762306a36Sopenharmony_ci else 159862306a36Sopenharmony_ci return 0; 159962306a36Sopenharmony_ci} 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_cistatic DEVICE_ATTR(oem_product_name, S_IRUGO, uvesafb_show_product_name, NULL); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_cistatic ssize_t uvesafb_show_product_rev(struct device *dev, 160462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 160562306a36Sopenharmony_ci{ 160662306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 160762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci if (par->vbe_ib.oem_product_rev_ptr) 161062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", (char *) 161162306a36Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_product_rev_ptr); 161262306a36Sopenharmony_ci else 161362306a36Sopenharmony_ci return 0; 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic DEVICE_ATTR(oem_product_rev, S_IRUGO, uvesafb_show_product_rev, NULL); 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic ssize_t uvesafb_show_oem_string(struct device *dev, 161962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 162062306a36Sopenharmony_ci{ 162162306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 162262306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_ci if (par->vbe_ib.oem_string_ptr) 162562306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", 162662306a36Sopenharmony_ci (char *)(&par->vbe_ib) + par->vbe_ib.oem_string_ptr); 162762306a36Sopenharmony_ci else 162862306a36Sopenharmony_ci return 0; 162962306a36Sopenharmony_ci} 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_cistatic DEVICE_ATTR(oem_string, S_IRUGO, uvesafb_show_oem_string, NULL); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_cistatic ssize_t uvesafb_show_nocrtc(struct device *dev, 163462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 163762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", par->nocrtc); 164062306a36Sopenharmony_ci} 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_cistatic ssize_t uvesafb_store_nocrtc(struct device *dev, 164362306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 164462306a36Sopenharmony_ci{ 164562306a36Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 164662306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (count > 0) { 164962306a36Sopenharmony_ci if (buf[0] == '0') 165062306a36Sopenharmony_ci par->nocrtc = 0; 165162306a36Sopenharmony_ci else 165262306a36Sopenharmony_ci par->nocrtc = 1; 165362306a36Sopenharmony_ci } 165462306a36Sopenharmony_ci return count; 165562306a36Sopenharmony_ci} 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_cistatic DEVICE_ATTR(nocrtc, S_IRUGO | S_IWUSR, uvesafb_show_nocrtc, 165862306a36Sopenharmony_ci uvesafb_store_nocrtc); 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic struct attribute *uvesafb_dev_attrs[] = { 166162306a36Sopenharmony_ci &dev_attr_vbe_version.attr, 166262306a36Sopenharmony_ci &dev_attr_vbe_modes.attr, 166362306a36Sopenharmony_ci &dev_attr_oem_vendor.attr, 166462306a36Sopenharmony_ci &dev_attr_oem_product_name.attr, 166562306a36Sopenharmony_ci &dev_attr_oem_product_rev.attr, 166662306a36Sopenharmony_ci &dev_attr_oem_string.attr, 166762306a36Sopenharmony_ci &dev_attr_nocrtc.attr, 166862306a36Sopenharmony_ci NULL, 166962306a36Sopenharmony_ci}; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic const struct attribute_group uvesafb_dev_attgrp = { 167262306a36Sopenharmony_ci .name = NULL, 167362306a36Sopenharmony_ci .attrs = uvesafb_dev_attrs, 167462306a36Sopenharmony_ci}; 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_cistatic int uvesafb_probe(struct platform_device *dev) 167762306a36Sopenharmony_ci{ 167862306a36Sopenharmony_ci struct fb_info *info; 167962306a36Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 168062306a36Sopenharmony_ci struct uvesafb_par *par; 168162306a36Sopenharmony_ci int err = 0, i; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(*par) + sizeof(u32) * 256, &dev->dev); 168462306a36Sopenharmony_ci if (!info) 168562306a36Sopenharmony_ci return -ENOMEM; 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci par = info->par; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci err = uvesafb_vbe_init(info); 169062306a36Sopenharmony_ci if (err) { 169162306a36Sopenharmony_ci pr_err("vbe_init() failed with %d\n", err); 169262306a36Sopenharmony_ci goto out; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci info->fbops = &uvesafb_ops; 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci i = uvesafb_vbe_init_mode(info); 169862306a36Sopenharmony_ci if (i < 0) { 169962306a36Sopenharmony_ci err = -EINVAL; 170062306a36Sopenharmony_ci goto out; 170162306a36Sopenharmony_ci } else { 170262306a36Sopenharmony_ci mode = &par->vbe_modes[i]; 170362306a36Sopenharmony_ci } 170462306a36Sopenharmony_ci 170562306a36Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { 170662306a36Sopenharmony_ci err = -ENXIO; 170762306a36Sopenharmony_ci goto out; 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci uvesafb_init_info(info, mode); 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci if (!request_region(0x3c0, 32, "uvesafb")) { 171362306a36Sopenharmony_ci pr_err("request region 0x3c0-0x3e0 failed\n"); 171462306a36Sopenharmony_ci err = -EIO; 171562306a36Sopenharmony_ci goto out_mode; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, 171962306a36Sopenharmony_ci "uvesafb")) { 172062306a36Sopenharmony_ci pr_err("cannot reserve video memory at 0x%lx\n", 172162306a36Sopenharmony_ci info->fix.smem_start); 172262306a36Sopenharmony_ci err = -EIO; 172362306a36Sopenharmony_ci goto out_reg; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci uvesafb_init_mtrr(info); 172762306a36Sopenharmony_ci uvesafb_ioremap(info); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (!info->screen_base) { 173062306a36Sopenharmony_ci pr_err("abort, cannot ioremap 0x%x bytes of video memory at 0x%lx\n", 173162306a36Sopenharmony_ci info->fix.smem_len, info->fix.smem_start); 173262306a36Sopenharmony_ci err = -EIO; 173362306a36Sopenharmony_ci goto out_mem; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_ci platform_set_drvdata(dev, info); 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (register_framebuffer(info) < 0) { 173962306a36Sopenharmony_ci pr_err("failed to register framebuffer device\n"); 174062306a36Sopenharmony_ci err = -EINVAL; 174162306a36Sopenharmony_ci goto out_unmap; 174262306a36Sopenharmony_ci } 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci pr_info("framebuffer at 0x%lx, mapped to 0x%p, using %dk, total %dk\n", 174562306a36Sopenharmony_ci info->fix.smem_start, info->screen_base, 174662306a36Sopenharmony_ci info->fix.smem_len / 1024, par->vbe_ib.total_memory * 64); 174762306a36Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci err = sysfs_create_group(&dev->dev.kobj, &uvesafb_dev_attgrp); 175062306a36Sopenharmony_ci if (err != 0) 175162306a36Sopenharmony_ci fb_warn(info, "failed to register attributes\n"); 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci return 0; 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_ciout_unmap: 175662306a36Sopenharmony_ci iounmap(info->screen_base); 175762306a36Sopenharmony_ciout_mem: 175862306a36Sopenharmony_ci arch_phys_wc_del(par->mtrr_handle); 175962306a36Sopenharmony_ci release_mem_region(info->fix.smem_start, info->fix.smem_len); 176062306a36Sopenharmony_ciout_reg: 176162306a36Sopenharmony_ci release_region(0x3c0, 32); 176262306a36Sopenharmony_ciout_mode: 176362306a36Sopenharmony_ci if (!list_empty(&info->modelist)) 176462306a36Sopenharmony_ci fb_destroy_modelist(&info->modelist); 176562306a36Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 176662306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 176762306a36Sopenharmony_ciout: 176862306a36Sopenharmony_ci kfree(par->vbe_modes); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci framebuffer_release(info); 177162306a36Sopenharmony_ci return err; 177262306a36Sopenharmony_ci} 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_cistatic void uvesafb_remove(struct platform_device *dev) 177562306a36Sopenharmony_ci{ 177662306a36Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 177762306a36Sopenharmony_ci struct uvesafb_par *par = info->par; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &uvesafb_dev_attgrp); 178062306a36Sopenharmony_ci unregister_framebuffer(info); 178162306a36Sopenharmony_ci release_region(0x3c0, 32); 178262306a36Sopenharmony_ci iounmap(info->screen_base); 178362306a36Sopenharmony_ci arch_phys_wc_del(par->mtrr_handle); 178462306a36Sopenharmony_ci release_mem_region(info->fix.smem_start, info->fix.smem_len); 178562306a36Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 178662306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci kfree(par->vbe_modes); 178962306a36Sopenharmony_ci kfree(par->vbe_state_orig); 179062306a36Sopenharmony_ci kfree(par->vbe_state_saved); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci framebuffer_release(info); 179362306a36Sopenharmony_ci} 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_cistatic struct platform_driver uvesafb_driver = { 179662306a36Sopenharmony_ci .probe = uvesafb_probe, 179762306a36Sopenharmony_ci .remove_new = uvesafb_remove, 179862306a36Sopenharmony_ci .driver = { 179962306a36Sopenharmony_ci .name = "uvesafb", 180062306a36Sopenharmony_ci }, 180162306a36Sopenharmony_ci}; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_cistatic struct platform_device *uvesafb_device; 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci#ifndef MODULE 180662306a36Sopenharmony_cistatic int uvesafb_setup(char *options) 180762306a36Sopenharmony_ci{ 180862306a36Sopenharmony_ci char *this_opt; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci if (!options || !*options) 181162306a36Sopenharmony_ci return 0; 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 181462306a36Sopenharmony_ci if (!*this_opt) continue; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci if (!strcmp(this_opt, "redraw")) 181762306a36Sopenharmony_ci ypan = 0; 181862306a36Sopenharmony_ci else if (!strcmp(this_opt, "ypan")) 181962306a36Sopenharmony_ci ypan = 1; 182062306a36Sopenharmony_ci else if (!strcmp(this_opt, "ywrap")) 182162306a36Sopenharmony_ci ypan = 2; 182262306a36Sopenharmony_ci else if (!strcmp(this_opt, "vgapal")) 182362306a36Sopenharmony_ci pmi_setpal = false; 182462306a36Sopenharmony_ci else if (!strcmp(this_opt, "pmipal")) 182562306a36Sopenharmony_ci pmi_setpal = true; 182662306a36Sopenharmony_ci else if (!strncmp(this_opt, "mtrr:", 5)) 182762306a36Sopenharmony_ci mtrr = simple_strtoul(this_opt+5, NULL, 0); 182862306a36Sopenharmony_ci else if (!strcmp(this_opt, "nomtrr")) 182962306a36Sopenharmony_ci mtrr = 0; 183062306a36Sopenharmony_ci else if (!strcmp(this_opt, "nocrtc")) 183162306a36Sopenharmony_ci nocrtc = true; 183262306a36Sopenharmony_ci else if (!strcmp(this_opt, "noedid")) 183362306a36Sopenharmony_ci noedid = true; 183462306a36Sopenharmony_ci else if (!strcmp(this_opt, "noblank")) 183562306a36Sopenharmony_ci blank = false; 183662306a36Sopenharmony_ci else if (!strncmp(this_opt, "vtotal:", 7)) 183762306a36Sopenharmony_ci vram_total = simple_strtoul(this_opt + 7, NULL, 0); 183862306a36Sopenharmony_ci else if (!strncmp(this_opt, "vremap:", 7)) 183962306a36Sopenharmony_ci vram_remap = simple_strtoul(this_opt + 7, NULL, 0); 184062306a36Sopenharmony_ci else if (!strncmp(this_opt, "maxhf:", 6)) 184162306a36Sopenharmony_ci maxhf = simple_strtoul(this_opt + 6, NULL, 0); 184262306a36Sopenharmony_ci else if (!strncmp(this_opt, "maxvf:", 6)) 184362306a36Sopenharmony_ci maxvf = simple_strtoul(this_opt + 6, NULL, 0); 184462306a36Sopenharmony_ci else if (!strncmp(this_opt, "maxclk:", 7)) 184562306a36Sopenharmony_ci maxclk = simple_strtoul(this_opt + 7, NULL, 0); 184662306a36Sopenharmony_ci else if (!strncmp(this_opt, "vbemode:", 8)) 184762306a36Sopenharmony_ci vbemode = simple_strtoul(this_opt + 8, NULL, 0); 184862306a36Sopenharmony_ci else if (this_opt[0] >= '0' && this_opt[0] <= '9') { 184962306a36Sopenharmony_ci mode_option = this_opt; 185062306a36Sopenharmony_ci } else { 185162306a36Sopenharmony_ci pr_warn("unrecognized option %s\n", this_opt); 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci } 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci if (mtrr != 3 && mtrr != 0) 185662306a36Sopenharmony_ci pr_warn("uvesafb: mtrr should be set to 0 or 3; %d is unsupported", mtrr); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci return 0; 185962306a36Sopenharmony_ci} 186062306a36Sopenharmony_ci#endif /* !MODULE */ 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_cistatic ssize_t v86d_show(struct device_driver *dev, char *buf) 186362306a36Sopenharmony_ci{ 186462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", v86d_path); 186562306a36Sopenharmony_ci} 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_cistatic ssize_t v86d_store(struct device_driver *dev, const char *buf, 186862306a36Sopenharmony_ci size_t count) 186962306a36Sopenharmony_ci{ 187062306a36Sopenharmony_ci strncpy(v86d_path, buf, PATH_MAX - 1); 187162306a36Sopenharmony_ci return count; 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_cistatic DRIVER_ATTR_RW(v86d); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic int uvesafb_init(void) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci int err; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci#ifndef MODULE 188062306a36Sopenharmony_ci char *option = NULL; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci if (fb_get_options("uvesafb", &option)) 188362306a36Sopenharmony_ci return -ENODEV; 188462306a36Sopenharmony_ci uvesafb_setup(option); 188562306a36Sopenharmony_ci#endif 188662306a36Sopenharmony_ci err = cn_add_callback(&uvesafb_cn_id, "uvesafb", uvesafb_cn_callback); 188762306a36Sopenharmony_ci if (err) 188862306a36Sopenharmony_ci return err; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci err = platform_driver_register(&uvesafb_driver); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if (!err) { 189362306a36Sopenharmony_ci uvesafb_device = platform_device_alloc("uvesafb", 0); 189462306a36Sopenharmony_ci if (uvesafb_device) 189562306a36Sopenharmony_ci err = platform_device_add(uvesafb_device); 189662306a36Sopenharmony_ci else 189762306a36Sopenharmony_ci err = -ENOMEM; 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci if (err) { 190062306a36Sopenharmony_ci platform_device_put(uvesafb_device); 190162306a36Sopenharmony_ci platform_driver_unregister(&uvesafb_driver); 190262306a36Sopenharmony_ci cn_del_callback(&uvesafb_cn_id); 190362306a36Sopenharmony_ci return err; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci err = driver_create_file(&uvesafb_driver.driver, 190762306a36Sopenharmony_ci &driver_attr_v86d); 190862306a36Sopenharmony_ci if (err) { 190962306a36Sopenharmony_ci pr_warn("failed to register attributes\n"); 191062306a36Sopenharmony_ci err = 0; 191162306a36Sopenharmony_ci } 191262306a36Sopenharmony_ci } 191362306a36Sopenharmony_ci return err; 191462306a36Sopenharmony_ci} 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_cimodule_init(uvesafb_init); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_cistatic void uvesafb_exit(void) 191962306a36Sopenharmony_ci{ 192062306a36Sopenharmony_ci struct uvesafb_ktask *task; 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci if (v86d_started) { 192362306a36Sopenharmony_ci task = uvesafb_prep(); 192462306a36Sopenharmony_ci if (task) { 192562306a36Sopenharmony_ci task->t.flags = TF_EXIT; 192662306a36Sopenharmony_ci uvesafb_exec(task); 192762306a36Sopenharmony_ci uvesafb_free(task); 192862306a36Sopenharmony_ci } 192962306a36Sopenharmony_ci } 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci driver_remove_file(&uvesafb_driver.driver, &driver_attr_v86d); 193262306a36Sopenharmony_ci platform_device_unregister(uvesafb_device); 193362306a36Sopenharmony_ci platform_driver_unregister(&uvesafb_driver); 193462306a36Sopenharmony_ci cn_del_callback(&uvesafb_cn_id); 193562306a36Sopenharmony_ci} 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_cimodule_exit(uvesafb_exit); 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic int param_set_scroll(const char *val, const struct kernel_param *kp) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci ypan = 0; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (!strcmp(val, "redraw")) 194462306a36Sopenharmony_ci ypan = 0; 194562306a36Sopenharmony_ci else if (!strcmp(val, "ypan")) 194662306a36Sopenharmony_ci ypan = 1; 194762306a36Sopenharmony_ci else if (!strcmp(val, "ywrap")) 194862306a36Sopenharmony_ci ypan = 2; 194962306a36Sopenharmony_ci else 195062306a36Sopenharmony_ci return -EINVAL; 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci return 0; 195362306a36Sopenharmony_ci} 195462306a36Sopenharmony_cistatic const struct kernel_param_ops param_ops_scroll = { 195562306a36Sopenharmony_ci .set = param_set_scroll, 195662306a36Sopenharmony_ci}; 195762306a36Sopenharmony_ci#define param_check_scroll(name, p) __param_check(name, p, void) 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_cimodule_param_named(scroll, ypan, scroll, 0); 196062306a36Sopenharmony_ciMODULE_PARM_DESC(scroll, 196162306a36Sopenharmony_ci "Scrolling mode, set to 'redraw', 'ypan', or 'ywrap'"); 196262306a36Sopenharmony_cimodule_param_named(vgapal, pmi_setpal, invbool, 0); 196362306a36Sopenharmony_ciMODULE_PARM_DESC(vgapal, "Set palette using VGA registers"); 196462306a36Sopenharmony_cimodule_param_named(pmipal, pmi_setpal, bool, 0); 196562306a36Sopenharmony_ciMODULE_PARM_DESC(pmipal, "Set palette using PMI calls"); 196662306a36Sopenharmony_cimodule_param(mtrr, uint, 0); 196762306a36Sopenharmony_ciMODULE_PARM_DESC(mtrr, 196862306a36Sopenharmony_ci "Memory Type Range Registers setting. Use 0 to disable."); 196962306a36Sopenharmony_cimodule_param(blank, bool, 0); 197062306a36Sopenharmony_ciMODULE_PARM_DESC(blank, "Enable hardware blanking"); 197162306a36Sopenharmony_cimodule_param(nocrtc, bool, 0); 197262306a36Sopenharmony_ciMODULE_PARM_DESC(nocrtc, "Ignore CRTC timings when setting modes"); 197362306a36Sopenharmony_cimodule_param(noedid, bool, 0); 197462306a36Sopenharmony_ciMODULE_PARM_DESC(noedid, 197562306a36Sopenharmony_ci "Ignore EDID-provided monitor limits when setting modes"); 197662306a36Sopenharmony_cimodule_param(vram_remap, uint, 0); 197762306a36Sopenharmony_ciMODULE_PARM_DESC(vram_remap, "Set amount of video memory to be used [MiB]"); 197862306a36Sopenharmony_cimodule_param(vram_total, uint, 0); 197962306a36Sopenharmony_ciMODULE_PARM_DESC(vram_total, "Set total amount of video memory [MiB]"); 198062306a36Sopenharmony_cimodule_param(maxclk, ushort, 0); 198162306a36Sopenharmony_ciMODULE_PARM_DESC(maxclk, "Maximum pixelclock [MHz], overrides EDID data"); 198262306a36Sopenharmony_cimodule_param(maxhf, ushort, 0); 198362306a36Sopenharmony_ciMODULE_PARM_DESC(maxhf, 198462306a36Sopenharmony_ci "Maximum horizontal frequency [kHz], overrides EDID data"); 198562306a36Sopenharmony_cimodule_param(maxvf, ushort, 0); 198662306a36Sopenharmony_ciMODULE_PARM_DESC(maxvf, 198762306a36Sopenharmony_ci "Maximum vertical frequency [Hz], overrides EDID data"); 198862306a36Sopenharmony_cimodule_param(mode_option, charp, 0); 198962306a36Sopenharmony_ciMODULE_PARM_DESC(mode_option, 199062306a36Sopenharmony_ci "Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); 199162306a36Sopenharmony_cimodule_param(vbemode, ushort, 0); 199262306a36Sopenharmony_ciMODULE_PARM_DESC(vbemode, 199362306a36Sopenharmony_ci "VBE mode number to set, overrides the 'mode' option"); 199462306a36Sopenharmony_cimodule_param_string(v86d, v86d_path, PATH_MAX, 0660); 199562306a36Sopenharmony_ciMODULE_PARM_DESC(v86d, "Path to the v86d userspace helper."); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 199862306a36Sopenharmony_ciMODULE_AUTHOR("Michal Januszewski <spock@gentoo.org>"); 199962306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards"); 200062306a36Sopenharmony_ci 2001