18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/drivers/video/ps3fb.c -- PS3 GPU frame buffer device 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2006 Sony Computer Entertainment Inc. 58c2ecf20Sopenharmony_ci * Copyright 2006, 2007 Sony Corporation 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is based on : 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * linux/drivers/video/vfb.c -- Virtual frame buffer device 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2002 James Simmons 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 1997 Geert Uytterhoeven 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 168c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive for 178c2ecf20Sopenharmony_ci * more details. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/errno.h> 238c2ecf20Sopenharmony_ci#include <linux/string.h> 248c2ecf20Sopenharmony_ci#include <linux/mm.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/console.h> 278c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 288c2ecf20Sopenharmony_ci#include <linux/kthread.h> 298c2ecf20Sopenharmony_ci#include <linux/freezer.h> 308c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 318c2ecf20Sopenharmony_ci#include <linux/fb.h> 328c2ecf20Sopenharmony_ci#include <linux/fbcon.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <asm/cell-regs.h> 368c2ecf20Sopenharmony_ci#include <asm/lv1call.h> 378c2ecf20Sopenharmony_ci#include <asm/ps3av.h> 388c2ecf20Sopenharmony_ci#include <asm/ps3fb.h> 398c2ecf20Sopenharmony_ci#include <asm/ps3.h> 408c2ecf20Sopenharmony_ci#include <asm/ps3gpu.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define DEVICE_NAME "ps3fb" 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#define GPU_CMD_BUF_SIZE (2 * 1024 * 1024) 468c2ecf20Sopenharmony_ci#define GPU_FB_START (64 * 1024) 478c2ecf20Sopenharmony_ci#define GPU_IOIF (0x0d000000UL) 488c2ecf20Sopenharmony_ci#define GPU_ALIGN_UP(x) ALIGN((x), 64) 498c2ecf20Sopenharmony_ci#define GPU_MAX_LINE_LENGTH (65536 - 64) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_VSYNC_0 0 /* vsync on head A */ 528c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_VSYNC_1 1 /* vsync on head B */ 538c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_FLIP_0 3 /* flip head A */ 548c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_FLIP_1 4 /* flip head B */ 558c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_QUEUE_0 5 /* queue head A */ 568c2ecf20Sopenharmony_ci#define GPU_INTR_STATUS_QUEUE_1 6 /* queue head B */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define GPU_DRIVER_INFO_VERSION 0x211 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* gpu internals */ 618c2ecf20Sopenharmony_cistruct display_head { 628c2ecf20Sopenharmony_ci u64 be_time_stamp; 638c2ecf20Sopenharmony_ci u32 status; 648c2ecf20Sopenharmony_ci u32 offset; 658c2ecf20Sopenharmony_ci u32 res1; 668c2ecf20Sopenharmony_ci u32 res2; 678c2ecf20Sopenharmony_ci u32 field; 688c2ecf20Sopenharmony_ci u32 reserved1; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci u64 res3; 718c2ecf20Sopenharmony_ci u32 raster; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci u64 vblank_count; 748c2ecf20Sopenharmony_ci u32 field_vsync; 758c2ecf20Sopenharmony_ci u32 reserved2; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct gpu_irq { 798c2ecf20Sopenharmony_ci u32 irq_outlet; 808c2ecf20Sopenharmony_ci u32 status; 818c2ecf20Sopenharmony_ci u32 mask; 828c2ecf20Sopenharmony_ci u32 video_cause; 838c2ecf20Sopenharmony_ci u32 graph_cause; 848c2ecf20Sopenharmony_ci u32 user_cause; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci u32 res1; 878c2ecf20Sopenharmony_ci u64 res2; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci u32 reserved[4]; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct gpu_driver_info { 938c2ecf20Sopenharmony_ci u32 version_driver; 948c2ecf20Sopenharmony_ci u32 version_gpu; 958c2ecf20Sopenharmony_ci u32 memory_size; 968c2ecf20Sopenharmony_ci u32 hardware_channel; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci u32 nvcore_frequency; 998c2ecf20Sopenharmony_ci u32 memory_frequency; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci u32 reserved[1063]; 1028c2ecf20Sopenharmony_ci struct display_head display_head[8]; 1038c2ecf20Sopenharmony_ci struct gpu_irq irq; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct ps3fb_priv { 1078c2ecf20Sopenharmony_ci unsigned int irq_no; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci u64 context_handle, memory_handle; 1108c2ecf20Sopenharmony_ci struct gpu_driver_info *dinfo; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci u64 vblank_count; /* frame count */ 1138c2ecf20Sopenharmony_ci wait_queue_head_t wait_vsync; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci atomic_t ext_flip; /* on/off flip with vsync */ 1168c2ecf20Sopenharmony_ci atomic_t f_count; /* fb_open count */ 1178c2ecf20Sopenharmony_ci int is_blanked; 1188c2ecf20Sopenharmony_ci int is_kicked; 1198c2ecf20Sopenharmony_ci struct task_struct *task; 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_cistatic struct ps3fb_priv ps3fb; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistruct ps3fb_par { 1248c2ecf20Sopenharmony_ci u32 pseudo_palette[16]; 1258c2ecf20Sopenharmony_ci int mode_id, new_mode_id; 1268c2ecf20Sopenharmony_ci unsigned int num_frames; /* num of frame buffers */ 1278c2ecf20Sopenharmony_ci unsigned int width; 1288c2ecf20Sopenharmony_ci unsigned int height; 1298c2ecf20Sopenharmony_ci unsigned int ddr_line_length; 1308c2ecf20Sopenharmony_ci unsigned int ddr_frame_size; 1318c2ecf20Sopenharmony_ci unsigned int xdr_frame_size; 1328c2ecf20Sopenharmony_ci unsigned int full_offset; /* start of fullscreen DDR fb */ 1338c2ecf20Sopenharmony_ci unsigned int fb_offset; /* start of actual DDR fb */ 1348c2ecf20Sopenharmony_ci unsigned int pan_offset; 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#define FIRST_NATIVE_MODE_INDEX 10 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic const struct fb_videomode ps3fb_modedb[] = { 1418c2ecf20Sopenharmony_ci /* 60 Hz broadcast modes (modes "1" to "5") */ 1428c2ecf20Sopenharmony_ci { 1438c2ecf20Sopenharmony_ci /* 480i */ 1448c2ecf20Sopenharmony_ci "480i", 60, 576, 384, 74074, 130, 89, 78, 57, 63, 6, 1458c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 1468c2ecf20Sopenharmony_ci }, { 1478c2ecf20Sopenharmony_ci /* 480p */ 1488c2ecf20Sopenharmony_ci "480p", 60, 576, 384, 37037, 130, 89, 78, 57, 63, 6, 1498c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1508c2ecf20Sopenharmony_ci }, { 1518c2ecf20Sopenharmony_ci /* 720p */ 1528c2ecf20Sopenharmony_ci "720p", 60, 1124, 644, 13481, 298, 148, 57, 44, 80, 5, 1538c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1548c2ecf20Sopenharmony_ci }, { 1558c2ecf20Sopenharmony_ci /* 1080i */ 1568c2ecf20Sopenharmony_ci "1080i", 60, 1688, 964, 13481, 264, 160, 94, 62, 88, 5, 1578c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 1588c2ecf20Sopenharmony_ci }, { 1598c2ecf20Sopenharmony_ci /* 1080p */ 1608c2ecf20Sopenharmony_ci "1080p", 60, 1688, 964, 6741, 264, 160, 94, 62, 88, 5, 1618c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1628c2ecf20Sopenharmony_ci }, 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci /* 50 Hz broadcast modes (modes "6" to "10") */ 1658c2ecf20Sopenharmony_ci { 1668c2ecf20Sopenharmony_ci /* 576i */ 1678c2ecf20Sopenharmony_ci "576i", 50, 576, 460, 74074, 142, 83, 97, 63, 63, 5, 1688c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 1698c2ecf20Sopenharmony_ci }, { 1708c2ecf20Sopenharmony_ci /* 576p */ 1718c2ecf20Sopenharmony_ci "576p", 50, 576, 460, 37037, 142, 83, 97, 63, 63, 5, 1728c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1738c2ecf20Sopenharmony_ci }, { 1748c2ecf20Sopenharmony_ci /* 720p */ 1758c2ecf20Sopenharmony_ci "720p", 50, 1124, 644, 13468, 298, 478, 57, 44, 80, 5, 1768c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1778c2ecf20Sopenharmony_ci }, { 1788c2ecf20Sopenharmony_ci /* 1080i */ 1798c2ecf20Sopenharmony_ci "1080i", 50, 1688, 964, 13468, 264, 600, 94, 62, 88, 5, 1808c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 1818c2ecf20Sopenharmony_ci }, { 1828c2ecf20Sopenharmony_ci /* 1080p */ 1838c2ecf20Sopenharmony_ci "1080p", 50, 1688, 964, 6734, 264, 600, 94, 62, 88, 5, 1848c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1858c2ecf20Sopenharmony_ci }, 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci [FIRST_NATIVE_MODE_INDEX] = 1888c2ecf20Sopenharmony_ci /* 60 Hz broadcast modes (full resolution versions of modes "1" to "5") */ 1898c2ecf20Sopenharmony_ci { 1908c2ecf20Sopenharmony_ci /* 480if */ 1918c2ecf20Sopenharmony_ci "480if", 60, 720, 480, 74074, 58, 17, 30, 9, 63, 6, 1928c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 1938c2ecf20Sopenharmony_ci }, { 1948c2ecf20Sopenharmony_ci /* 480pf */ 1958c2ecf20Sopenharmony_ci "480pf", 60, 720, 480, 37037, 58, 17, 30, 9, 63, 6, 1968c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 1978c2ecf20Sopenharmony_ci }, { 1988c2ecf20Sopenharmony_ci /* 720pf */ 1998c2ecf20Sopenharmony_ci "720pf", 60, 1280, 720, 13481, 220, 70, 19, 6, 80, 5, 2008c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 2018c2ecf20Sopenharmony_ci }, { 2028c2ecf20Sopenharmony_ci /* 1080if */ 2038c2ecf20Sopenharmony_ci "1080if", 60, 1920, 1080, 13481, 148, 44, 36, 4, 88, 5, 2048c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 2058c2ecf20Sopenharmony_ci }, { 2068c2ecf20Sopenharmony_ci /* 1080pf */ 2078c2ecf20Sopenharmony_ci "1080pf", 60, 1920, 1080, 6741, 148, 44, 36, 4, 88, 5, 2088c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 2098c2ecf20Sopenharmony_ci }, 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* 50 Hz broadcast modes (full resolution versions of modes "6" to "10") */ 2128c2ecf20Sopenharmony_ci { 2138c2ecf20Sopenharmony_ci /* 576if */ 2148c2ecf20Sopenharmony_ci "576if", 50, 720, 576, 74074, 70, 11, 39, 5, 63, 5, 2158c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 2168c2ecf20Sopenharmony_ci }, { 2178c2ecf20Sopenharmony_ci /* 576pf */ 2188c2ecf20Sopenharmony_ci "576pf", 50, 720, 576, 37037, 70, 11, 39, 5, 63, 5, 2198c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 2208c2ecf20Sopenharmony_ci }, { 2218c2ecf20Sopenharmony_ci /* 720pf */ 2228c2ecf20Sopenharmony_ci "720pf", 50, 1280, 720, 13468, 220, 400, 19, 6, 80, 5, 2238c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 2248c2ecf20Sopenharmony_ci }, { 2258c2ecf20Sopenharmony_ci /* 1080if */ 2268c2ecf20Sopenharmony_ci "1080if", 50, 1920, 1080, 13468, 148, 484, 36, 4, 88, 5, 2278c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED 2288c2ecf20Sopenharmony_ci }, { 2298c2ecf20Sopenharmony_ci /* 1080pf */ 2308c2ecf20Sopenharmony_ci "1080pf", 50, 1920, 1080, 6734, 148, 484, 36, 4, 88, 5, 2318c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED 2328c2ecf20Sopenharmony_ci }, 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* VESA modes (modes "11" to "13") */ 2358c2ecf20Sopenharmony_ci { 2368c2ecf20Sopenharmony_ci /* WXGA */ 2378c2ecf20Sopenharmony_ci "wxga", 60, 1280, 768, 12924, 160, 24, 29, 3, 136, 6, 2388c2ecf20Sopenharmony_ci 0, FB_VMODE_NONINTERLACED, 2398c2ecf20Sopenharmony_ci FB_MODE_IS_VESA 2408c2ecf20Sopenharmony_ci }, { 2418c2ecf20Sopenharmony_ci /* SXGA */ 2428c2ecf20Sopenharmony_ci "sxga", 60, 1280, 1024, 9259, 248, 48, 38, 1, 112, 3, 2438c2ecf20Sopenharmony_ci FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 2448c2ecf20Sopenharmony_ci FB_MODE_IS_VESA 2458c2ecf20Sopenharmony_ci }, { 2468c2ecf20Sopenharmony_ci /* WUXGA */ 2478c2ecf20Sopenharmony_ci "wuxga", 60, 1920, 1200, 6494, 80, 48, 26, 3, 32, 6, 2488c2ecf20Sopenharmony_ci FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, 2498c2ecf20Sopenharmony_ci FB_MODE_IS_VESA 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci#define HEAD_A 2558c2ecf20Sopenharmony_ci#define HEAD_B 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci#define BPP 4 /* number of bytes per pixel */ 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int ps3fb_mode; 2618c2ecf20Sopenharmony_cimodule_param(ps3fb_mode, int, 0); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic char *mode_option; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic int ps3fb_cmp_mode(const struct fb_videomode *vmode, 2668c2ecf20Sopenharmony_ci const struct fb_var_screeninfo *var) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci long xres, yres, left_margin, right_margin, upper_margin, lower_margin; 2698c2ecf20Sopenharmony_ci long dx, dy; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* maximum values */ 2728c2ecf20Sopenharmony_ci if (var->xres > vmode->xres || var->yres > vmode->yres || 2738c2ecf20Sopenharmony_ci var->pixclock > vmode->pixclock || 2748c2ecf20Sopenharmony_ci var->hsync_len > vmode->hsync_len || 2758c2ecf20Sopenharmony_ci var->vsync_len > vmode->vsync_len) 2768c2ecf20Sopenharmony_ci return -1; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* progressive/interlaced must match */ 2798c2ecf20Sopenharmony_ci if ((var->vmode & FB_VMODE_MASK) != vmode->vmode) 2808c2ecf20Sopenharmony_ci return -1; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* minimum resolution */ 2838c2ecf20Sopenharmony_ci xres = max(var->xres, 1U); 2848c2ecf20Sopenharmony_ci yres = max(var->yres, 1U); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci /* minimum margins */ 2878c2ecf20Sopenharmony_ci left_margin = max(var->left_margin, vmode->left_margin); 2888c2ecf20Sopenharmony_ci right_margin = max(var->right_margin, vmode->right_margin); 2898c2ecf20Sopenharmony_ci upper_margin = max(var->upper_margin, vmode->upper_margin); 2908c2ecf20Sopenharmony_ci lower_margin = max(var->lower_margin, vmode->lower_margin); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* resolution + margins may not exceed native parameters */ 2938c2ecf20Sopenharmony_ci dx = ((long)vmode->left_margin + (long)vmode->xres + 2948c2ecf20Sopenharmony_ci (long)vmode->right_margin) - 2958c2ecf20Sopenharmony_ci (left_margin + xres + right_margin); 2968c2ecf20Sopenharmony_ci if (dx < 0) 2978c2ecf20Sopenharmony_ci return -1; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci dy = ((long)vmode->upper_margin + (long)vmode->yres + 3008c2ecf20Sopenharmony_ci (long)vmode->lower_margin) - 3018c2ecf20Sopenharmony_ci (upper_margin + yres + lower_margin); 3028c2ecf20Sopenharmony_ci if (dy < 0) 3038c2ecf20Sopenharmony_ci return -1; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* exact match */ 3068c2ecf20Sopenharmony_ci if (!dx && !dy) 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* resolution difference */ 3108c2ecf20Sopenharmony_ci return (vmode->xres - xres) * (vmode->yres - yres); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic const struct fb_videomode *ps3fb_native_vmode(enum ps3av_mode_num id) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci return &ps3fb_modedb[FIRST_NATIVE_MODE_INDEX + id - 1]; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic const struct fb_videomode *ps3fb_vmode(int id) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci u32 mode = id & PS3AV_MODE_MASK; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (mode < PS3AV_MODE_480I || mode > PS3AV_MODE_WUXGA) 3238c2ecf20Sopenharmony_ci return NULL; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (mode <= PS3AV_MODE_1080P50 && !(id & PS3AV_MODE_FULL)) { 3268c2ecf20Sopenharmony_ci /* Non-fullscreen broadcast mode */ 3278c2ecf20Sopenharmony_ci return &ps3fb_modedb[mode - 1]; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return ps3fb_native_vmode(mode); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic unsigned int ps3fb_find_mode(struct fb_var_screeninfo *var, 3348c2ecf20Sopenharmony_ci u32 *ddr_line_length, u32 *xdr_line_length) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci unsigned int id, best_id; 3378c2ecf20Sopenharmony_ci int diff, best_diff; 3388c2ecf20Sopenharmony_ci const struct fb_videomode *vmode; 3398c2ecf20Sopenharmony_ci long gap; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci best_id = 0; 3428c2ecf20Sopenharmony_ci best_diff = INT_MAX; 3438c2ecf20Sopenharmony_ci pr_debug("%s: wanted %u [%u] %u x %u [%u] %u\n", __func__, 3448c2ecf20Sopenharmony_ci var->left_margin, var->xres, var->right_margin, 3458c2ecf20Sopenharmony_ci var->upper_margin, var->yres, var->lower_margin); 3468c2ecf20Sopenharmony_ci for (id = PS3AV_MODE_480I; id <= PS3AV_MODE_WUXGA; id++) { 3478c2ecf20Sopenharmony_ci vmode = ps3fb_native_vmode(id); 3488c2ecf20Sopenharmony_ci diff = ps3fb_cmp_mode(vmode, var); 3498c2ecf20Sopenharmony_ci pr_debug("%s: mode %u: %u [%u] %u x %u [%u] %u: diff = %d\n", 3508c2ecf20Sopenharmony_ci __func__, id, vmode->left_margin, vmode->xres, 3518c2ecf20Sopenharmony_ci vmode->right_margin, vmode->upper_margin, 3528c2ecf20Sopenharmony_ci vmode->yres, vmode->lower_margin, diff); 3538c2ecf20Sopenharmony_ci if (diff < 0) 3548c2ecf20Sopenharmony_ci continue; 3558c2ecf20Sopenharmony_ci if (diff < best_diff) { 3568c2ecf20Sopenharmony_ci best_id = id; 3578c2ecf20Sopenharmony_ci if (!diff) 3588c2ecf20Sopenharmony_ci break; 3598c2ecf20Sopenharmony_ci best_diff = diff; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!best_id) { 3648c2ecf20Sopenharmony_ci pr_debug("%s: no suitable mode found\n", __func__); 3658c2ecf20Sopenharmony_ci return 0; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci id = best_id; 3698c2ecf20Sopenharmony_ci vmode = ps3fb_native_vmode(id); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci *ddr_line_length = vmode->xres * BPP; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* minimum resolution */ 3748c2ecf20Sopenharmony_ci if (!var->xres) 3758c2ecf20Sopenharmony_ci var->xres = 1; 3768c2ecf20Sopenharmony_ci if (!var->yres) 3778c2ecf20Sopenharmony_ci var->yres = 1; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* minimum virtual resolution */ 3808c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xres) 3818c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 3828c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yres) 3838c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* minimum margins */ 3868c2ecf20Sopenharmony_ci if (var->left_margin < vmode->left_margin) 3878c2ecf20Sopenharmony_ci var->left_margin = vmode->left_margin; 3888c2ecf20Sopenharmony_ci if (var->right_margin < vmode->right_margin) 3898c2ecf20Sopenharmony_ci var->right_margin = vmode->right_margin; 3908c2ecf20Sopenharmony_ci if (var->upper_margin < vmode->upper_margin) 3918c2ecf20Sopenharmony_ci var->upper_margin = vmode->upper_margin; 3928c2ecf20Sopenharmony_ci if (var->lower_margin < vmode->lower_margin) 3938c2ecf20Sopenharmony_ci var->lower_margin = vmode->lower_margin; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* extra margins */ 3968c2ecf20Sopenharmony_ci gap = ((long)vmode->left_margin + (long)vmode->xres + 3978c2ecf20Sopenharmony_ci (long)vmode->right_margin) - 3988c2ecf20Sopenharmony_ci ((long)var->left_margin + (long)var->xres + 3998c2ecf20Sopenharmony_ci (long)var->right_margin); 4008c2ecf20Sopenharmony_ci if (gap > 0) { 4018c2ecf20Sopenharmony_ci var->left_margin += gap/2; 4028c2ecf20Sopenharmony_ci var->right_margin += (gap+1)/2; 4038c2ecf20Sopenharmony_ci pr_debug("%s: rounded up H to %u [%u] %u\n", __func__, 4048c2ecf20Sopenharmony_ci var->left_margin, var->xres, var->right_margin); 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci gap = ((long)vmode->upper_margin + (long)vmode->yres + 4088c2ecf20Sopenharmony_ci (long)vmode->lower_margin) - 4098c2ecf20Sopenharmony_ci ((long)var->upper_margin + (long)var->yres + 4108c2ecf20Sopenharmony_ci (long)var->lower_margin); 4118c2ecf20Sopenharmony_ci if (gap > 0) { 4128c2ecf20Sopenharmony_ci var->upper_margin += gap/2; 4138c2ecf20Sopenharmony_ci var->lower_margin += (gap+1)/2; 4148c2ecf20Sopenharmony_ci pr_debug("%s: rounded up V to %u [%u] %u\n", __func__, 4158c2ecf20Sopenharmony_ci var->upper_margin, var->yres, var->lower_margin); 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci /* fixed fields */ 4198c2ecf20Sopenharmony_ci var->pixclock = vmode->pixclock; 4208c2ecf20Sopenharmony_ci var->hsync_len = vmode->hsync_len; 4218c2ecf20Sopenharmony_ci var->vsync_len = vmode->vsync_len; 4228c2ecf20Sopenharmony_ci var->sync = vmode->sync; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci if (ps3_compare_firmware_version(1, 9, 0) >= 0) { 4258c2ecf20Sopenharmony_ci *xdr_line_length = GPU_ALIGN_UP(var->xres_virtual * BPP); 4268c2ecf20Sopenharmony_ci if (*xdr_line_length > GPU_MAX_LINE_LENGTH) 4278c2ecf20Sopenharmony_ci *xdr_line_length = GPU_MAX_LINE_LENGTH; 4288c2ecf20Sopenharmony_ci } else 4298c2ecf20Sopenharmony_ci *xdr_line_length = *ddr_line_length; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci if (vmode->sync & FB_SYNC_BROADCAST) { 4328c2ecf20Sopenharmony_ci /* Full broadcast modes have the full mode bit set */ 4338c2ecf20Sopenharmony_ci if (vmode->xres == var->xres && vmode->yres == var->yres) 4348c2ecf20Sopenharmony_ci id |= PS3AV_MODE_FULL; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci pr_debug("%s: mode %u\n", __func__, id); 4388c2ecf20Sopenharmony_ci return id; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic void ps3fb_sync_image(struct device *dev, u64 frame_offset, 4428c2ecf20Sopenharmony_ci u64 dst_offset, u64 src_offset, u32 width, 4438c2ecf20Sopenharmony_ci u32 height, u32 dst_line_length, 4448c2ecf20Sopenharmony_ci u32 src_line_length) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int status; 4478c2ecf20Sopenharmony_ci u64 line_length; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci line_length = dst_line_length; 4508c2ecf20Sopenharmony_ci if (src_line_length != dst_line_length) 4518c2ecf20Sopenharmony_ci line_length |= (u64)src_line_length << 32; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci src_offset += GPU_FB_START; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci mutex_lock(&ps3_gpu_mutex); 4568c2ecf20Sopenharmony_ci status = lv1_gpu_fb_blit(ps3fb.context_handle, dst_offset, 4578c2ecf20Sopenharmony_ci GPU_IOIF + src_offset, 4588c2ecf20Sopenharmony_ci L1GPU_FB_BLIT_WAIT_FOR_COMPLETION | 4598c2ecf20Sopenharmony_ci (width << 16) | height, 4608c2ecf20Sopenharmony_ci line_length); 4618c2ecf20Sopenharmony_ci mutex_unlock(&ps3_gpu_mutex); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (status) 4648c2ecf20Sopenharmony_ci dev_err(dev, "%s: lv1_gpu_fb_blit failed: %d\n", __func__, 4658c2ecf20Sopenharmony_ci status); 4668c2ecf20Sopenharmony_ci#ifdef HEAD_A 4678c2ecf20Sopenharmony_ci status = lv1_gpu_display_flip(ps3fb.context_handle, 0, frame_offset); 4688c2ecf20Sopenharmony_ci if (status) 4698c2ecf20Sopenharmony_ci dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__, 4708c2ecf20Sopenharmony_ci status); 4718c2ecf20Sopenharmony_ci#endif 4728c2ecf20Sopenharmony_ci#ifdef HEAD_B 4738c2ecf20Sopenharmony_ci status = lv1_gpu_display_flip(ps3fb.context_handle, 1, frame_offset); 4748c2ecf20Sopenharmony_ci if (status) 4758c2ecf20Sopenharmony_ci dev_err(dev, "%s: lv1_gpu_display_flip failed: %d\n", __func__, 4768c2ecf20Sopenharmony_ci status); 4778c2ecf20Sopenharmony_ci#endif 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int ps3fb_sync(struct fb_info *info, u32 frame) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct ps3fb_par *par = info->par; 4838c2ecf20Sopenharmony_ci int error = 0; 4848c2ecf20Sopenharmony_ci u64 ddr_base, xdr_base; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (frame > par->num_frames - 1) { 4878c2ecf20Sopenharmony_ci dev_dbg(info->device, "%s: invalid frame number (%u)\n", 4888c2ecf20Sopenharmony_ci __func__, frame); 4898c2ecf20Sopenharmony_ci error = -EINVAL; 4908c2ecf20Sopenharmony_ci goto out; 4918c2ecf20Sopenharmony_ci } 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci xdr_base = frame * par->xdr_frame_size; 4948c2ecf20Sopenharmony_ci ddr_base = frame * par->ddr_frame_size; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci ps3fb_sync_image(info->device, ddr_base + par->full_offset, 4978c2ecf20Sopenharmony_ci ddr_base + par->fb_offset, xdr_base + par->pan_offset, 4988c2ecf20Sopenharmony_ci par->width, par->height, par->ddr_line_length, 4998c2ecf20Sopenharmony_ci info->fix.line_length); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ciout: 5028c2ecf20Sopenharmony_ci return error; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int ps3fb_open(struct fb_info *info, int user) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci atomic_inc(&ps3fb.f_count); 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic int ps3fb_release(struct fb_info *info, int user) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&ps3fb.f_count)) { 5148c2ecf20Sopenharmony_ci if (atomic_read(&ps3fb.ext_flip)) { 5158c2ecf20Sopenharmony_ci atomic_set(&ps3fb.ext_flip, 0); 5168c2ecf20Sopenharmony_ci if (console_trylock()) { 5178c2ecf20Sopenharmony_ci ps3fb_sync(info, 0); /* single buffer */ 5188c2ecf20Sopenharmony_ci console_unlock(); 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci return 0; 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* 5268c2ecf20Sopenharmony_ci * Setting the video mode has been split into two parts. 5278c2ecf20Sopenharmony_ci * First part, xxxfb_check_var, must not write anything 5288c2ecf20Sopenharmony_ci * to hardware, it should only verify and adjust var. 5298c2ecf20Sopenharmony_ci * This means it doesn't alter par but it does use hardware 5308c2ecf20Sopenharmony_ci * data from it to check this var. 5318c2ecf20Sopenharmony_ci */ 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic int ps3fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci u32 xdr_line_length, ddr_line_length; 5368c2ecf20Sopenharmony_ci int mode; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mode = ps3fb_find_mode(var, &ddr_line_length, &xdr_line_length); 5398c2ecf20Sopenharmony_ci if (!mode) 5408c2ecf20Sopenharmony_ci return -EINVAL; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* Virtual screen */ 5438c2ecf20Sopenharmony_ci if (var->xres_virtual > xdr_line_length / BPP) { 5448c2ecf20Sopenharmony_ci dev_dbg(info->device, 5458c2ecf20Sopenharmony_ci "Horizontal virtual screen size too large\n"); 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual || 5508c2ecf20Sopenharmony_ci var->yoffset + var->yres > var->yres_virtual) { 5518c2ecf20Sopenharmony_ci dev_dbg(info->device, "panning out-of-range\n"); 5528c2ecf20Sopenharmony_ci return -EINVAL; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* We support ARGB8888 only */ 5568c2ecf20Sopenharmony_ci if (var->bits_per_pixel > 32 || var->grayscale || 5578c2ecf20Sopenharmony_ci var->red.offset > 16 || var->green.offset > 8 || 5588c2ecf20Sopenharmony_ci var->blue.offset > 0 || var->transp.offset > 24 || 5598c2ecf20Sopenharmony_ci var->red.length > 8 || var->green.length > 8 || 5608c2ecf20Sopenharmony_ci var->blue.length > 8 || var->transp.length > 8 || 5618c2ecf20Sopenharmony_ci var->red.msb_right || var->green.msb_right || 5628c2ecf20Sopenharmony_ci var->blue.msb_right || var->transp.msb_right || var->nonstd) { 5638c2ecf20Sopenharmony_ci dev_dbg(info->device, "We support ARGB8888 only\n"); 5648c2ecf20Sopenharmony_ci return -EINVAL; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 5688c2ecf20Sopenharmony_ci var->red.offset = 16; 5698c2ecf20Sopenharmony_ci var->green.offset = 8; 5708c2ecf20Sopenharmony_ci var->blue.offset = 0; 5718c2ecf20Sopenharmony_ci var->transp.offset = 24; 5728c2ecf20Sopenharmony_ci var->red.length = 8; 5738c2ecf20Sopenharmony_ci var->green.length = 8; 5748c2ecf20Sopenharmony_ci var->blue.length = 8; 5758c2ecf20Sopenharmony_ci var->transp.length = 8; 5768c2ecf20Sopenharmony_ci var->red.msb_right = 0; 5778c2ecf20Sopenharmony_ci var->green.msb_right = 0; 5788c2ecf20Sopenharmony_ci var->blue.msb_right = 0; 5798c2ecf20Sopenharmony_ci var->transp.msb_right = 0; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Rotation is not supported */ 5828c2ecf20Sopenharmony_ci if (var->rotate) { 5838c2ecf20Sopenharmony_ci dev_dbg(info->device, "Rotation is not supported\n"); 5848c2ecf20Sopenharmony_ci return -EINVAL; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Memory limit */ 5888c2ecf20Sopenharmony_ci if (var->yres_virtual * xdr_line_length > info->fix.smem_len) { 5898c2ecf20Sopenharmony_ci dev_dbg(info->device, "Not enough memory\n"); 5908c2ecf20Sopenharmony_ci return -ENOMEM; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci var->height = -1; 5948c2ecf20Sopenharmony_ci var->width = -1; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* 6008c2ecf20Sopenharmony_ci * This routine actually sets the video mode. 6018c2ecf20Sopenharmony_ci */ 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic int ps3fb_set_par(struct fb_info *info) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct ps3fb_par *par = info->par; 6068c2ecf20Sopenharmony_ci unsigned int mode, ddr_line_length, xdr_line_length, lines, maxlines; 6078c2ecf20Sopenharmony_ci unsigned int ddr_xoff, ddr_yoff, offset; 6088c2ecf20Sopenharmony_ci const struct fb_videomode *vmode; 6098c2ecf20Sopenharmony_ci u64 dst; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci mode = ps3fb_find_mode(&info->var, &ddr_line_length, &xdr_line_length); 6128c2ecf20Sopenharmony_ci if (!mode) 6138c2ecf20Sopenharmony_ci return -EINVAL; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci vmode = ps3fb_native_vmode(mode & PS3AV_MODE_MASK); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci info->fix.xpanstep = info->var.xres_virtual > info->var.xres ? 1 : 0; 6188c2ecf20Sopenharmony_ci info->fix.ypanstep = info->var.yres_virtual > info->var.yres ? 1 : 0; 6198c2ecf20Sopenharmony_ci info->fix.line_length = xdr_line_length; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci par->ddr_line_length = ddr_line_length; 6228c2ecf20Sopenharmony_ci par->ddr_frame_size = vmode->yres * ddr_line_length; 6238c2ecf20Sopenharmony_ci par->xdr_frame_size = info->var.yres_virtual * xdr_line_length; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci par->num_frames = info->fix.smem_len / 6268c2ecf20Sopenharmony_ci max(par->ddr_frame_size, par->xdr_frame_size); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Keep the special bits we cannot set using fb_var_screeninfo */ 6298c2ecf20Sopenharmony_ci par->new_mode_id = (par->new_mode_id & ~PS3AV_MODE_MASK) | mode; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci par->width = info->var.xres; 6328c2ecf20Sopenharmony_ci par->height = info->var.yres; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* Start of the virtual frame buffer (relative to fullscreen) */ 6358c2ecf20Sopenharmony_ci ddr_xoff = info->var.left_margin - vmode->left_margin; 6368c2ecf20Sopenharmony_ci ddr_yoff = info->var.upper_margin - vmode->upper_margin; 6378c2ecf20Sopenharmony_ci offset = ddr_yoff * ddr_line_length + ddr_xoff * BPP; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci par->fb_offset = GPU_ALIGN_UP(offset); 6408c2ecf20Sopenharmony_ci par->full_offset = par->fb_offset - offset; 6418c2ecf20Sopenharmony_ci par->pan_offset = info->var.yoffset * xdr_line_length + 6428c2ecf20Sopenharmony_ci info->var.xoffset * BPP; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (par->new_mode_id != par->mode_id) { 6458c2ecf20Sopenharmony_ci if (ps3av_set_video_mode(par->new_mode_id)) { 6468c2ecf20Sopenharmony_ci par->new_mode_id = par->mode_id; 6478c2ecf20Sopenharmony_ci return -EINVAL; 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci par->mode_id = par->new_mode_id; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci /* Clear XDR frame buffer memory */ 6538c2ecf20Sopenharmony_ci memset((void __force *)info->screen_base, 0, info->fix.smem_len); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Clear DDR frame buffer memory */ 6568c2ecf20Sopenharmony_ci lines = vmode->yres * par->num_frames; 6578c2ecf20Sopenharmony_ci if (par->full_offset) 6588c2ecf20Sopenharmony_ci lines++; 6598c2ecf20Sopenharmony_ci maxlines = info->fix.smem_len / ddr_line_length; 6608c2ecf20Sopenharmony_ci for (dst = 0; lines; dst += maxlines * ddr_line_length) { 6618c2ecf20Sopenharmony_ci unsigned int l = min(lines, maxlines); 6628c2ecf20Sopenharmony_ci ps3fb_sync_image(info->device, 0, dst, 0, vmode->xres, l, 6638c2ecf20Sopenharmony_ci ddr_line_length, ddr_line_length); 6648c2ecf20Sopenharmony_ci lines -= l; 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci return 0; 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* 6718c2ecf20Sopenharmony_ci * Set a single color register. The values supplied are already 6728c2ecf20Sopenharmony_ci * rounded down to the hardware's capabilities (according to the 6738c2ecf20Sopenharmony_ci * entries in the var structure). Return != 0 for invalid regno. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int ps3fb_setcolreg(unsigned int regno, unsigned int red, 6778c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 6788c2ecf20Sopenharmony_ci unsigned int transp, struct fb_info *info) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci if (regno >= 16) 6818c2ecf20Sopenharmony_ci return 1; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci red >>= 8; 6848c2ecf20Sopenharmony_ci green >>= 8; 6858c2ecf20Sopenharmony_ci blue >>= 8; 6868c2ecf20Sopenharmony_ci transp >>= 8; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci ((u32 *)info->pseudo_palette)[regno] = transp << 24 | red << 16 | 6898c2ecf20Sopenharmony_ci green << 8 | blue; 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int ps3fb_pan_display(struct fb_var_screeninfo *var, 6948c2ecf20Sopenharmony_ci struct fb_info *info) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct ps3fb_par *par = info->par; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci par->pan_offset = var->yoffset * info->fix.line_length + 6998c2ecf20Sopenharmony_ci var->xoffset * BPP; 7008c2ecf20Sopenharmony_ci return 0; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* 7048c2ecf20Sopenharmony_ci * As we have a virtual frame buffer, we need our own mmap function 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_cistatic int ps3fb_mmap(struct fb_info *info, struct vm_area_struct *vma) 7088c2ecf20Sopenharmony_ci{ 7098c2ecf20Sopenharmony_ci int r; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci r = vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci dev_dbg(info->device, "ps3fb: mmap framebuffer P(%lx)->V(%lx)\n", 7148c2ecf20Sopenharmony_ci info->fix.smem_start + (vma->vm_pgoff << PAGE_SHIFT), 7158c2ecf20Sopenharmony_ci vma->vm_start); 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return r; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* 7218c2ecf20Sopenharmony_ci * Blank the display 7228c2ecf20Sopenharmony_ci */ 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic int ps3fb_blank(int blank, struct fb_info *info) 7258c2ecf20Sopenharmony_ci{ 7268c2ecf20Sopenharmony_ci int retval; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci dev_dbg(info->device, "%s: blank:%d\n", __func__, blank); 7298c2ecf20Sopenharmony_ci switch (blank) { 7308c2ecf20Sopenharmony_ci case FB_BLANK_POWERDOWN: 7318c2ecf20Sopenharmony_ci case FB_BLANK_HSYNC_SUSPEND: 7328c2ecf20Sopenharmony_ci case FB_BLANK_VSYNC_SUSPEND: 7338c2ecf20Sopenharmony_ci case FB_BLANK_NORMAL: 7348c2ecf20Sopenharmony_ci retval = ps3av_video_mute(1); /* mute on */ 7358c2ecf20Sopenharmony_ci if (!retval) 7368c2ecf20Sopenharmony_ci ps3fb.is_blanked = 1; 7378c2ecf20Sopenharmony_ci break; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci default: /* unblank */ 7408c2ecf20Sopenharmony_ci retval = ps3av_video_mute(0); /* mute off */ 7418c2ecf20Sopenharmony_ci if (!retval) 7428c2ecf20Sopenharmony_ci ps3fb.is_blanked = 0; 7438c2ecf20Sopenharmony_ci break; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci return retval; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int ps3fb_get_vblank(struct fb_vblank *vblank) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci memset(vblank, 0, sizeof(*vblank)); 7518c2ecf20Sopenharmony_ci vblank->flags = FB_VBLANK_HAVE_VSYNC; 7528c2ecf20Sopenharmony_ci return 0; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic int ps3fb_wait_for_vsync(u32 crtc) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci int ret; 7588c2ecf20Sopenharmony_ci u64 count; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci count = ps3fb.vblank_count; 7618c2ecf20Sopenharmony_ci ret = wait_event_interruptible_timeout(ps3fb.wait_vsync, 7628c2ecf20Sopenharmony_ci count != ps3fb.vblank_count, 7638c2ecf20Sopenharmony_ci HZ / 10); 7648c2ecf20Sopenharmony_ci if (!ret) 7658c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* 7728c2ecf20Sopenharmony_ci * ioctl 7738c2ecf20Sopenharmony_ci */ 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int ps3fb_ioctl(struct fb_info *info, unsigned int cmd, 7768c2ecf20Sopenharmony_ci unsigned long arg) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 7798c2ecf20Sopenharmony_ci u32 val; 7808c2ecf20Sopenharmony_ci int retval = -EFAULT; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci switch (cmd) { 7838c2ecf20Sopenharmony_ci case FBIOGET_VBLANK: 7848c2ecf20Sopenharmony_ci { 7858c2ecf20Sopenharmony_ci struct fb_vblank vblank; 7868c2ecf20Sopenharmony_ci dev_dbg(info->device, "FBIOGET_VBLANK:\n"); 7878c2ecf20Sopenharmony_ci retval = ps3fb_get_vblank(&vblank); 7888c2ecf20Sopenharmony_ci if (retval) 7898c2ecf20Sopenharmony_ci break; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (copy_to_user(argp, &vblank, sizeof(vblank))) 7928c2ecf20Sopenharmony_ci retval = -EFAULT; 7938c2ecf20Sopenharmony_ci break; 7948c2ecf20Sopenharmony_ci } 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci case FBIO_WAITFORVSYNC: 7978c2ecf20Sopenharmony_ci { 7988c2ecf20Sopenharmony_ci u32 crt; 7998c2ecf20Sopenharmony_ci dev_dbg(info->device, "FBIO_WAITFORVSYNC:\n"); 8008c2ecf20Sopenharmony_ci if (get_user(crt, (u32 __user *) arg)) 8018c2ecf20Sopenharmony_ci break; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci retval = ps3fb_wait_for_vsync(crt); 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci case PS3FB_IOCTL_SETMODE: 8088c2ecf20Sopenharmony_ci { 8098c2ecf20Sopenharmony_ci struct ps3fb_par *par = info->par; 8108c2ecf20Sopenharmony_ci const struct fb_videomode *vmode; 8118c2ecf20Sopenharmony_ci struct fb_var_screeninfo var; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci if (copy_from_user(&val, argp, sizeof(val))) 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!(val & PS3AV_MODE_MASK)) { 8178c2ecf20Sopenharmony_ci u32 id = ps3av_get_auto_mode(); 8188c2ecf20Sopenharmony_ci if (id > 0) 8198c2ecf20Sopenharmony_ci val = (val & ~PS3AV_MODE_MASK) | id; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_SETMODE:%x\n", val); 8228c2ecf20Sopenharmony_ci retval = -EINVAL; 8238c2ecf20Sopenharmony_ci vmode = ps3fb_vmode(val); 8248c2ecf20Sopenharmony_ci if (vmode) { 8258c2ecf20Sopenharmony_ci var = info->var; 8268c2ecf20Sopenharmony_ci fb_videomode_to_var(&var, vmode); 8278c2ecf20Sopenharmony_ci console_lock(); 8288c2ecf20Sopenharmony_ci /* Force, in case only special bits changed */ 8298c2ecf20Sopenharmony_ci var.activate |= FB_ACTIVATE_FORCE; 8308c2ecf20Sopenharmony_ci par->new_mode_id = val; 8318c2ecf20Sopenharmony_ci retval = fb_set_var(info, &var); 8328c2ecf20Sopenharmony_ci if (!retval) 8338c2ecf20Sopenharmony_ci fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); 8348c2ecf20Sopenharmony_ci console_unlock(); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci case PS3FB_IOCTL_GETMODE: 8408c2ecf20Sopenharmony_ci val = ps3av_get_mode(); 8418c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_GETMODE:%x\n", val); 8428c2ecf20Sopenharmony_ci if (!copy_to_user(argp, &val, sizeof(val))) 8438c2ecf20Sopenharmony_ci retval = 0; 8448c2ecf20Sopenharmony_ci break; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci case PS3FB_IOCTL_SCREENINFO: 8478c2ecf20Sopenharmony_ci { 8488c2ecf20Sopenharmony_ci struct ps3fb_par *par = info->par; 8498c2ecf20Sopenharmony_ci struct ps3fb_ioctl_res res; 8508c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_SCREENINFO:\n"); 8518c2ecf20Sopenharmony_ci res.xres = info->fix.line_length / BPP; 8528c2ecf20Sopenharmony_ci res.yres = info->var.yres_virtual; 8538c2ecf20Sopenharmony_ci res.xoff = (res.xres - info->var.xres) / 2; 8548c2ecf20Sopenharmony_ci res.yoff = (res.yres - info->var.yres) / 2; 8558c2ecf20Sopenharmony_ci res.num_frames = par->num_frames; 8568c2ecf20Sopenharmony_ci if (!copy_to_user(argp, &res, sizeof(res))) 8578c2ecf20Sopenharmony_ci retval = 0; 8588c2ecf20Sopenharmony_ci break; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci case PS3FB_IOCTL_ON: 8628c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_ON:\n"); 8638c2ecf20Sopenharmony_ci atomic_inc(&ps3fb.ext_flip); 8648c2ecf20Sopenharmony_ci retval = 0; 8658c2ecf20Sopenharmony_ci break; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci case PS3FB_IOCTL_OFF: 8688c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_OFF:\n"); 8698c2ecf20Sopenharmony_ci atomic_dec_if_positive(&ps3fb.ext_flip); 8708c2ecf20Sopenharmony_ci retval = 0; 8718c2ecf20Sopenharmony_ci break; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci case PS3FB_IOCTL_FSEL: 8748c2ecf20Sopenharmony_ci if (copy_from_user(&val, argp, sizeof(val))) 8758c2ecf20Sopenharmony_ci break; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci dev_dbg(info->device, "PS3FB_IOCTL_FSEL:%d\n", val); 8788c2ecf20Sopenharmony_ci console_lock(); 8798c2ecf20Sopenharmony_ci retval = ps3fb_sync(info, val); 8808c2ecf20Sopenharmony_ci console_unlock(); 8818c2ecf20Sopenharmony_ci break; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci default: 8848c2ecf20Sopenharmony_ci retval = -ENOIOCTLCMD; 8858c2ecf20Sopenharmony_ci break; 8868c2ecf20Sopenharmony_ci } 8878c2ecf20Sopenharmony_ci return retval; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_cistatic int ps3fbd(void *arg) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct fb_info *info = arg; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci set_freezable(); 8958c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 8968c2ecf20Sopenharmony_ci try_to_freeze(); 8978c2ecf20Sopenharmony_ci set_current_state(TASK_INTERRUPTIBLE); 8988c2ecf20Sopenharmony_ci if (ps3fb.is_kicked) { 8998c2ecf20Sopenharmony_ci ps3fb.is_kicked = 0; 9008c2ecf20Sopenharmony_ci console_lock(); 9018c2ecf20Sopenharmony_ci ps3fb_sync(info, 0); /* single buffer */ 9028c2ecf20Sopenharmony_ci console_unlock(); 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci schedule(); 9058c2ecf20Sopenharmony_ci } 9068c2ecf20Sopenharmony_ci return 0; 9078c2ecf20Sopenharmony_ci} 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_cistatic irqreturn_t ps3fb_vsync_interrupt(int irq, void *ptr) 9108c2ecf20Sopenharmony_ci{ 9118c2ecf20Sopenharmony_ci struct device *dev = ptr; 9128c2ecf20Sopenharmony_ci u64 v1; 9138c2ecf20Sopenharmony_ci int status; 9148c2ecf20Sopenharmony_ci struct display_head *head = &ps3fb.dinfo->display_head[1]; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci status = lv1_gpu_context_intr(ps3fb.context_handle, &v1); 9178c2ecf20Sopenharmony_ci if (status) { 9188c2ecf20Sopenharmony_ci dev_err(dev, "%s: lv1_gpu_context_intr failed: %d\n", __func__, 9198c2ecf20Sopenharmony_ci status); 9208c2ecf20Sopenharmony_ci return IRQ_NONE; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci if (v1 & (1 << GPU_INTR_STATUS_VSYNC_1)) { 9248c2ecf20Sopenharmony_ci /* VSYNC */ 9258c2ecf20Sopenharmony_ci ps3fb.vblank_count = head->vblank_count; 9268c2ecf20Sopenharmony_ci if (ps3fb.task && !ps3fb.is_blanked && 9278c2ecf20Sopenharmony_ci !atomic_read(&ps3fb.ext_flip)) { 9288c2ecf20Sopenharmony_ci ps3fb.is_kicked = 1; 9298c2ecf20Sopenharmony_ci wake_up_process(ps3fb.task); 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci wake_up_interruptible(&ps3fb.wait_vsync); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_cistatic const struct fb_ops ps3fb_ops = { 9398c2ecf20Sopenharmony_ci .fb_open = ps3fb_open, 9408c2ecf20Sopenharmony_ci .fb_release = ps3fb_release, 9418c2ecf20Sopenharmony_ci .fb_read = fb_sys_read, 9428c2ecf20Sopenharmony_ci .fb_write = fb_sys_write, 9438c2ecf20Sopenharmony_ci .fb_check_var = ps3fb_check_var, 9448c2ecf20Sopenharmony_ci .fb_set_par = ps3fb_set_par, 9458c2ecf20Sopenharmony_ci .fb_setcolreg = ps3fb_setcolreg, 9468c2ecf20Sopenharmony_ci .fb_pan_display = ps3fb_pan_display, 9478c2ecf20Sopenharmony_ci .fb_fillrect = sys_fillrect, 9488c2ecf20Sopenharmony_ci .fb_copyarea = sys_copyarea, 9498c2ecf20Sopenharmony_ci .fb_imageblit = sys_imageblit, 9508c2ecf20Sopenharmony_ci .fb_mmap = ps3fb_mmap, 9518c2ecf20Sopenharmony_ci .fb_blank = ps3fb_blank, 9528c2ecf20Sopenharmony_ci .fb_ioctl = ps3fb_ioctl, 9538c2ecf20Sopenharmony_ci .fb_compat_ioctl = ps3fb_ioctl 9548c2ecf20Sopenharmony_ci}; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_cistatic const struct fb_fix_screeninfo ps3fb_fix = { 9578c2ecf20Sopenharmony_ci .id = DEVICE_NAME, 9588c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 9598c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 9608c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 9618c2ecf20Sopenharmony_ci}; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic int ps3fb_probe(struct ps3_system_bus_device *dev) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct fb_info *info; 9668c2ecf20Sopenharmony_ci struct ps3fb_par *par; 9678c2ecf20Sopenharmony_ci int retval; 9688c2ecf20Sopenharmony_ci u64 ddr_lpar = 0; 9698c2ecf20Sopenharmony_ci u64 lpar_dma_control = 0; 9708c2ecf20Sopenharmony_ci u64 lpar_driver_info = 0; 9718c2ecf20Sopenharmony_ci u64 lpar_reports = 0; 9728c2ecf20Sopenharmony_ci u64 lpar_reports_size = 0; 9738c2ecf20Sopenharmony_ci u64 xdr_lpar; 9748c2ecf20Sopenharmony_ci struct gpu_driver_info *dinfo; 9758c2ecf20Sopenharmony_ci void *fb_start; 9768c2ecf20Sopenharmony_ci int status; 9778c2ecf20Sopenharmony_ci struct task_struct *task; 9788c2ecf20Sopenharmony_ci unsigned long max_ps3fb_size; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (ps3fb_videomemory.size < GPU_CMD_BUF_SIZE) { 9818c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: Not enough video memory\n", __func__); 9828c2ecf20Sopenharmony_ci return -ENOMEM; 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci retval = ps3_open_hv_device(dev); 9868c2ecf20Sopenharmony_ci if (retval) { 9878c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: ps3_open_hv_device failed\n", 9888c2ecf20Sopenharmony_ci __func__); 9898c2ecf20Sopenharmony_ci goto err; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci if (!ps3fb_mode) 9938c2ecf20Sopenharmony_ci ps3fb_mode = ps3av_get_mode(); 9948c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "ps3fb_mode: %d\n", ps3fb_mode); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci atomic_set(&ps3fb.f_count, -1); /* fbcon opens ps3fb */ 9978c2ecf20Sopenharmony_ci atomic_set(&ps3fb.ext_flip, 0); /* for flip with vsync */ 9988c2ecf20Sopenharmony_ci init_waitqueue_head(&ps3fb.wait_vsync); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci#ifdef HEAD_A 10018c2ecf20Sopenharmony_ci status = lv1_gpu_display_sync(0x0, 0, L1GPU_DISPLAY_SYNC_VSYNC); 10028c2ecf20Sopenharmony_ci if (status) { 10038c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n", 10048c2ecf20Sopenharmony_ci __func__, status); 10058c2ecf20Sopenharmony_ci retval = -ENODEV; 10068c2ecf20Sopenharmony_ci goto err_close_device; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci#endif 10098c2ecf20Sopenharmony_ci#ifdef HEAD_B 10108c2ecf20Sopenharmony_ci status = lv1_gpu_display_sync(0x0, 1, L1GPU_DISPLAY_SYNC_VSYNC); 10118c2ecf20Sopenharmony_ci if (status) { 10128c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: lv1_gpu_display_sync failed: %d\n", 10138c2ecf20Sopenharmony_ci __func__, status); 10148c2ecf20Sopenharmony_ci retval = -ENODEV; 10158c2ecf20Sopenharmony_ci goto err_close_device; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci#endif 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci max_ps3fb_size = ALIGN(GPU_IOIF, 256*1024*1024) - GPU_IOIF; 10208c2ecf20Sopenharmony_ci if (ps3fb_videomemory.size > max_ps3fb_size) { 10218c2ecf20Sopenharmony_ci dev_info(&dev->core, "Limiting ps3fb mem size to %lu bytes\n", 10228c2ecf20Sopenharmony_ci max_ps3fb_size); 10238c2ecf20Sopenharmony_ci ps3fb_videomemory.size = max_ps3fb_size; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* get gpu context handle */ 10278c2ecf20Sopenharmony_ci status = lv1_gpu_memory_allocate(ps3fb_videomemory.size, 0, 0, 0, 0, 10288c2ecf20Sopenharmony_ci &ps3fb.memory_handle, &ddr_lpar); 10298c2ecf20Sopenharmony_ci if (status) { 10308c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: lv1_gpu_memory_allocate failed: %d\n", 10318c2ecf20Sopenharmony_ci __func__, status); 10328c2ecf20Sopenharmony_ci retval = -ENOMEM; 10338c2ecf20Sopenharmony_ci goto err_close_device; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "ddr:lpar:0x%llx\n", ddr_lpar); 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci status = lv1_gpu_context_allocate(ps3fb.memory_handle, 0, 10388c2ecf20Sopenharmony_ci &ps3fb.context_handle, 10398c2ecf20Sopenharmony_ci &lpar_dma_control, &lpar_driver_info, 10408c2ecf20Sopenharmony_ci &lpar_reports, &lpar_reports_size); 10418c2ecf20Sopenharmony_ci if (status) { 10428c2ecf20Sopenharmony_ci dev_err(&dev->core, 10438c2ecf20Sopenharmony_ci "%s: lv1_gpu_context_allocate failed: %d\n", __func__, 10448c2ecf20Sopenharmony_ci status); 10458c2ecf20Sopenharmony_ci retval = -ENOMEM; 10468c2ecf20Sopenharmony_ci goto err_gpu_memory_free; 10478c2ecf20Sopenharmony_ci } 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* vsync interrupt */ 10508c2ecf20Sopenharmony_ci dinfo = (void __force *)ioremap(lpar_driver_info, 128 * 1024); 10518c2ecf20Sopenharmony_ci if (!dinfo) { 10528c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: ioremap failed\n", __func__); 10538c2ecf20Sopenharmony_ci retval = -ENOMEM; 10548c2ecf20Sopenharmony_ci goto err_gpu_context_free; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ps3fb.dinfo = dinfo; 10588c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "version_driver:%x\n", dinfo->version_driver); 10598c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "irq outlet:%x\n", dinfo->irq.irq_outlet); 10608c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "version_gpu: %x memory_size: %x ch: %x " 10618c2ecf20Sopenharmony_ci "core_freq: %d mem_freq:%d\n", dinfo->version_gpu, 10628c2ecf20Sopenharmony_ci dinfo->memory_size, dinfo->hardware_channel, 10638c2ecf20Sopenharmony_ci dinfo->nvcore_frequency/1000000, 10648c2ecf20Sopenharmony_ci dinfo->memory_frequency/1000000); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci if (dinfo->version_driver != GPU_DRIVER_INFO_VERSION) { 10678c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: version_driver err:%x\n", __func__, 10688c2ecf20Sopenharmony_ci dinfo->version_driver); 10698c2ecf20Sopenharmony_ci retval = -EINVAL; 10708c2ecf20Sopenharmony_ci goto err_iounmap_dinfo; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci retval = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, dinfo->irq.irq_outlet, 10748c2ecf20Sopenharmony_ci &ps3fb.irq_no); 10758c2ecf20Sopenharmony_ci if (retval) { 10768c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: ps3_alloc_irq failed %d\n", __func__, 10778c2ecf20Sopenharmony_ci retval); 10788c2ecf20Sopenharmony_ci goto err_iounmap_dinfo; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci retval = request_irq(ps3fb.irq_no, ps3fb_vsync_interrupt, 10828c2ecf20Sopenharmony_ci 0, DEVICE_NAME, &dev->core); 10838c2ecf20Sopenharmony_ci if (retval) { 10848c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: request_irq failed %d\n", __func__, 10858c2ecf20Sopenharmony_ci retval); 10868c2ecf20Sopenharmony_ci goto err_destroy_plug; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci dinfo->irq.mask = (1 << GPU_INTR_STATUS_VSYNC_1) | 10908c2ecf20Sopenharmony_ci (1 << GPU_INTR_STATUS_FLIP_1); 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* Clear memory to prevent kernel info leakage into userspace */ 10938c2ecf20Sopenharmony_ci memset(ps3fb_videomemory.address, 0, ps3fb_videomemory.size); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci status = lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, 10988c2ecf20Sopenharmony_ci xdr_lpar, ps3fb_videomemory.size, 10998c2ecf20Sopenharmony_ci CBE_IOPTE_PP_W | CBE_IOPTE_PP_R | 11008c2ecf20Sopenharmony_ci CBE_IOPTE_M); 11018c2ecf20Sopenharmony_ci if (status) { 11028c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: lv1_gpu_context_iomap failed: %d\n", 11038c2ecf20Sopenharmony_ci __func__, status); 11048c2ecf20Sopenharmony_ci retval = -ENXIO; 11058c2ecf20Sopenharmony_ci goto err_free_irq; 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci dev_dbg(&dev->core, "video:%p ioif:%lx lpar:%llx size:%lx\n", 11098c2ecf20Sopenharmony_ci ps3fb_videomemory.address, GPU_IOIF, xdr_lpar, 11108c2ecf20Sopenharmony_ci ps3fb_videomemory.size); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci status = lv1_gpu_fb_setup(ps3fb.context_handle, xdr_lpar, 11138c2ecf20Sopenharmony_ci GPU_CMD_BUF_SIZE, GPU_IOIF); 11148c2ecf20Sopenharmony_ci if (status) { 11158c2ecf20Sopenharmony_ci dev_err(&dev->core, "%s: lv1_gpu_fb_setup failed: %d\n", 11168c2ecf20Sopenharmony_ci __func__, status); 11178c2ecf20Sopenharmony_ci retval = -ENXIO; 11188c2ecf20Sopenharmony_ci goto err_context_unmap; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct ps3fb_par), &dev->core); 11228c2ecf20Sopenharmony_ci if (!info) { 11238c2ecf20Sopenharmony_ci retval = -ENOMEM; 11248c2ecf20Sopenharmony_ci goto err_context_fb_close; 11258c2ecf20Sopenharmony_ci } 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci par = info->par; 11288c2ecf20Sopenharmony_ci par->mode_id = ~ps3fb_mode; /* != ps3fb_mode, to trigger change */ 11298c2ecf20Sopenharmony_ci par->new_mode_id = ps3fb_mode; 11308c2ecf20Sopenharmony_ci par->num_frames = 1; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci info->fbops = &ps3fb_ops; 11338c2ecf20Sopenharmony_ci info->fix = ps3fb_fix; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * The GPU command buffer is at the start of video memory 11378c2ecf20Sopenharmony_ci * As we don't use the full command buffer, we can put the actual 11388c2ecf20Sopenharmony_ci * frame buffer at offset GPU_FB_START and save some precious XDR 11398c2ecf20Sopenharmony_ci * memory 11408c2ecf20Sopenharmony_ci */ 11418c2ecf20Sopenharmony_ci fb_start = ps3fb_videomemory.address + GPU_FB_START; 11428c2ecf20Sopenharmony_ci info->screen_base = (char __force __iomem *)fb_start; 11438c2ecf20Sopenharmony_ci info->fix.smem_start = __pa(fb_start); 11448c2ecf20Sopenharmony_ci info->fix.smem_len = ps3fb_videomemory.size - GPU_FB_START; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci info->pseudo_palette = par->pseudo_palette; 11478c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_READS_FAST | 11488c2ecf20Sopenharmony_ci FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci retval = fb_alloc_cmap(&info->cmap, 256, 0); 11518c2ecf20Sopenharmony_ci if (retval < 0) 11528c2ecf20Sopenharmony_ci goto err_framebuffer_release; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (!fb_find_mode(&info->var, info, mode_option, ps3fb_modedb, 11558c2ecf20Sopenharmony_ci ARRAY_SIZE(ps3fb_modedb), 11568c2ecf20Sopenharmony_ci ps3fb_vmode(par->new_mode_id), 32)) { 11578c2ecf20Sopenharmony_ci retval = -EINVAL; 11588c2ecf20Sopenharmony_ci goto err_fb_dealloc; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci fb_videomode_to_modelist(ps3fb_modedb, ARRAY_SIZE(ps3fb_modedb), 11628c2ecf20Sopenharmony_ci &info->modelist); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci retval = register_framebuffer(info); 11658c2ecf20Sopenharmony_ci if (retval < 0) 11668c2ecf20Sopenharmony_ci goto err_fb_dealloc; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci ps3_system_bus_set_drvdata(dev, info); 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci dev_info(info->device, "%s %s, using %u KiB of video memory\n", 11718c2ecf20Sopenharmony_ci dev_driver_string(info->dev), dev_name(info->dev), 11728c2ecf20Sopenharmony_ci info->fix.smem_len >> 10); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci task = kthread_run(ps3fbd, info, DEVICE_NAME); 11758c2ecf20Sopenharmony_ci if (IS_ERR(task)) { 11768c2ecf20Sopenharmony_ci retval = PTR_ERR(task); 11778c2ecf20Sopenharmony_ci goto err_unregister_framebuffer; 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci ps3fb.task = task; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci return 0; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cierr_unregister_framebuffer: 11858c2ecf20Sopenharmony_ci unregister_framebuffer(info); 11868c2ecf20Sopenharmony_cierr_fb_dealloc: 11878c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 11888c2ecf20Sopenharmony_cierr_framebuffer_release: 11898c2ecf20Sopenharmony_ci framebuffer_release(info); 11908c2ecf20Sopenharmony_cierr_context_fb_close: 11918c2ecf20Sopenharmony_ci lv1_gpu_fb_close(ps3fb.context_handle); 11928c2ecf20Sopenharmony_cierr_context_unmap: 11938c2ecf20Sopenharmony_ci lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, 11948c2ecf20Sopenharmony_ci ps3fb_videomemory.size, CBE_IOPTE_M); 11958c2ecf20Sopenharmony_cierr_free_irq: 11968c2ecf20Sopenharmony_ci free_irq(ps3fb.irq_no, &dev->core); 11978c2ecf20Sopenharmony_cierr_destroy_plug: 11988c2ecf20Sopenharmony_ci ps3_irq_plug_destroy(ps3fb.irq_no); 11998c2ecf20Sopenharmony_cierr_iounmap_dinfo: 12008c2ecf20Sopenharmony_ci iounmap((u8 __force __iomem *)ps3fb.dinfo); 12018c2ecf20Sopenharmony_cierr_gpu_context_free: 12028c2ecf20Sopenharmony_ci lv1_gpu_context_free(ps3fb.context_handle); 12038c2ecf20Sopenharmony_cierr_gpu_memory_free: 12048c2ecf20Sopenharmony_ci lv1_gpu_memory_free(ps3fb.memory_handle); 12058c2ecf20Sopenharmony_cierr_close_device: 12068c2ecf20Sopenharmony_ci ps3_close_hv_device(dev); 12078c2ecf20Sopenharmony_cierr: 12088c2ecf20Sopenharmony_ci return retval; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_cistatic int ps3fb_shutdown(struct ps3_system_bus_device *dev) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci struct fb_info *info = ps3_system_bus_get_drvdata(dev); 12148c2ecf20Sopenharmony_ci u64 xdr_lpar = ps3_mm_phys_to_lpar(__pa(ps3fb_videomemory.address)); 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci atomic_inc(&ps3fb.ext_flip); /* flip off */ 12198c2ecf20Sopenharmony_ci ps3fb.dinfo->irq.mask = 0; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci if (ps3fb.task) { 12228c2ecf20Sopenharmony_ci struct task_struct *task = ps3fb.task; 12238c2ecf20Sopenharmony_ci ps3fb.task = NULL; 12248c2ecf20Sopenharmony_ci kthread_stop(task); 12258c2ecf20Sopenharmony_ci } 12268c2ecf20Sopenharmony_ci if (ps3fb.irq_no) { 12278c2ecf20Sopenharmony_ci free_irq(ps3fb.irq_no, &dev->core); 12288c2ecf20Sopenharmony_ci ps3_irq_plug_destroy(ps3fb.irq_no); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci if (info) { 12318c2ecf20Sopenharmony_ci unregister_framebuffer(info); 12328c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 12338c2ecf20Sopenharmony_ci framebuffer_release(info); 12348c2ecf20Sopenharmony_ci ps3_system_bus_set_drvdata(dev, NULL); 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci iounmap((u8 __force __iomem *)ps3fb.dinfo); 12378c2ecf20Sopenharmony_ci lv1_gpu_fb_close(ps3fb.context_handle); 12388c2ecf20Sopenharmony_ci lv1_gpu_context_iomap(ps3fb.context_handle, GPU_IOIF, xdr_lpar, 12398c2ecf20Sopenharmony_ci ps3fb_videomemory.size, CBE_IOPTE_M); 12408c2ecf20Sopenharmony_ci lv1_gpu_context_free(ps3fb.context_handle); 12418c2ecf20Sopenharmony_ci lv1_gpu_memory_free(ps3fb.memory_handle); 12428c2ecf20Sopenharmony_ci ps3_close_hv_device(dev); 12438c2ecf20Sopenharmony_ci dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci return 0; 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic struct ps3_system_bus_driver ps3fb_driver = { 12498c2ecf20Sopenharmony_ci .match_id = PS3_MATCH_ID_GPU, 12508c2ecf20Sopenharmony_ci .match_sub_id = PS3_MATCH_SUB_ID_GPU_FB, 12518c2ecf20Sopenharmony_ci .core.name = DEVICE_NAME, 12528c2ecf20Sopenharmony_ci .core.owner = THIS_MODULE, 12538c2ecf20Sopenharmony_ci .probe = ps3fb_probe, 12548c2ecf20Sopenharmony_ci .remove = ps3fb_shutdown, 12558c2ecf20Sopenharmony_ci .shutdown = ps3fb_shutdown, 12568c2ecf20Sopenharmony_ci}; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic int __init ps3fb_setup(void) 12598c2ecf20Sopenharmony_ci{ 12608c2ecf20Sopenharmony_ci char *options; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci#ifdef MODULE 12638c2ecf20Sopenharmony_ci return 0; 12648c2ecf20Sopenharmony_ci#endif 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci if (fb_get_options(DEVICE_NAME, &options)) 12678c2ecf20Sopenharmony_ci return -ENXIO; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (!options || !*options) 12708c2ecf20Sopenharmony_ci return 0; 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci while (1) { 12738c2ecf20Sopenharmony_ci char *this_opt = strsep(&options, ","); 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if (!this_opt) 12768c2ecf20Sopenharmony_ci break; 12778c2ecf20Sopenharmony_ci if (!*this_opt) 12788c2ecf20Sopenharmony_ci continue; 12798c2ecf20Sopenharmony_ci if (!strncmp(this_opt, "mode:", 5)) 12808c2ecf20Sopenharmony_ci ps3fb_mode = simple_strtoul(this_opt + 5, NULL, 0); 12818c2ecf20Sopenharmony_ci else 12828c2ecf20Sopenharmony_ci mode_option = this_opt; 12838c2ecf20Sopenharmony_ci } 12848c2ecf20Sopenharmony_ci return 0; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int __init ps3fb_init(void) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci if (!ps3fb_videomemory.address || ps3fb_setup()) 12908c2ecf20Sopenharmony_ci return -ENXIO; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci return ps3_system_bus_driver_register(&ps3fb_driver); 12938c2ecf20Sopenharmony_ci} 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_cistatic void __exit ps3fb_exit(void) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci pr_debug(" -> %s:%d\n", __func__, __LINE__); 12988c2ecf20Sopenharmony_ci ps3_system_bus_driver_unregister(&ps3fb_driver); 12998c2ecf20Sopenharmony_ci pr_debug(" <- %s:%d\n", __func__, __LINE__); 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cimodule_init(ps3fb_init); 13038c2ecf20Sopenharmony_cimodule_exit(ps3fb_exit); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 13068c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PS3 GPU Frame Buffer Driver"); 13078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sony Computer Entertainment Inc."); 13088c2ecf20Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_GPU_FB); 1309