18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * A framebuffer driver for VBE 2.0+ compliant video cards 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2007 Michal Januszewski <spock@gentoo.org> 68c2ecf20Sopenharmony_ci * Loosely based upon the vesafb driver. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/timer.h> 178c2ecf20Sopenharmony_ci#include <linux/completion.h> 188c2ecf20Sopenharmony_ci#include <linux/connector.h> 198c2ecf20Sopenharmony_ci#include <linux/random.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/limits.h> 228c2ecf20Sopenharmony_ci#include <linux/fb.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/mutex.h> 258c2ecf20Sopenharmony_ci#include <linux/slab.h> 268c2ecf20Sopenharmony_ci#include <video/edid.h> 278c2ecf20Sopenharmony_ci#include <video/uvesafb.h> 288c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 298c2ecf20Sopenharmony_ci#include <video/vga.h> 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci#include "edid.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct cb_id uvesafb_cn_id = { 348c2ecf20Sopenharmony_ci .idx = CN_IDX_V86D, 358c2ecf20Sopenharmony_ci .val = CN_VAL_V86D_UVESAFB 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_cistatic char v86d_path[PATH_MAX] = "/sbin/v86d"; 388c2ecf20Sopenharmony_cistatic char v86d_started; /* has v86d been started by uvesafb? */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo uvesafb_fix = { 418c2ecf20Sopenharmony_ci .id = "VESA VGA", 428c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 438c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 448c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int mtrr = 3; /* enable mtrr by default */ 488c2ecf20Sopenharmony_cistatic bool blank = true; /* enable blanking by default */ 498c2ecf20Sopenharmony_cistatic int ypan = 1; /* 0: scroll, 1: ypan, 2: ywrap */ 508c2ecf20Sopenharmony_cistatic bool pmi_setpal = true; /* use PMI for palette changes */ 518c2ecf20Sopenharmony_cistatic bool nocrtc; /* ignore CRTC settings */ 528c2ecf20Sopenharmony_cistatic bool noedid; /* don't try DDC transfers */ 538c2ecf20Sopenharmony_cistatic int vram_remap; /* set amt. of memory to be used */ 548c2ecf20Sopenharmony_cistatic int vram_total; /* set total amount of memory */ 558c2ecf20Sopenharmony_cistatic u16 maxclk; /* maximum pixel clock */ 568c2ecf20Sopenharmony_cistatic u16 maxvf; /* maximum vertical frequency */ 578c2ecf20Sopenharmony_cistatic u16 maxhf; /* maximum horizontal frequency */ 588c2ecf20Sopenharmony_cistatic u16 vbemode; /* force use of a specific VBE mode */ 598c2ecf20Sopenharmony_cistatic char *mode_option; 608c2ecf20Sopenharmony_cistatic u8 dac_width = 6; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic struct uvesafb_ktask *uvfb_tasks[UVESAFB_TASKS_MAX]; 638c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(uvfb_lock); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * A handler for replies from userspace. 678c2ecf20Sopenharmony_ci * 688c2ecf20Sopenharmony_ci * Make sure each message passes consistency checks and if it does, 698c2ecf20Sopenharmony_ci * find the kernel part of the task struct, copy the registers and 708c2ecf20Sopenharmony_ci * the buffer contents and then complete the task. 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_cistatic void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct uvesafb_task *utask; 758c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 788c2ecf20Sopenharmony_ci return; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (msg->seq >= UVESAFB_TASKS_MAX) 818c2ecf20Sopenharmony_ci return; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mutex_lock(&uvfb_lock); 848c2ecf20Sopenharmony_ci task = uvfb_tasks[msg->seq]; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (!task || msg->ack != task->ack) { 878c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 888c2ecf20Sopenharmony_ci return; 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci utask = (struct uvesafb_task *)msg->data; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* Sanity checks for the buffer length. */ 948c2ecf20Sopenharmony_ci if (task->t.buf_len < utask->buf_len || 958c2ecf20Sopenharmony_ci utask->buf_len > msg->len - sizeof(*utask)) { 968c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 978c2ecf20Sopenharmony_ci return; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci uvfb_tasks[msg->seq] = NULL; 1018c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci memcpy(&task->t, utask, sizeof(*utask)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (task->t.buf_len && task->buf) 1068c2ecf20Sopenharmony_ci memcpy(task->buf, utask + 1, task->t.buf_len); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci complete(task->done); 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int uvesafb_helper_start(void) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci char *envp[] = { 1158c2ecf20Sopenharmony_ci "HOME=/", 1168c2ecf20Sopenharmony_ci "PATH=/sbin:/bin", 1178c2ecf20Sopenharmony_ci NULL, 1188c2ecf20Sopenharmony_ci }; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci char *argv[] = { 1218c2ecf20Sopenharmony_ci v86d_path, 1228c2ecf20Sopenharmony_ci NULL, 1238c2ecf20Sopenharmony_ci }; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * Execute a uvesafb task. 1308c2ecf20Sopenharmony_ci * 1318c2ecf20Sopenharmony_ci * Returns 0 if the task is executed successfully. 1328c2ecf20Sopenharmony_ci * 1338c2ecf20Sopenharmony_ci * A message sent to the userspace consists of the uvesafb_task 1348c2ecf20Sopenharmony_ci * struct and (optionally) a buffer. The uvesafb_task struct is 1358c2ecf20Sopenharmony_ci * a simplified version of uvesafb_ktask (its kernel counterpart) 1368c2ecf20Sopenharmony_ci * containing only the register values, flags and the length of 1378c2ecf20Sopenharmony_ci * the buffer. 1388c2ecf20Sopenharmony_ci * 1398c2ecf20Sopenharmony_ci * Each message is assigned a sequence number (increased linearly) 1408c2ecf20Sopenharmony_ci * and a random ack number. The sequence number is used as a key 1418c2ecf20Sopenharmony_ci * for the uvfb_tasks array which holds pointers to uvesafb_ktask 1428c2ecf20Sopenharmony_ci * structs for all requests. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistatic int uvesafb_exec(struct uvesafb_ktask *task) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci static int seq; 1478c2ecf20Sopenharmony_ci struct cn_msg *m; 1488c2ecf20Sopenharmony_ci int err; 1498c2ecf20Sopenharmony_ci int len = sizeof(task->t) + task->t.buf_len; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * Check whether the message isn't longer than the maximum 1538c2ecf20Sopenharmony_ci * allowed by connector. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_ci if (sizeof(*m) + len > CONNECTOR_MAX_MSG_SIZE) { 1568c2ecf20Sopenharmony_ci pr_warn("message too long (%d), can't execute task\n", 1578c2ecf20Sopenharmony_ci (int)(sizeof(*m) + len)); 1588c2ecf20Sopenharmony_ci return -E2BIG; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci m = kzalloc(sizeof(*m) + len, GFP_KERNEL); 1628c2ecf20Sopenharmony_ci if (!m) 1638c2ecf20Sopenharmony_ci return -ENOMEM; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci init_completion(task->done); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci memcpy(&m->id, &uvesafb_cn_id, sizeof(m->id)); 1688c2ecf20Sopenharmony_ci m->seq = seq; 1698c2ecf20Sopenharmony_ci m->len = len; 1708c2ecf20Sopenharmony_ci m->ack = prandom_u32(); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* uvesafb_task structure */ 1738c2ecf20Sopenharmony_ci memcpy(m + 1, &task->t, sizeof(task->t)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Buffer */ 1768c2ecf20Sopenharmony_ci memcpy((u8 *)(m + 1) + sizeof(task->t), task->buf, task->t.buf_len); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * Save the message ack number so that we can find the kernel 1808c2ecf20Sopenharmony_ci * part of this task when a reply is received from userspace. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci task->ack = m->ack; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci mutex_lock(&uvfb_lock); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* If all slots are taken -- bail out. */ 1878c2ecf20Sopenharmony_ci if (uvfb_tasks[seq]) { 1888c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 1898c2ecf20Sopenharmony_ci err = -EBUSY; 1908c2ecf20Sopenharmony_ci goto out; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Save a pointer to the kernel part of the task struct. */ 1948c2ecf20Sopenharmony_ci uvfb_tasks[seq] = task; 1958c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci err = cn_netlink_send(m, 0, 0, GFP_KERNEL); 1988c2ecf20Sopenharmony_ci if (err == -ESRCH) { 1998c2ecf20Sopenharmony_ci /* 2008c2ecf20Sopenharmony_ci * Try to start the userspace helper if sending 2018c2ecf20Sopenharmony_ci * the request failed the first time. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci err = uvesafb_helper_start(); 2048c2ecf20Sopenharmony_ci if (err) { 2058c2ecf20Sopenharmony_ci pr_err("failed to execute %s\n", v86d_path); 2068c2ecf20Sopenharmony_ci pr_err("make sure that the v86d helper is installed and executable\n"); 2078c2ecf20Sopenharmony_ci } else { 2088c2ecf20Sopenharmony_ci v86d_started = 1; 2098c2ecf20Sopenharmony_ci err = cn_netlink_send(m, 0, 0, gfp_any()); 2108c2ecf20Sopenharmony_ci if (err == -ENOBUFS) 2118c2ecf20Sopenharmony_ci err = 0; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } else if (err == -ENOBUFS) 2148c2ecf20Sopenharmony_ci err = 0; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (!err && !(task->t.flags & TF_EXIT)) 2178c2ecf20Sopenharmony_ci err = !wait_for_completion_timeout(task->done, 2188c2ecf20Sopenharmony_ci msecs_to_jiffies(UVESAFB_TIMEOUT)); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mutex_lock(&uvfb_lock); 2218c2ecf20Sopenharmony_ci uvfb_tasks[seq] = NULL; 2228c2ecf20Sopenharmony_ci mutex_unlock(&uvfb_lock); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci seq++; 2258c2ecf20Sopenharmony_ci if (seq >= UVESAFB_TASKS_MAX) 2268c2ecf20Sopenharmony_ci seq = 0; 2278c2ecf20Sopenharmony_ciout: 2288c2ecf20Sopenharmony_ci kfree(m); 2298c2ecf20Sopenharmony_ci return err; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Free a uvesafb_ktask struct. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistatic void uvesafb_free(struct uvesafb_ktask *task) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci if (task) { 2388c2ecf20Sopenharmony_ci kfree(task->done); 2398c2ecf20Sopenharmony_ci kfree(task); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* 2448c2ecf20Sopenharmony_ci * Prepare a uvesafb_ktask struct to be used again. 2458c2ecf20Sopenharmony_ci */ 2468c2ecf20Sopenharmony_cistatic void uvesafb_reset(struct uvesafb_ktask *task) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct completion *cpl = task->done; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci memset(task, 0, sizeof(*task)); 2518c2ecf20Sopenharmony_ci task->done = cpl; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci/* 2558c2ecf20Sopenharmony_ci * Allocate and prepare a uvesafb_ktask struct. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_cistatic struct uvesafb_ktask *uvesafb_prep(void) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci task = kzalloc(sizeof(*task), GFP_KERNEL); 2628c2ecf20Sopenharmony_ci if (task) { 2638c2ecf20Sopenharmony_ci task->done = kzalloc(sizeof(*task->done), GFP_KERNEL); 2648c2ecf20Sopenharmony_ci if (!task->done) { 2658c2ecf20Sopenharmony_ci kfree(task); 2668c2ecf20Sopenharmony_ci task = NULL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci return task; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic void uvesafb_setup_var(struct fb_var_screeninfo *var, 2738c2ecf20Sopenharmony_ci struct fb_info *info, struct vbe_mode_ib *mode) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci var->vmode = FB_VMODE_NONINTERLACED; 2788c2ecf20Sopenharmony_ci var->sync = FB_SYNC_VERT_HIGH_ACT; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci var->xres = mode->x_res; 2818c2ecf20Sopenharmony_ci var->yres = mode->y_res; 2828c2ecf20Sopenharmony_ci var->xres_virtual = mode->x_res; 2838c2ecf20Sopenharmony_ci var->yres_virtual = (par->ypan) ? 2848c2ecf20Sopenharmony_ci info->fix.smem_len / mode->bytes_per_scan_line : 2858c2ecf20Sopenharmony_ci mode->y_res; 2868c2ecf20Sopenharmony_ci var->xoffset = 0; 2878c2ecf20Sopenharmony_ci var->yoffset = 0; 2888c2ecf20Sopenharmony_ci var->bits_per_pixel = mode->bits_per_pixel; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 15) 2918c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (var->bits_per_pixel > 8) { 2948c2ecf20Sopenharmony_ci var->red.offset = mode->red_off; 2958c2ecf20Sopenharmony_ci var->red.length = mode->red_len; 2968c2ecf20Sopenharmony_ci var->green.offset = mode->green_off; 2978c2ecf20Sopenharmony_ci var->green.length = mode->green_len; 2988c2ecf20Sopenharmony_ci var->blue.offset = mode->blue_off; 2998c2ecf20Sopenharmony_ci var->blue.length = mode->blue_len; 3008c2ecf20Sopenharmony_ci var->transp.offset = mode->rsvd_off; 3018c2ecf20Sopenharmony_ci var->transp.length = mode->rsvd_len; 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci var->red.offset = 0; 3048c2ecf20Sopenharmony_ci var->green.offset = 0; 3058c2ecf20Sopenharmony_ci var->blue.offset = 0; 3068c2ecf20Sopenharmony_ci var->transp.offset = 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci var->red.length = 8; 3098c2ecf20Sopenharmony_ci var->green.length = 8; 3108c2ecf20Sopenharmony_ci var->blue.length = 8; 3118c2ecf20Sopenharmony_ci var->transp.length = 0; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic int uvesafb_vbe_find_mode(struct uvesafb_par *par, 3168c2ecf20Sopenharmony_ci int xres, int yres, int depth, unsigned char flags) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int i, match = -1, h = 0, d = 0x7fffffff; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 3218c2ecf20Sopenharmony_ci h = abs(par->vbe_modes[i].x_res - xres) + 3228c2ecf20Sopenharmony_ci abs(par->vbe_modes[i].y_res - yres) + 3238c2ecf20Sopenharmony_ci abs(depth - par->vbe_modes[i].depth); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* 3268c2ecf20Sopenharmony_ci * We have an exact match in terms of resolution 3278c2ecf20Sopenharmony_ci * and depth. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_ci if (h == 0) 3308c2ecf20Sopenharmony_ci return i; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (h < d || (h == d && par->vbe_modes[i].depth > depth)) { 3338c2ecf20Sopenharmony_ci d = h; 3348c2ecf20Sopenharmony_ci match = i; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci i = 1; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (flags & UVESAFB_EXACT_DEPTH && 3408c2ecf20Sopenharmony_ci par->vbe_modes[match].depth != depth) 3418c2ecf20Sopenharmony_ci i = 0; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (flags & UVESAFB_EXACT_RES && d > 24) 3448c2ecf20Sopenharmony_ci i = 0; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (i != 0) 3478c2ecf20Sopenharmony_ci return match; 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci return -1; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic u8 *uvesafb_vbe_state_save(struct uvesafb_par *par) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 3558c2ecf20Sopenharmony_ci u8 *state; 3568c2ecf20Sopenharmony_ci int err; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (!par->vbe_state_size) 3598c2ecf20Sopenharmony_ci return NULL; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci state = kmalloc(par->vbe_state_size, GFP_KERNEL); 3628c2ecf20Sopenharmony_ci if (!state) 3638c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci task = uvesafb_prep(); 3668c2ecf20Sopenharmony_ci if (!task) { 3678c2ecf20Sopenharmony_ci kfree(state); 3688c2ecf20Sopenharmony_ci return NULL; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f04; 3728c2ecf20Sopenharmony_ci task->t.regs.ecx = 0x000f; 3738c2ecf20Sopenharmony_ci task->t.regs.edx = 0x0001; 3748c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESBX; 3758c2ecf20Sopenharmony_ci task->t.buf_len = par->vbe_state_size; 3768c2ecf20Sopenharmony_ci task->buf = state; 3778c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 3808c2ecf20Sopenharmony_ci pr_warn("VBE get state call failed (eax=0x%x, err=%d)\n", 3818c2ecf20Sopenharmony_ci task->t.regs.eax, err); 3828c2ecf20Sopenharmony_ci kfree(state); 3838c2ecf20Sopenharmony_ci state = NULL; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci uvesafb_free(task); 3878c2ecf20Sopenharmony_ci return state; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void uvesafb_vbe_state_restore(struct uvesafb_par *par, u8 *state_buf) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 3938c2ecf20Sopenharmony_ci int err; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci if (!state_buf) 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci task = uvesafb_prep(); 3998c2ecf20Sopenharmony_ci if (!task) 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f04; 4038c2ecf20Sopenharmony_ci task->t.regs.ecx = 0x000f; 4048c2ecf20Sopenharmony_ci task->t.regs.edx = 0x0002; 4058c2ecf20Sopenharmony_ci task->t.buf_len = par->vbe_state_size; 4068c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_ESBX; 4078c2ecf20Sopenharmony_ci task->buf = state_buf; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 4108c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) 4118c2ecf20Sopenharmony_ci pr_warn("VBE state restore call failed (eax=0x%x, err=%d)\n", 4128c2ecf20Sopenharmony_ci task->t.regs.eax, err); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci uvesafb_free(task); 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int uvesafb_vbe_getinfo(struct uvesafb_ktask *task, 4188c2ecf20Sopenharmony_ci struct uvesafb_par *par) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci int err; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f00; 4238c2ecf20Sopenharmony_ci task->t.flags = TF_VBEIB; 4248c2ecf20Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_ib); 4258c2ecf20Sopenharmony_ci task->buf = &par->vbe_ib; 4268c2ecf20Sopenharmony_ci strncpy(par->vbe_ib.vbe_signature, "VBE2", 4); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 4298c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 4308c2ecf20Sopenharmony_ci pr_err("Getting VBE info block failed (eax=0x%x, err=%d)\n", 4318c2ecf20Sopenharmony_ci (u32)task->t.regs.eax, err); 4328c2ecf20Sopenharmony_ci return -EINVAL; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0200) { 4368c2ecf20Sopenharmony_ci pr_err("Sorry, pre-VBE 2.0 cards are not supported\n"); 4378c2ecf20Sopenharmony_ci return -EINVAL; 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!par->vbe_ib.mode_list_ptr) { 4418c2ecf20Sopenharmony_ci pr_err("Missing mode list!\n"); 4428c2ecf20Sopenharmony_ci return -EINVAL; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci pr_info(""); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* 4488c2ecf20Sopenharmony_ci * Convert string pointers and the mode list pointer into 4498c2ecf20Sopenharmony_ci * usable addresses. Print informational messages about the 4508c2ecf20Sopenharmony_ci * video adapter and its vendor. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_vendor_name_ptr) 4538c2ecf20Sopenharmony_ci pr_cont("%s, ", 4548c2ecf20Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_vendor_name_ptr); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_product_name_ptr) 4578c2ecf20Sopenharmony_ci pr_cont("%s, ", 4588c2ecf20Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_product_name_ptr); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_product_rev_ptr) 4618c2ecf20Sopenharmony_ci pr_cont("%s, ", 4628c2ecf20Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_product_rev_ptr); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_string_ptr) 4658c2ecf20Sopenharmony_ci pr_cont("OEM: %s, ", 4668c2ecf20Sopenharmony_ci ((char *)task->buf) + par->vbe_ib.oem_string_ptr); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci pr_cont("VBE v%d.%d\n", 4698c2ecf20Sopenharmony_ci (par->vbe_ib.vbe_version & 0xff00) >> 8, 4708c2ecf20Sopenharmony_ci par->vbe_ib.vbe_version & 0xff); 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci return 0; 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic int uvesafb_vbe_getmodes(struct uvesafb_ktask *task, 4768c2ecf20Sopenharmony_ci struct uvesafb_par *par) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci int off = 0, err; 4798c2ecf20Sopenharmony_ci u16 *mode; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci par->vbe_modes_cnt = 0; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* Count available modes. */ 4848c2ecf20Sopenharmony_ci mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); 4858c2ecf20Sopenharmony_ci while (*mode != 0xffff) { 4868c2ecf20Sopenharmony_ci par->vbe_modes_cnt++; 4878c2ecf20Sopenharmony_ci mode++; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci par->vbe_modes = kcalloc(par->vbe_modes_cnt, 4918c2ecf20Sopenharmony_ci sizeof(struct vbe_mode_ib), 4928c2ecf20Sopenharmony_ci GFP_KERNEL); 4938c2ecf20Sopenharmony_ci if (!par->vbe_modes) 4948c2ecf20Sopenharmony_ci return -ENOMEM; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* Get info about all available modes. */ 4978c2ecf20Sopenharmony_ci mode = (u16 *) (((u8 *)&par->vbe_ib) + par->vbe_ib.mode_list_ptr); 4988c2ecf20Sopenharmony_ci while (*mode != 0xffff) { 4998c2ecf20Sopenharmony_ci struct vbe_mode_ib *mib; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci uvesafb_reset(task); 5028c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f01; 5038c2ecf20Sopenharmony_ci task->t.regs.ecx = (u32) *mode; 5048c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESDI; 5058c2ecf20Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_mode_ib); 5068c2ecf20Sopenharmony_ci task->buf = par->vbe_modes + off; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 5098c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 5108c2ecf20Sopenharmony_ci pr_warn("Getting mode info block for mode 0x%x failed (eax=0x%x, err=%d)\n", 5118c2ecf20Sopenharmony_ci *mode, (u32)task->t.regs.eax, err); 5128c2ecf20Sopenharmony_ci mode++; 5138c2ecf20Sopenharmony_ci par->vbe_modes_cnt--; 5148c2ecf20Sopenharmony_ci continue; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci mib = task->buf; 5188c2ecf20Sopenharmony_ci mib->mode_id = *mode; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci /* 5218c2ecf20Sopenharmony_ci * We only want modes that are supported with the current 5228c2ecf20Sopenharmony_ci * hardware configuration, color, graphics and that have 5238c2ecf20Sopenharmony_ci * support for the LFB. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci if ((mib->mode_attr & VBE_MODE_MASK) == VBE_MODE_MASK && 5268c2ecf20Sopenharmony_ci mib->bits_per_pixel >= 8) 5278c2ecf20Sopenharmony_ci off++; 5288c2ecf20Sopenharmony_ci else 5298c2ecf20Sopenharmony_ci par->vbe_modes_cnt--; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci mode++; 5328c2ecf20Sopenharmony_ci mib->depth = mib->red_len + mib->green_len + mib->blue_len; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* 5358c2ecf20Sopenharmony_ci * Handle 8bpp modes and modes with broken color component 5368c2ecf20Sopenharmony_ci * lengths. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_ci if (mib->depth == 0 || (mib->depth == 24 && 5398c2ecf20Sopenharmony_ci mib->bits_per_pixel == 32)) 5408c2ecf20Sopenharmony_ci mib->depth = mib->bits_per_pixel; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (par->vbe_modes_cnt > 0) 5448c2ecf20Sopenharmony_ci return 0; 5458c2ecf20Sopenharmony_ci else 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci/* 5508c2ecf20Sopenharmony_ci * The Protected Mode Interface is 32-bit x86 code, so we only run it on 5518c2ecf20Sopenharmony_ci * x86 and not x86_64. 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32 5548c2ecf20Sopenharmony_cistatic int uvesafb_vbe_getpmi(struct uvesafb_ktask *task, 5558c2ecf20Sopenharmony_ci struct uvesafb_par *par) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci int i, err; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci uvesafb_reset(task); 5608c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f0a; 5618c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0; 5628c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x4f || task->t.regs.es < 0xc000) { 5658c2ecf20Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 5668c2ecf20Sopenharmony_ci } else { 5678c2ecf20Sopenharmony_ci par->pmi_base = (u16 *)phys_to_virt(((u32)task->t.regs.es << 4) 5688c2ecf20Sopenharmony_ci + task->t.regs.edi); 5698c2ecf20Sopenharmony_ci par->pmi_start = (u8 *)par->pmi_base + par->pmi_base[1]; 5708c2ecf20Sopenharmony_ci par->pmi_pal = (u8 *)par->pmi_base + par->pmi_base[2]; 5718c2ecf20Sopenharmony_ci pr_info("protected mode interface info at %04x:%04x\n", 5728c2ecf20Sopenharmony_ci (u16)task->t.regs.es, (u16)task->t.regs.edi); 5738c2ecf20Sopenharmony_ci pr_info("pmi: set display start = %p, set palette = %p\n", 5748c2ecf20Sopenharmony_ci par->pmi_start, par->pmi_pal); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (par->pmi_base[3]) { 5778c2ecf20Sopenharmony_ci pr_info("pmi: ports ="); 5788c2ecf20Sopenharmony_ci for (i = par->pmi_base[3]/2; 5798c2ecf20Sopenharmony_ci par->pmi_base[i] != 0xffff; i++) 5808c2ecf20Sopenharmony_ci pr_cont(" %x", par->pmi_base[i]); 5818c2ecf20Sopenharmony_ci pr_cont("\n"); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if (par->pmi_base[i] != 0xffff) { 5848c2ecf20Sopenharmony_ci pr_info("can't handle memory requests, pmi disabled\n"); 5858c2ecf20Sopenharmony_ci par->ypan = par->pmi_setpal = 0; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_32 */ 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci/* 5948c2ecf20Sopenharmony_ci * Check whether a video mode is supported by the Video BIOS and is 5958c2ecf20Sopenharmony_ci * compatible with the monitor limits. 5968c2ecf20Sopenharmony_ci */ 5978c2ecf20Sopenharmony_cistatic int uvesafb_is_valid_mode(struct fb_videomode *mode, 5988c2ecf20Sopenharmony_ci struct fb_info *info) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci if (info->monspecs.gtf) { 6018c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, mode); 6028c2ecf20Sopenharmony_ci if (fb_validate_mode(&info->var, info)) 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci if (uvesafb_vbe_find_mode(info->par, mode->xres, mode->yres, 8, 6078c2ecf20Sopenharmony_ci UVESAFB_EXACT_RES) == -1) 6088c2ecf20Sopenharmony_ci return 0; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci return 1; 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic int uvesafb_vbe_getedid(struct uvesafb_ktask *task, struct fb_info *info) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 6168c2ecf20Sopenharmony_ci int err = 0; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (noedid || par->vbe_ib.vbe_version < 0x0300) 6198c2ecf20Sopenharmony_ci return -EINVAL; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f15; 6228c2ecf20Sopenharmony_ci task->t.regs.ebx = 0; 6238c2ecf20Sopenharmony_ci task->t.regs.ecx = 0; 6248c2ecf20Sopenharmony_ci task->t.buf_len = 0; 6258c2ecf20Sopenharmony_ci task->t.flags = 0; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x004f || err) 6308c2ecf20Sopenharmony_ci return -EINVAL; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if ((task->t.regs.ebx & 0x3) == 3) { 6338c2ecf20Sopenharmony_ci pr_info("VBIOS/hardware supports both DDC1 and DDC2 transfers\n"); 6348c2ecf20Sopenharmony_ci } else if ((task->t.regs.ebx & 0x3) == 2) { 6358c2ecf20Sopenharmony_ci pr_info("VBIOS/hardware supports DDC2 transfers\n"); 6368c2ecf20Sopenharmony_ci } else if ((task->t.regs.ebx & 0x3) == 1) { 6378c2ecf20Sopenharmony_ci pr_info("VBIOS/hardware supports DDC1 transfers\n"); 6388c2ecf20Sopenharmony_ci } else { 6398c2ecf20Sopenharmony_ci pr_info("VBIOS/hardware doesn't support DDC transfers\n"); 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f15; 6448c2ecf20Sopenharmony_ci task->t.regs.ebx = 1; 6458c2ecf20Sopenharmony_ci task->t.regs.ecx = task->t.regs.edx = 0; 6468c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_RET | TF_BUF_ESDI; 6478c2ecf20Sopenharmony_ci task->t.buf_len = EDID_LENGTH; 6488c2ecf20Sopenharmony_ci task->buf = kzalloc(EDID_LENGTH, GFP_KERNEL); 6498c2ecf20Sopenharmony_ci if (!task->buf) 6508c2ecf20Sopenharmony_ci return -ENOMEM; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if ((task->t.regs.eax & 0xffff) == 0x004f && !err) { 6558c2ecf20Sopenharmony_ci fb_edid_to_monspecs(task->buf, &info->monspecs); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci if (info->monspecs.vfmax && info->monspecs.hfmax) { 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * If the maximum pixel clock wasn't specified in 6608c2ecf20Sopenharmony_ci * the EDID block, set it to 300 MHz. 6618c2ecf20Sopenharmony_ci */ 6628c2ecf20Sopenharmony_ci if (info->monspecs.dclkmax == 0) 6638c2ecf20Sopenharmony_ci info->monspecs.dclkmax = 300 * 1000000; 6648c2ecf20Sopenharmony_ci info->monspecs.gtf = 1; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci } else { 6678c2ecf20Sopenharmony_ci err = -EINVAL; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci kfree(task->buf); 6718c2ecf20Sopenharmony_ci return err; 6728c2ecf20Sopenharmony_ci} 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_cistatic void uvesafb_vbe_getmonspecs(struct uvesafb_ktask *task, 6758c2ecf20Sopenharmony_ci struct fb_info *info) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 6788c2ecf20Sopenharmony_ci int i; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci memset(&info->monspecs, 0, sizeof(info->monspecs)); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci /* 6838c2ecf20Sopenharmony_ci * If we don't get all necessary data from the EDID block, 6848c2ecf20Sopenharmony_ci * mark it as incompatible with the GTF and set nocrtc so 6858c2ecf20Sopenharmony_ci * that we always use the default BIOS refresh rate. 6868c2ecf20Sopenharmony_ci */ 6878c2ecf20Sopenharmony_ci if (uvesafb_vbe_getedid(task, info)) { 6888c2ecf20Sopenharmony_ci info->monspecs.gtf = 0; 6898c2ecf20Sopenharmony_ci par->nocrtc = 1; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci /* Kernel command line overrides. */ 6938c2ecf20Sopenharmony_ci if (maxclk) 6948c2ecf20Sopenharmony_ci info->monspecs.dclkmax = maxclk * 1000000; 6958c2ecf20Sopenharmony_ci if (maxvf) 6968c2ecf20Sopenharmony_ci info->monspecs.vfmax = maxvf; 6978c2ecf20Sopenharmony_ci if (maxhf) 6988c2ecf20Sopenharmony_ci info->monspecs.hfmax = maxhf * 1000; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci /* 7018c2ecf20Sopenharmony_ci * In case DDC transfers are not supported, the user can provide 7028c2ecf20Sopenharmony_ci * monitor limits manually. Lower limits are set to "safe" values. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_ci if (info->monspecs.gtf == 0 && maxclk && maxvf && maxhf) { 7058c2ecf20Sopenharmony_ci info->monspecs.dclkmin = 0; 7068c2ecf20Sopenharmony_ci info->monspecs.vfmin = 60; 7078c2ecf20Sopenharmony_ci info->monspecs.hfmin = 29000; 7088c2ecf20Sopenharmony_ci info->monspecs.gtf = 1; 7098c2ecf20Sopenharmony_ci par->nocrtc = 0; 7108c2ecf20Sopenharmony_ci } 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (info->monspecs.gtf) 7138c2ecf20Sopenharmony_ci pr_info("monitor limits: vf = %d Hz, hf = %d kHz, clk = %d MHz\n", 7148c2ecf20Sopenharmony_ci info->monspecs.vfmax, 7158c2ecf20Sopenharmony_ci (int)(info->monspecs.hfmax / 1000), 7168c2ecf20Sopenharmony_ci (int)(info->monspecs.dclkmax / 1000000)); 7178c2ecf20Sopenharmony_ci else 7188c2ecf20Sopenharmony_ci pr_info("no monitor limits have been set, default refresh rate will be used\n"); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Add VBE modes to the modelist. */ 7218c2ecf20Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 7228c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 7238c2ecf20Sopenharmony_ci struct vbe_mode_ib *mode; 7248c2ecf20Sopenharmony_ci struct fb_videomode vmode; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mode = &par->vbe_modes[i]; 7278c2ecf20Sopenharmony_ci memset(&var, 0, sizeof(var)); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci var.xres = mode->x_res; 7308c2ecf20Sopenharmony_ci var.yres = mode->y_res; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, &var, info); 7338c2ecf20Sopenharmony_ci fb_var_to_videomode(&vmode, &var); 7348c2ecf20Sopenharmony_ci fb_add_videomode(&vmode, &info->modelist); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Add valid VESA modes to our modelist. */ 7388c2ecf20Sopenharmony_ci for (i = 0; i < VESA_MODEDB_SIZE; i++) { 7398c2ecf20Sopenharmony_ci if (uvesafb_is_valid_mode((struct fb_videomode *) 7408c2ecf20Sopenharmony_ci &vesa_modes[i], info)) 7418c2ecf20Sopenharmony_ci fb_add_videomode(&vesa_modes[i], &info->modelist); 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci for (i = 0; i < info->monspecs.modedb_len; i++) { 7458c2ecf20Sopenharmony_ci if (uvesafb_is_valid_mode(&info->monspecs.modedb[i], info)) 7468c2ecf20Sopenharmony_ci fb_add_videomode(&info->monspecs.modedb[i], 7478c2ecf20Sopenharmony_ci &info->modelist); 7488c2ecf20Sopenharmony_ci } 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci return; 7518c2ecf20Sopenharmony_ci} 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic void uvesafb_vbe_getstatesize(struct uvesafb_ktask *task, 7548c2ecf20Sopenharmony_ci struct uvesafb_par *par) 7558c2ecf20Sopenharmony_ci{ 7568c2ecf20Sopenharmony_ci int err; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci uvesafb_reset(task); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* 7618c2ecf20Sopenharmony_ci * Get the VBE state buffer size. We want all available 7628c2ecf20Sopenharmony_ci * hardware state data (CL = 0x0f). 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f04; 7658c2ecf20Sopenharmony_ci task->t.regs.ecx = 0x000f; 7668c2ecf20Sopenharmony_ci task->t.regs.edx = 0x0000; 7678c2ecf20Sopenharmony_ci task->t.flags = 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 7728c2ecf20Sopenharmony_ci pr_warn("VBE state buffer size cannot be determined (eax=0x%x, err=%d)\n", 7738c2ecf20Sopenharmony_ci task->t.regs.eax, err); 7748c2ecf20Sopenharmony_ci par->vbe_state_size = 0; 7758c2ecf20Sopenharmony_ci return; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci par->vbe_state_size = 64 * (task->t.regs.ebx & 0xffff); 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic int uvesafb_vbe_init(struct fb_info *info) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci struct uvesafb_ktask *task = NULL; 7848c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 7858c2ecf20Sopenharmony_ci int err; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci task = uvesafb_prep(); 7888c2ecf20Sopenharmony_ci if (!task) 7898c2ecf20Sopenharmony_ci return -ENOMEM; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci err = uvesafb_vbe_getinfo(task, par); 7928c2ecf20Sopenharmony_ci if (err) 7938c2ecf20Sopenharmony_ci goto out; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci err = uvesafb_vbe_getmodes(task, par); 7968c2ecf20Sopenharmony_ci if (err) 7978c2ecf20Sopenharmony_ci goto out; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci par->nocrtc = nocrtc; 8008c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32 8018c2ecf20Sopenharmony_ci par->pmi_setpal = pmi_setpal; 8028c2ecf20Sopenharmony_ci par->ypan = ypan; 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci if (par->pmi_setpal || par->ypan) { 8058c2ecf20Sopenharmony_ci if (__supported_pte_mask & _PAGE_NX) { 8068c2ecf20Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 8078c2ecf20Sopenharmony_ci pr_warn("NX protection is active, better not use the PMI\n"); 8088c2ecf20Sopenharmony_ci } else { 8098c2ecf20Sopenharmony_ci uvesafb_vbe_getpmi(task, par); 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci#else 8138c2ecf20Sopenharmony_ci /* The protected mode interface is not available on non-x86. */ 8148c2ecf20Sopenharmony_ci par->pmi_setpal = par->ypan = 0; 8158c2ecf20Sopenharmony_ci#endif 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&info->modelist); 8188c2ecf20Sopenharmony_ci uvesafb_vbe_getmonspecs(task, info); 8198c2ecf20Sopenharmony_ci uvesafb_vbe_getstatesize(task, par); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ciout: uvesafb_free(task); 8228c2ecf20Sopenharmony_ci return err; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic int uvesafb_vbe_init_mode(struct fb_info *info) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci struct list_head *pos; 8288c2ecf20Sopenharmony_ci struct fb_modelist *modelist; 8298c2ecf20Sopenharmony_ci struct fb_videomode *mode; 8308c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 8318c2ecf20Sopenharmony_ci int i, modeid; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci /* Has the user requested a specific VESA mode? */ 8348c2ecf20Sopenharmony_ci if (vbemode) { 8358c2ecf20Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 8368c2ecf20Sopenharmony_ci if (par->vbe_modes[i].mode_id == vbemode) { 8378c2ecf20Sopenharmony_ci modeid = i; 8388c2ecf20Sopenharmony_ci uvesafb_setup_var(&info->var, info, 8398c2ecf20Sopenharmony_ci &par->vbe_modes[modeid]); 8408c2ecf20Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 8418c2ecf20Sopenharmony_ci &info->var, info); 8428c2ecf20Sopenharmony_ci /* 8438c2ecf20Sopenharmony_ci * With pixclock set to 0, the default BIOS 8448c2ecf20Sopenharmony_ci * timings will be used in set_par(). 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_ci info->var.pixclock = 0; 8478c2ecf20Sopenharmony_ci goto gotmode; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci } 8508c2ecf20Sopenharmony_ci pr_info("requested VBE mode 0x%x is unavailable\n", vbemode); 8518c2ecf20Sopenharmony_ci vbemode = 0; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* Count the modes in the modelist */ 8558c2ecf20Sopenharmony_ci i = 0; 8568c2ecf20Sopenharmony_ci list_for_each(pos, &info->modelist) 8578c2ecf20Sopenharmony_ci i++; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* 8608c2ecf20Sopenharmony_ci * Convert the modelist into a modedb so that we can use it with 8618c2ecf20Sopenharmony_ci * fb_find_mode(). 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci mode = kcalloc(i, sizeof(*mode), GFP_KERNEL); 8648c2ecf20Sopenharmony_ci if (mode) { 8658c2ecf20Sopenharmony_ci i = 0; 8668c2ecf20Sopenharmony_ci list_for_each(pos, &info->modelist) { 8678c2ecf20Sopenharmony_ci modelist = list_entry(pos, struct fb_modelist, list); 8688c2ecf20Sopenharmony_ci mode[i] = modelist->mode; 8698c2ecf20Sopenharmony_ci i++; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (!mode_option) 8738c2ecf20Sopenharmony_ci mode_option = UVESAFB_DEFAULT_MODE; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci i = fb_find_mode(&info->var, info, mode_option, mode, i, 8768c2ecf20Sopenharmony_ci NULL, 8); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci kfree(mode); 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci /* fb_find_mode() failed */ 8828c2ecf20Sopenharmony_ci if (i == 0) { 8838c2ecf20Sopenharmony_ci info->var.xres = 640; 8848c2ecf20Sopenharmony_ci info->var.yres = 480; 8858c2ecf20Sopenharmony_ci mode = (struct fb_videomode *) 8868c2ecf20Sopenharmony_ci fb_find_best_mode(&info->var, &info->modelist); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci if (mode) { 8898c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, mode); 8908c2ecf20Sopenharmony_ci } else { 8918c2ecf20Sopenharmony_ci modeid = par->vbe_modes[0].mode_id; 8928c2ecf20Sopenharmony_ci uvesafb_setup_var(&info->var, info, 8938c2ecf20Sopenharmony_ci &par->vbe_modes[modeid]); 8948c2ecf20Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 8958c2ecf20Sopenharmony_ci &info->var, info); 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci goto gotmode; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci /* Look for a matching VBE mode. */ 9028c2ecf20Sopenharmony_ci modeid = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, 9038c2ecf20Sopenharmony_ci info->var.bits_per_pixel, UVESAFB_EXACT_RES); 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci if (modeid == -1) 9068c2ecf20Sopenharmony_ci return -EINVAL; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci uvesafb_setup_var(&info->var, info, &par->vbe_modes[modeid]); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_cigotmode: 9118c2ecf20Sopenharmony_ci /* 9128c2ecf20Sopenharmony_ci * If we are not VBE3.0+ compliant, we're done -- the BIOS will 9138c2ecf20Sopenharmony_ci * ignore our timings anyway. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0300 || par->nocrtc) 9168c2ecf20Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, 9178c2ecf20Sopenharmony_ci &info->var, info); 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci return modeid; 9208c2ecf20Sopenharmony_ci} 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_cistatic int uvesafb_setpalette(struct uvesafb_pal_entry *entries, int count, 9238c2ecf20Sopenharmony_ci int start, struct fb_info *info) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 9268c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 9278c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 9288c2ecf20Sopenharmony_ci int i = par->mode_idx; 9298c2ecf20Sopenharmony_ci#endif 9308c2ecf20Sopenharmony_ci int err = 0; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci /* 9338c2ecf20Sopenharmony_ci * We support palette modifications for 8 bpp modes only, so 9348c2ecf20Sopenharmony_ci * there can never be more than 256 entries. 9358c2ecf20Sopenharmony_ci */ 9368c2ecf20Sopenharmony_ci if (start + count > 256) 9378c2ecf20Sopenharmony_ci return -EINVAL; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 9408c2ecf20Sopenharmony_ci /* Use VGA registers if mode is VGA-compatible. */ 9418c2ecf20Sopenharmony_ci if (i >= 0 && i < par->vbe_modes_cnt && 9428c2ecf20Sopenharmony_ci par->vbe_modes[i].mode_attr & VBE_MODE_VGACOMPAT) { 9438c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) { 9448c2ecf20Sopenharmony_ci outb_p(start + i, dac_reg); 9458c2ecf20Sopenharmony_ci outb_p(entries[i].red, dac_val); 9468c2ecf20Sopenharmony_ci outb_p(entries[i].green, dac_val); 9478c2ecf20Sopenharmony_ci outb_p(entries[i].blue, dac_val); 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32 9518c2ecf20Sopenharmony_ci else if (par->pmi_setpal) { 9528c2ecf20Sopenharmony_ci __asm__ __volatile__( 9538c2ecf20Sopenharmony_ci "call *(%%esi)" 9548c2ecf20Sopenharmony_ci : /* no return value */ 9558c2ecf20Sopenharmony_ci : "a" (0x4f09), /* EAX */ 9568c2ecf20Sopenharmony_ci "b" (0), /* EBX */ 9578c2ecf20Sopenharmony_ci "c" (count), /* ECX */ 9588c2ecf20Sopenharmony_ci "d" (start), /* EDX */ 9598c2ecf20Sopenharmony_ci "D" (entries), /* EDI */ 9608c2ecf20Sopenharmony_ci "S" (&par->pmi_pal)); /* ESI */ 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci#endif /* CONFIG_X86_32 */ 9638c2ecf20Sopenharmony_ci else 9648c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 */ 9658c2ecf20Sopenharmony_ci { 9668c2ecf20Sopenharmony_ci task = uvesafb_prep(); 9678c2ecf20Sopenharmony_ci if (!task) 9688c2ecf20Sopenharmony_ci return -ENOMEM; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f09; 9718c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0; 9728c2ecf20Sopenharmony_ci task->t.regs.ecx = count; 9738c2ecf20Sopenharmony_ci task->t.regs.edx = start; 9748c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_ESDI; 9758c2ecf20Sopenharmony_ci task->t.buf_len = sizeof(struct uvesafb_pal_entry) * count; 9768c2ecf20Sopenharmony_ci task->buf = entries; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 9798c2ecf20Sopenharmony_ci if ((task->t.regs.eax & 0xffff) != 0x004f) 9808c2ecf20Sopenharmony_ci err = 1; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci uvesafb_free(task); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci return err; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_cistatic int uvesafb_setcolreg(unsigned regno, unsigned red, unsigned green, 9888c2ecf20Sopenharmony_ci unsigned blue, unsigned transp, 9898c2ecf20Sopenharmony_ci struct fb_info *info) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci struct uvesafb_pal_entry entry; 9928c2ecf20Sopenharmony_ci int shift = 16 - dac_width; 9938c2ecf20Sopenharmony_ci int err = 0; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci if (regno >= info->cmap.len) 9968c2ecf20Sopenharmony_ci return -EINVAL; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 8) { 9998c2ecf20Sopenharmony_ci entry.red = red >> shift; 10008c2ecf20Sopenharmony_ci entry.green = green >> shift; 10018c2ecf20Sopenharmony_ci entry.blue = blue >> shift; 10028c2ecf20Sopenharmony_ci entry.pad = 0; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci err = uvesafb_setpalette(&entry, 1, regno, info); 10058c2ecf20Sopenharmony_ci } else if (regno < 16) { 10068c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 10078c2ecf20Sopenharmony_ci case 16: 10088c2ecf20Sopenharmony_ci if (info->var.red.offset == 10) { 10098c2ecf20Sopenharmony_ci /* 1:5:5:5 */ 10108c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 10118c2ecf20Sopenharmony_ci ((red & 0xf800) >> 1) | 10128c2ecf20Sopenharmony_ci ((green & 0xf800) >> 6) | 10138c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 10148c2ecf20Sopenharmony_ci } else { 10158c2ecf20Sopenharmony_ci /* 0:5:6:5 */ 10168c2ecf20Sopenharmony_ci ((u32 *) (info->pseudo_palette))[regno] = 10178c2ecf20Sopenharmony_ci ((red & 0xf800) ) | 10188c2ecf20Sopenharmony_ci ((green & 0xfc00) >> 5) | 10198c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci case 24: 10248c2ecf20Sopenharmony_ci case 32: 10258c2ecf20Sopenharmony_ci red >>= 8; 10268c2ecf20Sopenharmony_ci green >>= 8; 10278c2ecf20Sopenharmony_ci blue >>= 8; 10288c2ecf20Sopenharmony_ci ((u32 *)(info->pseudo_palette))[regno] = 10298c2ecf20Sopenharmony_ci (red << info->var.red.offset) | 10308c2ecf20Sopenharmony_ci (green << info->var.green.offset) | 10318c2ecf20Sopenharmony_ci (blue << info->var.blue.offset); 10328c2ecf20Sopenharmony_ci break; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci return err; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic int uvesafb_setcmap(struct fb_cmap *cmap, struct fb_info *info) 10398c2ecf20Sopenharmony_ci{ 10408c2ecf20Sopenharmony_ci struct uvesafb_pal_entry *entries; 10418c2ecf20Sopenharmony_ci int shift = 16 - dac_width; 10428c2ecf20Sopenharmony_ci int i, err = 0; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (info->var.bits_per_pixel == 8) { 10458c2ecf20Sopenharmony_ci if (cmap->start + cmap->len > info->cmap.start + 10468c2ecf20Sopenharmony_ci info->cmap.len || cmap->start < info->cmap.start) 10478c2ecf20Sopenharmony_ci return -EINVAL; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci entries = kmalloc_array(cmap->len, sizeof(*entries), 10508c2ecf20Sopenharmony_ci GFP_KERNEL); 10518c2ecf20Sopenharmony_ci if (!entries) 10528c2ecf20Sopenharmony_ci return -ENOMEM; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci for (i = 0; i < cmap->len; i++) { 10558c2ecf20Sopenharmony_ci entries[i].red = cmap->red[i] >> shift; 10568c2ecf20Sopenharmony_ci entries[i].green = cmap->green[i] >> shift; 10578c2ecf20Sopenharmony_ci entries[i].blue = cmap->blue[i] >> shift; 10588c2ecf20Sopenharmony_ci entries[i].pad = 0; 10598c2ecf20Sopenharmony_ci } 10608c2ecf20Sopenharmony_ci err = uvesafb_setpalette(entries, cmap->len, cmap->start, info); 10618c2ecf20Sopenharmony_ci kfree(entries); 10628c2ecf20Sopenharmony_ci } else { 10638c2ecf20Sopenharmony_ci /* 10648c2ecf20Sopenharmony_ci * For modes with bpp > 8, we only set the pseudo palette in 10658c2ecf20Sopenharmony_ci * the fb_info struct. We rely on uvesafb_setcolreg to do all 10668c2ecf20Sopenharmony_ci * sanity checking. 10678c2ecf20Sopenharmony_ci */ 10688c2ecf20Sopenharmony_ci for (i = 0; i < cmap->len; i++) { 10698c2ecf20Sopenharmony_ci err |= uvesafb_setcolreg(cmap->start + i, cmap->red[i], 10708c2ecf20Sopenharmony_ci cmap->green[i], cmap->blue[i], 10718c2ecf20Sopenharmony_ci 0, info); 10728c2ecf20Sopenharmony_ci } 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci return err; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic int uvesafb_pan_display(struct fb_var_screeninfo *var, 10788c2ecf20Sopenharmony_ci struct fb_info *info) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_32 10818c2ecf20Sopenharmony_ci int offset; 10828c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci offset = (var->yoffset * info->fix.line_length + var->xoffset) / 4; 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci /* 10878c2ecf20Sopenharmony_ci * It turns out it's not the best idea to do panning via vm86, 10888c2ecf20Sopenharmony_ci * so we only allow it if we have a PMI. 10898c2ecf20Sopenharmony_ci */ 10908c2ecf20Sopenharmony_ci if (par->pmi_start) { 10918c2ecf20Sopenharmony_ci __asm__ __volatile__( 10928c2ecf20Sopenharmony_ci "call *(%%edi)" 10938c2ecf20Sopenharmony_ci : /* no return value */ 10948c2ecf20Sopenharmony_ci : "a" (0x4f07), /* EAX */ 10958c2ecf20Sopenharmony_ci "b" (0), /* EBX */ 10968c2ecf20Sopenharmony_ci "c" (offset), /* ECX */ 10978c2ecf20Sopenharmony_ci "d" (offset >> 16), /* EDX */ 10988c2ecf20Sopenharmony_ci "D" (&par->pmi_start)); /* EDI */ 10998c2ecf20Sopenharmony_ci } 11008c2ecf20Sopenharmony_ci#endif 11018c2ecf20Sopenharmony_ci return 0; 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int uvesafb_blank(int blank, struct fb_info *info) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 11078c2ecf20Sopenharmony_ci int err = 1; 11088c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 11098c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci if (par->vbe_ib.capabilities & VBE_CAP_VGACOMPAT) { 11128c2ecf20Sopenharmony_ci int loop = 10000; 11138c2ecf20Sopenharmony_ci u8 seq = 0, crtc17 = 0; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (blank == FB_BLANK_POWERDOWN) { 11168c2ecf20Sopenharmony_ci seq = 0x20; 11178c2ecf20Sopenharmony_ci crtc17 = 0x00; 11188c2ecf20Sopenharmony_ci err = 0; 11198c2ecf20Sopenharmony_ci } else { 11208c2ecf20Sopenharmony_ci seq = 0x00; 11218c2ecf20Sopenharmony_ci crtc17 = 0x80; 11228c2ecf20Sopenharmony_ci err = (blank == FB_BLANK_UNBLANK) ? 0 : -EINVAL; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci vga_wseq(NULL, 0x00, 0x01); 11268c2ecf20Sopenharmony_ci seq |= vga_rseq(NULL, 0x01) & ~0x20; 11278c2ecf20Sopenharmony_ci vga_wseq(NULL, 0x00, seq); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci crtc17 |= vga_rcrt(NULL, 0x17) & ~0x80; 11308c2ecf20Sopenharmony_ci while (loop--); 11318c2ecf20Sopenharmony_ci vga_wcrt(NULL, 0x17, crtc17); 11328c2ecf20Sopenharmony_ci vga_wseq(NULL, 0x00, 0x03); 11338c2ecf20Sopenharmony_ci } else 11348c2ecf20Sopenharmony_ci#endif /* CONFIG_X86 */ 11358c2ecf20Sopenharmony_ci { 11368c2ecf20Sopenharmony_ci task = uvesafb_prep(); 11378c2ecf20Sopenharmony_ci if (!task) 11388c2ecf20Sopenharmony_ci return -ENOMEM; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f10; 11418c2ecf20Sopenharmony_ci switch (blank) { 11428c2ecf20Sopenharmony_ci case FB_BLANK_UNBLANK: 11438c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0001; 11448c2ecf20Sopenharmony_ci break; 11458c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 11468c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0101; /* standby */ 11478c2ecf20Sopenharmony_ci break; 11488c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 11498c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0401; /* powerdown */ 11508c2ecf20Sopenharmony_ci break; 11518c2ecf20Sopenharmony_ci default: 11528c2ecf20Sopenharmony_ci goto out; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 11568c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) 11578c2ecf20Sopenharmony_ci err = 1; 11588c2ecf20Sopenharmony_ciout: uvesafb_free(task); 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci return err; 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int uvesafb_open(struct fb_info *info, int user) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 11668c2ecf20Sopenharmony_ci int cnt = atomic_read(&par->ref_count); 11678c2ecf20Sopenharmony_ci u8 *buf = NULL; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci if (!cnt && par->vbe_state_size) { 11708c2ecf20Sopenharmony_ci buf = uvesafb_vbe_state_save(par); 11718c2ecf20Sopenharmony_ci if (IS_ERR(buf)) { 11728c2ecf20Sopenharmony_ci pr_warn("save hardware state failed, error code is %ld!\n", 11738c2ecf20Sopenharmony_ci PTR_ERR(buf)); 11748c2ecf20Sopenharmony_ci } else { 11758c2ecf20Sopenharmony_ci par->vbe_state_orig = buf; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci atomic_inc(&par->ref_count); 11808c2ecf20Sopenharmony_ci return 0; 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic int uvesafb_release(struct fb_info *info, int user) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci struct uvesafb_ktask *task = NULL; 11868c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 11878c2ecf20Sopenharmony_ci int cnt = atomic_read(&par->ref_count); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (!cnt) 11908c2ecf20Sopenharmony_ci return -EINVAL; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci if (cnt != 1) 11938c2ecf20Sopenharmony_ci goto out; 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci task = uvesafb_prep(); 11968c2ecf20Sopenharmony_ci if (!task) 11978c2ecf20Sopenharmony_ci goto out; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* First, try to set the standard 80x25 text mode. */ 12008c2ecf20Sopenharmony_ci task->t.regs.eax = 0x0003; 12018c2ecf20Sopenharmony_ci uvesafb_exec(task); 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci /* 12048c2ecf20Sopenharmony_ci * Now try to restore whatever hardware state we might have 12058c2ecf20Sopenharmony_ci * saved when the fb device was first opened. 12068c2ecf20Sopenharmony_ci */ 12078c2ecf20Sopenharmony_ci uvesafb_vbe_state_restore(par, par->vbe_state_orig); 12088c2ecf20Sopenharmony_ciout: 12098c2ecf20Sopenharmony_ci atomic_dec(&par->ref_count); 12108c2ecf20Sopenharmony_ci uvesafb_free(task); 12118c2ecf20Sopenharmony_ci return 0; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic int uvesafb_set_par(struct fb_info *info) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 12178c2ecf20Sopenharmony_ci struct uvesafb_ktask *task = NULL; 12188c2ecf20Sopenharmony_ci struct vbe_crtc_ib *crtc = NULL; 12198c2ecf20Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 12208c2ecf20Sopenharmony_ci int i, err = 0, depth = info->var.bits_per_pixel; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci if (depth > 8 && depth != 32) 12238c2ecf20Sopenharmony_ci depth = info->var.red.length + info->var.green.length + 12248c2ecf20Sopenharmony_ci info->var.blue.length; 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci i = uvesafb_vbe_find_mode(par, info->var.xres, info->var.yres, depth, 12278c2ecf20Sopenharmony_ci UVESAFB_EXACT_RES | UVESAFB_EXACT_DEPTH); 12288c2ecf20Sopenharmony_ci if (i >= 0) 12298c2ecf20Sopenharmony_ci mode = &par->vbe_modes[i]; 12308c2ecf20Sopenharmony_ci else 12318c2ecf20Sopenharmony_ci return -EINVAL; 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci task = uvesafb_prep(); 12348c2ecf20Sopenharmony_ci if (!task) 12358c2ecf20Sopenharmony_ci return -ENOMEM; 12368c2ecf20Sopenharmony_cisetmode: 12378c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f02; 12388c2ecf20Sopenharmony_ci task->t.regs.ebx = mode->mode_id | 0x4000; /* use LFB */ 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci if (par->vbe_ib.vbe_version >= 0x0300 && !par->nocrtc && 12418c2ecf20Sopenharmony_ci info->var.pixclock != 0) { 12428c2ecf20Sopenharmony_ci task->t.regs.ebx |= 0x0800; /* use CRTC data */ 12438c2ecf20Sopenharmony_ci task->t.flags = TF_BUF_ESDI; 12448c2ecf20Sopenharmony_ci crtc = kzalloc(sizeof(struct vbe_crtc_ib), GFP_KERNEL); 12458c2ecf20Sopenharmony_ci if (!crtc) { 12468c2ecf20Sopenharmony_ci err = -ENOMEM; 12478c2ecf20Sopenharmony_ci goto out; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci crtc->horiz_start = info->var.xres + info->var.right_margin; 12508c2ecf20Sopenharmony_ci crtc->horiz_end = crtc->horiz_start + info->var.hsync_len; 12518c2ecf20Sopenharmony_ci crtc->horiz_total = crtc->horiz_end + info->var.left_margin; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci crtc->vert_start = info->var.yres + info->var.lower_margin; 12548c2ecf20Sopenharmony_ci crtc->vert_end = crtc->vert_start + info->var.vsync_len; 12558c2ecf20Sopenharmony_ci crtc->vert_total = crtc->vert_end + info->var.upper_margin; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci crtc->pixel_clock = PICOS2KHZ(info->var.pixclock) * 1000; 12588c2ecf20Sopenharmony_ci crtc->refresh_rate = (u16)(100 * (crtc->pixel_clock / 12598c2ecf20Sopenharmony_ci (crtc->vert_total * crtc->horiz_total))); 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci if (info->var.vmode & FB_VMODE_DOUBLE) 12628c2ecf20Sopenharmony_ci crtc->flags |= 0x1; 12638c2ecf20Sopenharmony_ci if (info->var.vmode & FB_VMODE_INTERLACED) 12648c2ecf20Sopenharmony_ci crtc->flags |= 0x2; 12658c2ecf20Sopenharmony_ci if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) 12668c2ecf20Sopenharmony_ci crtc->flags |= 0x4; 12678c2ecf20Sopenharmony_ci if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) 12688c2ecf20Sopenharmony_ci crtc->flags |= 0x8; 12698c2ecf20Sopenharmony_ci memcpy(&par->crtc, crtc, sizeof(*crtc)); 12708c2ecf20Sopenharmony_ci } else { 12718c2ecf20Sopenharmony_ci memset(&par->crtc, 0, sizeof(*crtc)); 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci task->t.buf_len = sizeof(struct vbe_crtc_ib); 12758c2ecf20Sopenharmony_ci task->buf = &par->crtc; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 12788c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f) { 12798c2ecf20Sopenharmony_ci /* 12808c2ecf20Sopenharmony_ci * The mode switch might have failed because we tried to 12818c2ecf20Sopenharmony_ci * use our own timings. Try again with the default timings. 12828c2ecf20Sopenharmony_ci */ 12838c2ecf20Sopenharmony_ci if (crtc != NULL) { 12848c2ecf20Sopenharmony_ci pr_warn("mode switch failed (eax=0x%x, err=%d) - trying again with default timings\n", 12858c2ecf20Sopenharmony_ci task->t.regs.eax, err); 12868c2ecf20Sopenharmony_ci uvesafb_reset(task); 12878c2ecf20Sopenharmony_ci kfree(crtc); 12888c2ecf20Sopenharmony_ci crtc = NULL; 12898c2ecf20Sopenharmony_ci info->var.pixclock = 0; 12908c2ecf20Sopenharmony_ci goto setmode; 12918c2ecf20Sopenharmony_ci } else { 12928c2ecf20Sopenharmony_ci pr_err("mode switch failed (eax=0x%x, err=%d)\n", 12938c2ecf20Sopenharmony_ci task->t.regs.eax, err); 12948c2ecf20Sopenharmony_ci err = -EINVAL; 12958c2ecf20Sopenharmony_ci goto out; 12968c2ecf20Sopenharmony_ci } 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci par->mode_idx = i; 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* For 8bpp modes, always try to set the DAC to 8 bits. */ 13018c2ecf20Sopenharmony_ci if (par->vbe_ib.capabilities & VBE_CAP_CAN_SWITCH_DAC && 13028c2ecf20Sopenharmony_ci mode->bits_per_pixel <= 8) { 13038c2ecf20Sopenharmony_ci uvesafb_reset(task); 13048c2ecf20Sopenharmony_ci task->t.regs.eax = 0x4f08; 13058c2ecf20Sopenharmony_ci task->t.regs.ebx = 0x0800; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci err = uvesafb_exec(task); 13088c2ecf20Sopenharmony_ci if (err || (task->t.regs.eax & 0xffff) != 0x004f || 13098c2ecf20Sopenharmony_ci ((task->t.regs.ebx & 0xff00) >> 8) != 8) { 13108c2ecf20Sopenharmony_ci dac_width = 6; 13118c2ecf20Sopenharmony_ci } else { 13128c2ecf20Sopenharmony_ci dac_width = 8; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci info->fix.visual = (info->var.bits_per_pixel == 8) ? 13178c2ecf20Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 13188c2ecf20Sopenharmony_ci info->fix.line_length = mode->bytes_per_scan_line; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ciout: 13218c2ecf20Sopenharmony_ci kfree(crtc); 13228c2ecf20Sopenharmony_ci uvesafb_free(task); 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci return err; 13258c2ecf20Sopenharmony_ci} 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_cistatic void uvesafb_check_limits(struct fb_var_screeninfo *var, 13288c2ecf20Sopenharmony_ci struct fb_info *info) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci const struct fb_videomode *mode; 13318c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci /* 13348c2ecf20Sopenharmony_ci * If pixclock is set to 0, then we're using default BIOS timings 13358c2ecf20Sopenharmony_ci * and thus don't have to perform any checks here. 13368c2ecf20Sopenharmony_ci */ 13378c2ecf20Sopenharmony_ci if (!var->pixclock) 13388c2ecf20Sopenharmony_ci return; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (par->vbe_ib.vbe_version < 0x0300) { 13418c2ecf20Sopenharmony_ci fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON, 60, var, info); 13428c2ecf20Sopenharmony_ci return; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (!fb_validate_mode(var, info)) 13468c2ecf20Sopenharmony_ci return; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci mode = fb_find_best_mode(var, &info->modelist); 13498c2ecf20Sopenharmony_ci if (mode) { 13508c2ecf20Sopenharmony_ci if (mode->xres == var->xres && mode->yres == var->yres && 13518c2ecf20Sopenharmony_ci !(mode->vmode & (FB_VMODE_INTERLACED | FB_VMODE_DOUBLE))) { 13528c2ecf20Sopenharmony_ci fb_videomode_to_var(var, mode); 13538c2ecf20Sopenharmony_ci return; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci } 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (info->monspecs.gtf && !fb_get_mode(FB_MAXTIMINGS, 0, var, info)) 13588c2ecf20Sopenharmony_ci return; 13598c2ecf20Sopenharmony_ci /* Use default refresh rate */ 13608c2ecf20Sopenharmony_ci var->pixclock = 0; 13618c2ecf20Sopenharmony_ci} 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic int uvesafb_check_var(struct fb_var_screeninfo *var, 13648c2ecf20Sopenharmony_ci struct fb_info *info) 13658c2ecf20Sopenharmony_ci{ 13668c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 13678c2ecf20Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 13688c2ecf20Sopenharmony_ci int match = -1; 13698c2ecf20Sopenharmony_ci int depth = var->red.length + var->green.length + var->blue.length; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* 13728c2ecf20Sopenharmony_ci * Various apps will use bits_per_pixel to set the color depth, 13738c2ecf20Sopenharmony_ci * which is theoretically incorrect, but which we'll try to handle 13748c2ecf20Sopenharmony_ci * here. 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci if (depth == 0 || abs(depth - var->bits_per_pixel) >= 8) 13778c2ecf20Sopenharmony_ci depth = var->bits_per_pixel; 13788c2ecf20Sopenharmony_ci 13798c2ecf20Sopenharmony_ci match = uvesafb_vbe_find_mode(par, var->xres, var->yres, depth, 13808c2ecf20Sopenharmony_ci UVESAFB_EXACT_RES); 13818c2ecf20Sopenharmony_ci if (match == -1) 13828c2ecf20Sopenharmony_ci return -EINVAL; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci mode = &par->vbe_modes[match]; 13858c2ecf20Sopenharmony_ci uvesafb_setup_var(var, info, mode); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* 13888c2ecf20Sopenharmony_ci * Check whether we have remapped enough memory for this mode. 13898c2ecf20Sopenharmony_ci * We might be called at an early stage, when we haven't remapped 13908c2ecf20Sopenharmony_ci * any memory yet, in which case we simply skip the check. 13918c2ecf20Sopenharmony_ci */ 13928c2ecf20Sopenharmony_ci if (var->yres * mode->bytes_per_scan_line > info->fix.smem_len 13938c2ecf20Sopenharmony_ci && info->fix.smem_len) 13948c2ecf20Sopenharmony_ci return -EINVAL; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_DOUBLE) && 13978c2ecf20Sopenharmony_ci !(par->vbe_modes[match].mode_attr & 0x100)) 13988c2ecf20Sopenharmony_ci var->vmode &= ~FB_VMODE_DOUBLE; 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_INTERLACED) && 14018c2ecf20Sopenharmony_ci !(par->vbe_modes[match].mode_attr & 0x200)) 14028c2ecf20Sopenharmony_ci var->vmode &= ~FB_VMODE_INTERLACED; 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci uvesafb_check_limits(var, info); 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 14078c2ecf20Sopenharmony_ci var->yres_virtual = (par->ypan) ? 14088c2ecf20Sopenharmony_ci info->fix.smem_len / mode->bytes_per_scan_line : 14098c2ecf20Sopenharmony_ci var->yres; 14108c2ecf20Sopenharmony_ci return 0; 14118c2ecf20Sopenharmony_ci} 14128c2ecf20Sopenharmony_ci 14138c2ecf20Sopenharmony_cistatic struct fb_ops uvesafb_ops = { 14148c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14158c2ecf20Sopenharmony_ci .fb_open = uvesafb_open, 14168c2ecf20Sopenharmony_ci .fb_release = uvesafb_release, 14178c2ecf20Sopenharmony_ci .fb_setcolreg = uvesafb_setcolreg, 14188c2ecf20Sopenharmony_ci .fb_setcmap = uvesafb_setcmap, 14198c2ecf20Sopenharmony_ci .fb_pan_display = uvesafb_pan_display, 14208c2ecf20Sopenharmony_ci .fb_blank = uvesafb_blank, 14218c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 14228c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 14238c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 14248c2ecf20Sopenharmony_ci .fb_check_var = uvesafb_check_var, 14258c2ecf20Sopenharmony_ci .fb_set_par = uvesafb_set_par, 14268c2ecf20Sopenharmony_ci}; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_cistatic void uvesafb_init_info(struct fb_info *info, struct vbe_mode_ib *mode) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci unsigned int size_vmode; 14318c2ecf20Sopenharmony_ci unsigned int size_remap; 14328c2ecf20Sopenharmony_ci unsigned int size_total; 14338c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 14348c2ecf20Sopenharmony_ci int i, h; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci info->pseudo_palette = ((u8 *)info->par + sizeof(struct uvesafb_par)); 14378c2ecf20Sopenharmony_ci info->fix = uvesafb_fix; 14388c2ecf20Sopenharmony_ci info->fix.ypanstep = par->ypan ? 1 : 0; 14398c2ecf20Sopenharmony_ci info->fix.ywrapstep = (par->ypan > 1) ? 1 : 0; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* Disable blanking if the user requested so. */ 14428c2ecf20Sopenharmony_ci if (!blank) 14438c2ecf20Sopenharmony_ci uvesafb_ops.fb_blank = NULL; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci /* 14468c2ecf20Sopenharmony_ci * Find out how much IO memory is required for the mode with 14478c2ecf20Sopenharmony_ci * the highest resolution. 14488c2ecf20Sopenharmony_ci */ 14498c2ecf20Sopenharmony_ci size_remap = 0; 14508c2ecf20Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt; i++) { 14518c2ecf20Sopenharmony_ci h = par->vbe_modes[i].bytes_per_scan_line * 14528c2ecf20Sopenharmony_ci par->vbe_modes[i].y_res; 14538c2ecf20Sopenharmony_ci if (h > size_remap) 14548c2ecf20Sopenharmony_ci size_remap = h; 14558c2ecf20Sopenharmony_ci } 14568c2ecf20Sopenharmony_ci size_remap *= 2; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci /* 14598c2ecf20Sopenharmony_ci * size_vmode -- that is the amount of memory needed for the 14608c2ecf20Sopenharmony_ci * used video mode, i.e. the minimum amount of 14618c2ecf20Sopenharmony_ci * memory we need. 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci size_vmode = info->var.yres * mode->bytes_per_scan_line; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* 14668c2ecf20Sopenharmony_ci * size_total -- all video memory we have. Used for mtrr 14678c2ecf20Sopenharmony_ci * entries, resource allocation and bounds 14688c2ecf20Sopenharmony_ci * checking. 14698c2ecf20Sopenharmony_ci */ 14708c2ecf20Sopenharmony_ci size_total = par->vbe_ib.total_memory * 65536; 14718c2ecf20Sopenharmony_ci if (vram_total) 14728c2ecf20Sopenharmony_ci size_total = vram_total * 1024 * 1024; 14738c2ecf20Sopenharmony_ci if (size_total < size_vmode) 14748c2ecf20Sopenharmony_ci size_total = size_vmode; 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* 14778c2ecf20Sopenharmony_ci * size_remap -- the amount of video memory we are going to 14788c2ecf20Sopenharmony_ci * use for vesafb. With modern cards it is no 14798c2ecf20Sopenharmony_ci * option to simply use size_total as th 14808c2ecf20Sopenharmony_ci * wastes plenty of kernel address space. 14818c2ecf20Sopenharmony_ci */ 14828c2ecf20Sopenharmony_ci if (vram_remap) 14838c2ecf20Sopenharmony_ci size_remap = vram_remap * 1024 * 1024; 14848c2ecf20Sopenharmony_ci if (size_remap < size_vmode) 14858c2ecf20Sopenharmony_ci size_remap = size_vmode; 14868c2ecf20Sopenharmony_ci if (size_remap > size_total) 14878c2ecf20Sopenharmony_ci size_remap = size_total; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci info->fix.smem_len = size_remap; 14908c2ecf20Sopenharmony_ci info->fix.smem_start = mode->phys_base_ptr; 14918c2ecf20Sopenharmony_ci 14928c2ecf20Sopenharmony_ci /* 14938c2ecf20Sopenharmony_ci * We have to set yres_virtual here because when setup_var() was 14948c2ecf20Sopenharmony_ci * called, smem_len wasn't defined yet. 14958c2ecf20Sopenharmony_ci */ 14968c2ecf20Sopenharmony_ci info->var.yres_virtual = info->fix.smem_len / 14978c2ecf20Sopenharmony_ci mode->bytes_per_scan_line; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci if (par->ypan && info->var.yres_virtual > info->var.yres) { 15008c2ecf20Sopenharmony_ci pr_info("scrolling: %s using protected mode interface, yres_virtual=%d\n", 15018c2ecf20Sopenharmony_ci (par->ypan > 1) ? "ywrap" : "ypan", 15028c2ecf20Sopenharmony_ci info->var.yres_virtual); 15038c2ecf20Sopenharmony_ci } else { 15048c2ecf20Sopenharmony_ci pr_info("scrolling: redraw\n"); 15058c2ecf20Sopenharmony_ci info->var.yres_virtual = info->var.yres; 15068c2ecf20Sopenharmony_ci par->ypan = 0; 15078c2ecf20Sopenharmony_ci } 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci info->flags = FBINFO_FLAG_DEFAULT | 15108c2ecf20Sopenharmony_ci (par->ypan ? FBINFO_HWACCEL_YPAN : 0); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (!par->ypan) 15138c2ecf20Sopenharmony_ci uvesafb_ops.fb_pan_display = NULL; 15148c2ecf20Sopenharmony_ci} 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic void uvesafb_init_mtrr(struct fb_info *info) 15178c2ecf20Sopenharmony_ci{ 15188c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (mtrr && !(info->fix.smem_start & (PAGE_SIZE - 1))) { 15218c2ecf20Sopenharmony_ci int temp_size = info->fix.smem_len; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci int rc; 15248c2ecf20Sopenharmony_ci 15258c2ecf20Sopenharmony_ci /* Find the largest power-of-two */ 15268c2ecf20Sopenharmony_ci temp_size = roundup_pow_of_two(temp_size); 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci /* Try and find a power of two to add */ 15298c2ecf20Sopenharmony_ci do { 15308c2ecf20Sopenharmony_ci rc = arch_phys_wc_add(info->fix.smem_start, temp_size); 15318c2ecf20Sopenharmony_ci temp_size >>= 1; 15328c2ecf20Sopenharmony_ci } while (temp_size >= PAGE_SIZE && rc == -EINVAL); 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (rc >= 0) 15358c2ecf20Sopenharmony_ci par->mtrr_handle = rc; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci} 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_cistatic void uvesafb_ioremap(struct fb_info *info) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci info->screen_base = ioremap_wc(info->fix.smem_start, info->fix.smem_len); 15428c2ecf20Sopenharmony_ci} 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_vbe_ver(struct device *dev, 15458c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 15468c2ecf20Sopenharmony_ci{ 15478c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 15488c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%.4x\n", par->vbe_ib.vbe_version); 15518c2ecf20Sopenharmony_ci} 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_cistatic DEVICE_ATTR(vbe_version, S_IRUGO, uvesafb_show_vbe_ver, NULL); 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_vbe_modes(struct device *dev, 15568c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 15578c2ecf20Sopenharmony_ci{ 15588c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 15598c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 15608c2ecf20Sopenharmony_ci int ret = 0, i; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci for (i = 0; i < par->vbe_modes_cnt && ret < PAGE_SIZE; i++) { 15638c2ecf20Sopenharmony_ci ret += scnprintf(buf + ret, PAGE_SIZE - ret, 15648c2ecf20Sopenharmony_ci "%dx%d-%d, 0x%.4x\n", 15658c2ecf20Sopenharmony_ci par->vbe_modes[i].x_res, par->vbe_modes[i].y_res, 15668c2ecf20Sopenharmony_ci par->vbe_modes[i].depth, par->vbe_modes[i].mode_id); 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci return ret; 15708c2ecf20Sopenharmony_ci} 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_cistatic DEVICE_ATTR(vbe_modes, S_IRUGO, uvesafb_show_vbe_modes, NULL); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_vendor(struct device *dev, 15758c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 15768c2ecf20Sopenharmony_ci{ 15778c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 15788c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_vendor_name_ptr) 15818c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", (char *) 15828c2ecf20Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_vendor_name_ptr); 15838c2ecf20Sopenharmony_ci else 15848c2ecf20Sopenharmony_ci return 0; 15858c2ecf20Sopenharmony_ci} 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_cistatic DEVICE_ATTR(oem_vendor, S_IRUGO, uvesafb_show_vendor, NULL); 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_product_name(struct device *dev, 15908c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 15938c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_product_name_ptr) 15968c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", (char *) 15978c2ecf20Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_product_name_ptr); 15988c2ecf20Sopenharmony_ci else 15998c2ecf20Sopenharmony_ci return 0; 16008c2ecf20Sopenharmony_ci} 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_cistatic DEVICE_ATTR(oem_product_name, S_IRUGO, uvesafb_show_product_name, NULL); 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_product_rev(struct device *dev, 16058c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 16088c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_product_rev_ptr) 16118c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", (char *) 16128c2ecf20Sopenharmony_ci (&par->vbe_ib) + par->vbe_ib.oem_product_rev_ptr); 16138c2ecf20Sopenharmony_ci else 16148c2ecf20Sopenharmony_ci return 0; 16158c2ecf20Sopenharmony_ci} 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cistatic DEVICE_ATTR(oem_product_rev, S_IRUGO, uvesafb_show_product_rev, NULL); 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_oem_string(struct device *dev, 16208c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 16218c2ecf20Sopenharmony_ci{ 16228c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 16238c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci if (par->vbe_ib.oem_string_ptr) 16268c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", 16278c2ecf20Sopenharmony_ci (char *)(&par->vbe_ib) + par->vbe_ib.oem_string_ptr); 16288c2ecf20Sopenharmony_ci else 16298c2ecf20Sopenharmony_ci return 0; 16308c2ecf20Sopenharmony_ci} 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_cistatic DEVICE_ATTR(oem_string, S_IRUGO, uvesafb_show_oem_string, NULL); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_cistatic ssize_t uvesafb_show_nocrtc(struct device *dev, 16358c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 16368c2ecf20Sopenharmony_ci{ 16378c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 16388c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", par->nocrtc); 16418c2ecf20Sopenharmony_ci} 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_cistatic ssize_t uvesafb_store_nocrtc(struct device *dev, 16448c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 16458c2ecf20Sopenharmony_ci{ 16468c2ecf20Sopenharmony_ci struct fb_info *info = dev_get_drvdata(dev); 16478c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci if (count > 0) { 16508c2ecf20Sopenharmony_ci if (buf[0] == '0') 16518c2ecf20Sopenharmony_ci par->nocrtc = 0; 16528c2ecf20Sopenharmony_ci else 16538c2ecf20Sopenharmony_ci par->nocrtc = 1; 16548c2ecf20Sopenharmony_ci } 16558c2ecf20Sopenharmony_ci return count; 16568c2ecf20Sopenharmony_ci} 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_cistatic DEVICE_ATTR(nocrtc, S_IRUGO | S_IWUSR, uvesafb_show_nocrtc, 16598c2ecf20Sopenharmony_ci uvesafb_store_nocrtc); 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cistatic struct attribute *uvesafb_dev_attrs[] = { 16628c2ecf20Sopenharmony_ci &dev_attr_vbe_version.attr, 16638c2ecf20Sopenharmony_ci &dev_attr_vbe_modes.attr, 16648c2ecf20Sopenharmony_ci &dev_attr_oem_vendor.attr, 16658c2ecf20Sopenharmony_ci &dev_attr_oem_product_name.attr, 16668c2ecf20Sopenharmony_ci &dev_attr_oem_product_rev.attr, 16678c2ecf20Sopenharmony_ci &dev_attr_oem_string.attr, 16688c2ecf20Sopenharmony_ci &dev_attr_nocrtc.attr, 16698c2ecf20Sopenharmony_ci NULL, 16708c2ecf20Sopenharmony_ci}; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic const struct attribute_group uvesafb_dev_attgrp = { 16738c2ecf20Sopenharmony_ci .name = NULL, 16748c2ecf20Sopenharmony_ci .attrs = uvesafb_dev_attrs, 16758c2ecf20Sopenharmony_ci}; 16768c2ecf20Sopenharmony_ci 16778c2ecf20Sopenharmony_cistatic int uvesafb_probe(struct platform_device *dev) 16788c2ecf20Sopenharmony_ci{ 16798c2ecf20Sopenharmony_ci struct fb_info *info; 16808c2ecf20Sopenharmony_ci struct vbe_mode_ib *mode = NULL; 16818c2ecf20Sopenharmony_ci struct uvesafb_par *par; 16828c2ecf20Sopenharmony_ci int err = 0, i; 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(*par) + sizeof(u32) * 256, &dev->dev); 16858c2ecf20Sopenharmony_ci if (!info) 16868c2ecf20Sopenharmony_ci return -ENOMEM; 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci par = info->par; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci err = uvesafb_vbe_init(info); 16918c2ecf20Sopenharmony_ci if (err) { 16928c2ecf20Sopenharmony_ci pr_err("vbe_init() failed with %d\n", err); 16938c2ecf20Sopenharmony_ci goto out; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci info->fbops = &uvesafb_ops; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci i = uvesafb_vbe_init_mode(info); 16998c2ecf20Sopenharmony_ci if (i < 0) { 17008c2ecf20Sopenharmony_ci err = -EINVAL; 17018c2ecf20Sopenharmony_ci goto out; 17028c2ecf20Sopenharmony_ci } else { 17038c2ecf20Sopenharmony_ci mode = &par->vbe_modes[i]; 17048c2ecf20Sopenharmony_ci } 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { 17078c2ecf20Sopenharmony_ci err = -ENXIO; 17088c2ecf20Sopenharmony_ci goto out; 17098c2ecf20Sopenharmony_ci } 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci uvesafb_init_info(info, mode); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci if (!request_region(0x3c0, 32, "uvesafb")) { 17148c2ecf20Sopenharmony_ci pr_err("request region 0x3c0-0x3e0 failed\n"); 17158c2ecf20Sopenharmony_ci err = -EIO; 17168c2ecf20Sopenharmony_ci goto out_mode; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (!request_mem_region(info->fix.smem_start, info->fix.smem_len, 17208c2ecf20Sopenharmony_ci "uvesafb")) { 17218c2ecf20Sopenharmony_ci pr_err("cannot reserve video memory at 0x%lx\n", 17228c2ecf20Sopenharmony_ci info->fix.smem_start); 17238c2ecf20Sopenharmony_ci err = -EIO; 17248c2ecf20Sopenharmony_ci goto out_reg; 17258c2ecf20Sopenharmony_ci } 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci uvesafb_init_mtrr(info); 17288c2ecf20Sopenharmony_ci uvesafb_ioremap(info); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci if (!info->screen_base) { 17318c2ecf20Sopenharmony_ci pr_err("abort, cannot ioremap 0x%x bytes of video memory at 0x%lx\n", 17328c2ecf20Sopenharmony_ci info->fix.smem_len, info->fix.smem_start); 17338c2ecf20Sopenharmony_ci err = -EIO; 17348c2ecf20Sopenharmony_ci goto out_mem; 17358c2ecf20Sopenharmony_ci } 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci platform_set_drvdata(dev, info); 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci if (register_framebuffer(info) < 0) { 17408c2ecf20Sopenharmony_ci pr_err("failed to register framebuffer device\n"); 17418c2ecf20Sopenharmony_ci err = -EINVAL; 17428c2ecf20Sopenharmony_ci goto out_unmap; 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci pr_info("framebuffer at 0x%lx, mapped to 0x%p, using %dk, total %dk\n", 17468c2ecf20Sopenharmony_ci info->fix.smem_start, info->screen_base, 17478c2ecf20Sopenharmony_ci info->fix.smem_len / 1024, par->vbe_ib.total_memory * 64); 17488c2ecf20Sopenharmony_ci fb_info(info, "%s frame buffer device\n", info->fix.id); 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci err = sysfs_create_group(&dev->dev.kobj, &uvesafb_dev_attgrp); 17518c2ecf20Sopenharmony_ci if (err != 0) 17528c2ecf20Sopenharmony_ci fb_warn(info, "failed to register attributes\n"); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci return 0; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ciout_unmap: 17578c2ecf20Sopenharmony_ci iounmap(info->screen_base); 17588c2ecf20Sopenharmony_ciout_mem: 17598c2ecf20Sopenharmony_ci arch_phys_wc_del(par->mtrr_handle); 17608c2ecf20Sopenharmony_ci release_mem_region(info->fix.smem_start, info->fix.smem_len); 17618c2ecf20Sopenharmony_ciout_reg: 17628c2ecf20Sopenharmony_ci release_region(0x3c0, 32); 17638c2ecf20Sopenharmony_ciout_mode: 17648c2ecf20Sopenharmony_ci if (!list_empty(&info->modelist)) 17658c2ecf20Sopenharmony_ci fb_destroy_modelist(&info->modelist); 17668c2ecf20Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 17678c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 17688c2ecf20Sopenharmony_ciout: 17698c2ecf20Sopenharmony_ci kfree(par->vbe_modes); 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci framebuffer_release(info); 17728c2ecf20Sopenharmony_ci return err; 17738c2ecf20Sopenharmony_ci} 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_cistatic int uvesafb_remove(struct platform_device *dev) 17768c2ecf20Sopenharmony_ci{ 17778c2ecf20Sopenharmony_ci struct fb_info *info = platform_get_drvdata(dev); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci if (info) { 17808c2ecf20Sopenharmony_ci struct uvesafb_par *par = info->par; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci sysfs_remove_group(&dev->dev.kobj, &uvesafb_dev_attgrp); 17838c2ecf20Sopenharmony_ci unregister_framebuffer(info); 17848c2ecf20Sopenharmony_ci release_region(0x3c0, 32); 17858c2ecf20Sopenharmony_ci iounmap(info->screen_base); 17868c2ecf20Sopenharmony_ci arch_phys_wc_del(par->mtrr_handle); 17878c2ecf20Sopenharmony_ci release_mem_region(info->fix.smem_start, info->fix.smem_len); 17888c2ecf20Sopenharmony_ci fb_destroy_modedb(info->monspecs.modedb); 17898c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci kfree(par->vbe_modes); 17928c2ecf20Sopenharmony_ci kfree(par->vbe_state_orig); 17938c2ecf20Sopenharmony_ci kfree(par->vbe_state_saved); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci framebuffer_release(info); 17968c2ecf20Sopenharmony_ci } 17978c2ecf20Sopenharmony_ci return 0; 17988c2ecf20Sopenharmony_ci} 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_cistatic struct platform_driver uvesafb_driver = { 18018c2ecf20Sopenharmony_ci .probe = uvesafb_probe, 18028c2ecf20Sopenharmony_ci .remove = uvesafb_remove, 18038c2ecf20Sopenharmony_ci .driver = { 18048c2ecf20Sopenharmony_ci .name = "uvesafb", 18058c2ecf20Sopenharmony_ci }, 18068c2ecf20Sopenharmony_ci}; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_cistatic struct platform_device *uvesafb_device; 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci#ifndef MODULE 18118c2ecf20Sopenharmony_cistatic int uvesafb_setup(char *options) 18128c2ecf20Sopenharmony_ci{ 18138c2ecf20Sopenharmony_ci char *this_opt; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci if (!options || !*options) 18168c2ecf20Sopenharmony_ci return 0; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ",")) != NULL) { 18198c2ecf20Sopenharmony_ci if (!*this_opt) continue; 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci if (!strcmp(this_opt, "redraw")) 18228c2ecf20Sopenharmony_ci ypan = 0; 18238c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "ypan")) 18248c2ecf20Sopenharmony_ci ypan = 1; 18258c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "ywrap")) 18268c2ecf20Sopenharmony_ci ypan = 2; 18278c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "vgapal")) 18288c2ecf20Sopenharmony_ci pmi_setpal = false; 18298c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "pmipal")) 18308c2ecf20Sopenharmony_ci pmi_setpal = true; 18318c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "mtrr:", 5)) 18328c2ecf20Sopenharmony_ci mtrr = simple_strtoul(this_opt+5, NULL, 0); 18338c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "nomtrr")) 18348c2ecf20Sopenharmony_ci mtrr = 0; 18358c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "nocrtc")) 18368c2ecf20Sopenharmony_ci nocrtc = true; 18378c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "noedid")) 18388c2ecf20Sopenharmony_ci noedid = true; 18398c2ecf20Sopenharmony_ci else if (!strcmp(this_opt, "noblank")) 18408c2ecf20Sopenharmony_ci blank = false; 18418c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "vtotal:", 7)) 18428c2ecf20Sopenharmony_ci vram_total = simple_strtoul(this_opt + 7, NULL, 0); 18438c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "vremap:", 7)) 18448c2ecf20Sopenharmony_ci vram_remap = simple_strtoul(this_opt + 7, NULL, 0); 18458c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "maxhf:", 6)) 18468c2ecf20Sopenharmony_ci maxhf = simple_strtoul(this_opt + 6, NULL, 0); 18478c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "maxvf:", 6)) 18488c2ecf20Sopenharmony_ci maxvf = simple_strtoul(this_opt + 6, NULL, 0); 18498c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "maxclk:", 7)) 18508c2ecf20Sopenharmony_ci maxclk = simple_strtoul(this_opt + 7, NULL, 0); 18518c2ecf20Sopenharmony_ci else if (!strncmp(this_opt, "vbemode:", 8)) 18528c2ecf20Sopenharmony_ci vbemode = simple_strtoul(this_opt + 8, NULL, 0); 18538c2ecf20Sopenharmony_ci else if (this_opt[0] >= '0' && this_opt[0] <= '9') { 18548c2ecf20Sopenharmony_ci mode_option = this_opt; 18558c2ecf20Sopenharmony_ci } else { 18568c2ecf20Sopenharmony_ci pr_warn("unrecognized option %s\n", this_opt); 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci } 18598c2ecf20Sopenharmony_ci 18608c2ecf20Sopenharmony_ci if (mtrr != 3 && mtrr != 0) 18618c2ecf20Sopenharmony_ci pr_warn("uvesafb: mtrr should be set to 0 or 3; %d is unsupported", mtrr); 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci return 0; 18648c2ecf20Sopenharmony_ci} 18658c2ecf20Sopenharmony_ci#endif /* !MODULE */ 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_cistatic ssize_t v86d_show(struct device_driver *dev, char *buf) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s\n", v86d_path); 18708c2ecf20Sopenharmony_ci} 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_cistatic ssize_t v86d_store(struct device_driver *dev, const char *buf, 18738c2ecf20Sopenharmony_ci size_t count) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci strncpy(v86d_path, buf, PATH_MAX); 18768c2ecf20Sopenharmony_ci return count; 18778c2ecf20Sopenharmony_ci} 18788c2ecf20Sopenharmony_cistatic DRIVER_ATTR_RW(v86d); 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic int uvesafb_init(void) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci int err; 18838c2ecf20Sopenharmony_ci 18848c2ecf20Sopenharmony_ci#ifndef MODULE 18858c2ecf20Sopenharmony_ci char *option = NULL; 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci if (fb_get_options("uvesafb", &option)) 18888c2ecf20Sopenharmony_ci return -ENODEV; 18898c2ecf20Sopenharmony_ci uvesafb_setup(option); 18908c2ecf20Sopenharmony_ci#endif 18918c2ecf20Sopenharmony_ci err = cn_add_callback(&uvesafb_cn_id, "uvesafb", uvesafb_cn_callback); 18928c2ecf20Sopenharmony_ci if (err) 18938c2ecf20Sopenharmony_ci return err; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci err = platform_driver_register(&uvesafb_driver); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (!err) { 18988c2ecf20Sopenharmony_ci uvesafb_device = platform_device_alloc("uvesafb", 0); 18998c2ecf20Sopenharmony_ci if (uvesafb_device) 19008c2ecf20Sopenharmony_ci err = platform_device_add(uvesafb_device); 19018c2ecf20Sopenharmony_ci else 19028c2ecf20Sopenharmony_ci err = -ENOMEM; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci if (err) { 19058c2ecf20Sopenharmony_ci platform_device_put(uvesafb_device); 19068c2ecf20Sopenharmony_ci platform_driver_unregister(&uvesafb_driver); 19078c2ecf20Sopenharmony_ci cn_del_callback(&uvesafb_cn_id); 19088c2ecf20Sopenharmony_ci return err; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci err = driver_create_file(&uvesafb_driver.driver, 19128c2ecf20Sopenharmony_ci &driver_attr_v86d); 19138c2ecf20Sopenharmony_ci if (err) { 19148c2ecf20Sopenharmony_ci pr_warn("failed to register attributes\n"); 19158c2ecf20Sopenharmony_ci err = 0; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci return err; 19198c2ecf20Sopenharmony_ci} 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_cimodule_init(uvesafb_init); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_cistatic void uvesafb_exit(void) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci struct uvesafb_ktask *task; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci if (v86d_started) { 19288c2ecf20Sopenharmony_ci task = uvesafb_prep(); 19298c2ecf20Sopenharmony_ci if (task) { 19308c2ecf20Sopenharmony_ci task->t.flags = TF_EXIT; 19318c2ecf20Sopenharmony_ci uvesafb_exec(task); 19328c2ecf20Sopenharmony_ci uvesafb_free(task); 19338c2ecf20Sopenharmony_ci } 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci driver_remove_file(&uvesafb_driver.driver, &driver_attr_v86d); 19378c2ecf20Sopenharmony_ci platform_device_unregister(uvesafb_device); 19388c2ecf20Sopenharmony_ci platform_driver_unregister(&uvesafb_driver); 19398c2ecf20Sopenharmony_ci cn_del_callback(&uvesafb_cn_id); 19408c2ecf20Sopenharmony_ci} 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_cimodule_exit(uvesafb_exit); 19438c2ecf20Sopenharmony_ci 19448c2ecf20Sopenharmony_cistatic int param_set_scroll(const char *val, const struct kernel_param *kp) 19458c2ecf20Sopenharmony_ci{ 19468c2ecf20Sopenharmony_ci ypan = 0; 19478c2ecf20Sopenharmony_ci 19488c2ecf20Sopenharmony_ci if (!strcmp(val, "redraw")) 19498c2ecf20Sopenharmony_ci ypan = 0; 19508c2ecf20Sopenharmony_ci else if (!strcmp(val, "ypan")) 19518c2ecf20Sopenharmony_ci ypan = 1; 19528c2ecf20Sopenharmony_ci else if (!strcmp(val, "ywrap")) 19538c2ecf20Sopenharmony_ci ypan = 2; 19548c2ecf20Sopenharmony_ci else 19558c2ecf20Sopenharmony_ci return -EINVAL; 19568c2ecf20Sopenharmony_ci 19578c2ecf20Sopenharmony_ci return 0; 19588c2ecf20Sopenharmony_ci} 19598c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_scroll = { 19608c2ecf20Sopenharmony_ci .set = param_set_scroll, 19618c2ecf20Sopenharmony_ci}; 19628c2ecf20Sopenharmony_ci#define param_check_scroll(name, p) __param_check(name, p, void) 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_cimodule_param_named(scroll, ypan, scroll, 0); 19658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(scroll, 19668c2ecf20Sopenharmony_ci "Scrolling mode, set to 'redraw', 'ypan', or 'ywrap'"); 19678c2ecf20Sopenharmony_cimodule_param_named(vgapal, pmi_setpal, invbool, 0); 19688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vgapal, "Set palette using VGA registers"); 19698c2ecf20Sopenharmony_cimodule_param_named(pmipal, pmi_setpal, bool, 0); 19708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pmipal, "Set palette using PMI calls"); 19718c2ecf20Sopenharmony_cimodule_param(mtrr, uint, 0); 19728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mtrr, 19738c2ecf20Sopenharmony_ci "Memory Type Range Registers setting. Use 0 to disable."); 19748c2ecf20Sopenharmony_cimodule_param(blank, bool, 0); 19758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(blank, "Enable hardware blanking"); 19768c2ecf20Sopenharmony_cimodule_param(nocrtc, bool, 0); 19778c2ecf20Sopenharmony_ciMODULE_PARM_DESC(nocrtc, "Ignore CRTC timings when setting modes"); 19788c2ecf20Sopenharmony_cimodule_param(noedid, bool, 0); 19798c2ecf20Sopenharmony_ciMODULE_PARM_DESC(noedid, 19808c2ecf20Sopenharmony_ci "Ignore EDID-provided monitor limits when setting modes"); 19818c2ecf20Sopenharmony_cimodule_param(vram_remap, uint, 0); 19828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vram_remap, "Set amount of video memory to be used [MiB]"); 19838c2ecf20Sopenharmony_cimodule_param(vram_total, uint, 0); 19848c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vram_total, "Set total amount of video memory [MiB]"); 19858c2ecf20Sopenharmony_cimodule_param(maxclk, ushort, 0); 19868c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxclk, "Maximum pixelclock [MHz], overrides EDID data"); 19878c2ecf20Sopenharmony_cimodule_param(maxhf, ushort, 0); 19888c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxhf, 19898c2ecf20Sopenharmony_ci "Maximum horizontal frequency [kHz], overrides EDID data"); 19908c2ecf20Sopenharmony_cimodule_param(maxvf, ushort, 0); 19918c2ecf20Sopenharmony_ciMODULE_PARM_DESC(maxvf, 19928c2ecf20Sopenharmony_ci "Maximum vertical frequency [Hz], overrides EDID data"); 19938c2ecf20Sopenharmony_cimodule_param(mode_option, charp, 0); 19948c2ecf20Sopenharmony_ciMODULE_PARM_DESC(mode_option, 19958c2ecf20Sopenharmony_ci "Specify initial video mode as \"<xres>x<yres>[-<bpp>][@<refresh>]\""); 19968c2ecf20Sopenharmony_cimodule_param(vbemode, ushort, 0); 19978c2ecf20Sopenharmony_ciMODULE_PARM_DESC(vbemode, 19988c2ecf20Sopenharmony_ci "VBE mode number to set, overrides the 'mode' option"); 19998c2ecf20Sopenharmony_cimodule_param_string(v86d, v86d_path, PATH_MAX, 0660); 20008c2ecf20Sopenharmony_ciMODULE_PARM_DESC(v86d, "Path to the v86d userspace helper."); 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 20038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Michal Januszewski <spock@gentoo.org>"); 20048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for VBE2.0+ compliant graphics boards"); 20058c2ecf20Sopenharmony_ci 2006