18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/video/mmp/fb/mmpfb.c 48c2ecf20Sopenharmony_ci * Framebuffer driver for Marvell Display controller. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd. 78c2ecf20Sopenharmony_ci * Authors: Zhou Zhu <zzhu3@marvell.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci#include "mmpfb.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int var_to_pixfmt(struct fb_var_screeninfo *var) 158c2ecf20Sopenharmony_ci{ 168c2ecf20Sopenharmony_ci /* 178c2ecf20Sopenharmony_ci * Pseudocolor mode? 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 8) 208c2ecf20Sopenharmony_ci return PIXFMT_PSEUDOCOLOR; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci /* 238c2ecf20Sopenharmony_ci * Check for YUV422PLANAR. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length == 8 && 268c2ecf20Sopenharmony_ci var->green.length == 4 && var->blue.length == 4) { 278c2ecf20Sopenharmony_ci if (var->green.offset >= var->blue.offset) 288c2ecf20Sopenharmony_ci return PIXFMT_YUV422P; 298c2ecf20Sopenharmony_ci else 308c2ecf20Sopenharmony_ci return PIXFMT_YVU422P; 318c2ecf20Sopenharmony_ci } 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci /* 348c2ecf20Sopenharmony_ci * Check for YUV420PLANAR. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 12 && var->red.length == 8 && 378c2ecf20Sopenharmony_ci var->green.length == 2 && var->blue.length == 2) { 388c2ecf20Sopenharmony_ci if (var->green.offset >= var->blue.offset) 398c2ecf20Sopenharmony_ci return PIXFMT_YUV420P; 408c2ecf20Sopenharmony_ci else 418c2ecf20Sopenharmony_ci return PIXFMT_YVU420P; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * Check for YUV422PACK. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length == 16 && 488c2ecf20Sopenharmony_ci var->green.length == 16 && var->blue.length == 16) { 498c2ecf20Sopenharmony_ci if (var->red.offset == 0) 508c2ecf20Sopenharmony_ci return PIXFMT_YUYV; 518c2ecf20Sopenharmony_ci else if (var->green.offset >= var->blue.offset) 528c2ecf20Sopenharmony_ci return PIXFMT_UYVY; 538c2ecf20Sopenharmony_ci else 548c2ecf20Sopenharmony_ci return PIXFMT_VYUY; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* 588c2ecf20Sopenharmony_ci * Check for 565/1555. 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length <= 5 && 618c2ecf20Sopenharmony_ci var->green.length <= 6 && var->blue.length <= 5) { 628c2ecf20Sopenharmony_ci if (var->transp.length == 0) { 638c2ecf20Sopenharmony_ci if (var->red.offset >= var->blue.offset) 648c2ecf20Sopenharmony_ci return PIXFMT_RGB565; 658c2ecf20Sopenharmony_ci else 668c2ecf20Sopenharmony_ci return PIXFMT_BGR565; 678c2ecf20Sopenharmony_ci } 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * Check for 888/A888. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci if (var->bits_per_pixel <= 32 && var->red.length <= 8 && 748c2ecf20Sopenharmony_ci var->green.length <= 8 && var->blue.length <= 8) { 758c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 24 && var->transp.length == 0) { 768c2ecf20Sopenharmony_ci if (var->red.offset >= var->blue.offset) 778c2ecf20Sopenharmony_ci return PIXFMT_RGB888PACK; 788c2ecf20Sopenharmony_ci else 798c2ecf20Sopenharmony_ci return PIXFMT_BGR888PACK; 808c2ecf20Sopenharmony_ci } 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 32 && var->transp.offset == 24) { 838c2ecf20Sopenharmony_ci if (var->red.offset >= var->blue.offset) 848c2ecf20Sopenharmony_ci return PIXFMT_RGBA888; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci return PIXFMT_BGRA888; 878c2ecf20Sopenharmony_ci } else { 888c2ecf20Sopenharmony_ci if (var->red.offset >= var->blue.offset) 898c2ecf20Sopenharmony_ci return PIXFMT_RGB888UNPACK; 908c2ecf20Sopenharmony_ci else 918c2ecf20Sopenharmony_ci return PIXFMT_BGR888UNPACK; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci switch (pix_fmt) { 1018c2ecf20Sopenharmony_ci case PIXFMT_RGB565: 1028c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1038c2ecf20Sopenharmony_ci var->red.offset = 11; var->red.length = 5; 1048c2ecf20Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 1058c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 5; 1068c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1078c2ecf20Sopenharmony_ci break; 1088c2ecf20Sopenharmony_ci case PIXFMT_BGR565: 1098c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1108c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 5; 1118c2ecf20Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 1128c2ecf20Sopenharmony_ci var->blue.offset = 11; var->blue.length = 5; 1138c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1148c2ecf20Sopenharmony_ci break; 1158c2ecf20Sopenharmony_ci case PIXFMT_RGB888UNPACK: 1168c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1178c2ecf20Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 1188c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1198c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 1208c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci case PIXFMT_BGR888UNPACK: 1238c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1248c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 1258c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1268c2ecf20Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 1278c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1288c2ecf20Sopenharmony_ci break; 1298c2ecf20Sopenharmony_ci case PIXFMT_RGBA888: 1308c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1318c2ecf20Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 1328c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1338c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 1348c2ecf20Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case PIXFMT_BGRA888: 1378c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 1388c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 1398c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1408c2ecf20Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 1418c2ecf20Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci case PIXFMT_RGB888PACK: 1448c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 1458c2ecf20Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 1468c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1478c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 1488c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1498c2ecf20Sopenharmony_ci break; 1508c2ecf20Sopenharmony_ci case PIXFMT_BGR888PACK: 1518c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 1528c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 1538c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 1548c2ecf20Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 1558c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci case PIXFMT_YUV420P: 1588c2ecf20Sopenharmony_ci var->bits_per_pixel = 12; 1598c2ecf20Sopenharmony_ci var->red.offset = 4; var->red.length = 8; 1608c2ecf20Sopenharmony_ci var->green.offset = 2; var->green.length = 2; 1618c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 2; 1628c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci case PIXFMT_YVU420P: 1658c2ecf20Sopenharmony_ci var->bits_per_pixel = 12; 1668c2ecf20Sopenharmony_ci var->red.offset = 4; var->red.length = 8; 1678c2ecf20Sopenharmony_ci var->green.offset = 0; var->green.length = 2; 1688c2ecf20Sopenharmony_ci var->blue.offset = 2; var->blue.length = 2; 1698c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case PIXFMT_YUV422P: 1728c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1738c2ecf20Sopenharmony_ci var->red.offset = 8; var->red.length = 8; 1748c2ecf20Sopenharmony_ci var->green.offset = 4; var->green.length = 4; 1758c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 4; 1768c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1778c2ecf20Sopenharmony_ci break; 1788c2ecf20Sopenharmony_ci case PIXFMT_YVU422P: 1798c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1808c2ecf20Sopenharmony_ci var->red.offset = 8; var->red.length = 8; 1818c2ecf20Sopenharmony_ci var->green.offset = 0; var->green.length = 4; 1828c2ecf20Sopenharmony_ci var->blue.offset = 4; var->blue.length = 4; 1838c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci case PIXFMT_UYVY: 1868c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1878c2ecf20Sopenharmony_ci var->red.offset = 8; var->red.length = 16; 1888c2ecf20Sopenharmony_ci var->green.offset = 4; var->green.length = 16; 1898c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 16; 1908c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case PIXFMT_VYUY: 1938c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 1948c2ecf20Sopenharmony_ci var->red.offset = 8; var->red.length = 16; 1958c2ecf20Sopenharmony_ci var->green.offset = 0; var->green.length = 16; 1968c2ecf20Sopenharmony_ci var->blue.offset = 4; var->blue.length = 16; 1978c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case PIXFMT_YUYV: 2008c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 2018c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 16; 2028c2ecf20Sopenharmony_ci var->green.offset = 4; var->green.length = 16; 2038c2ecf20Sopenharmony_ci var->blue.offset = 8; var->blue.length = 16; 2048c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci case PIXFMT_PSEUDOCOLOR: 2078c2ecf20Sopenharmony_ci var->bits_per_pixel = 8; 2088c2ecf20Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 2098c2ecf20Sopenharmony_ci var->green.offset = 0; var->green.length = 8; 2108c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 2118c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 2128c2ecf20Sopenharmony_ci break; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * fb framework has its limitation: 2188c2ecf20Sopenharmony_ci * 1. input color/output color is not seprated 2198c2ecf20Sopenharmony_ci * 2. fb_videomode not include output color 2208c2ecf20Sopenharmony_ci * so for fb usage, we keep a output format which is not changed 2218c2ecf20Sopenharmony_ci * then it's added for mmpmode 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_cistatic void fbmode_to_mmpmode(struct mmp_mode *mode, 2248c2ecf20Sopenharmony_ci struct fb_videomode *videomode, int output_fmt) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci u64 div_result = 1000000000000ll; 2278c2ecf20Sopenharmony_ci mode->name = videomode->name; 2288c2ecf20Sopenharmony_ci mode->refresh = videomode->refresh; 2298c2ecf20Sopenharmony_ci mode->xres = videomode->xres; 2308c2ecf20Sopenharmony_ci mode->yres = videomode->yres; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci do_div(div_result, videomode->pixclock); 2338c2ecf20Sopenharmony_ci mode->pixclock_freq = (u32)div_result; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci mode->left_margin = videomode->left_margin; 2368c2ecf20Sopenharmony_ci mode->right_margin = videomode->right_margin; 2378c2ecf20Sopenharmony_ci mode->upper_margin = videomode->upper_margin; 2388c2ecf20Sopenharmony_ci mode->lower_margin = videomode->lower_margin; 2398c2ecf20Sopenharmony_ci mode->hsync_len = videomode->hsync_len; 2408c2ecf20Sopenharmony_ci mode->vsync_len = videomode->vsync_len; 2418c2ecf20Sopenharmony_ci mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT); 2428c2ecf20Sopenharmony_ci mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT); 2438c2ecf20Sopenharmony_ci /* no defined flag in fb, use vmode>>3*/ 2448c2ecf20Sopenharmony_ci mode->invert_pixclock = !!(videomode->vmode & 8); 2458c2ecf20Sopenharmony_ci mode->pix_fmt_out = output_fmt; 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic void mmpmode_to_fbmode(struct fb_videomode *videomode, 2498c2ecf20Sopenharmony_ci struct mmp_mode *mode) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci u64 div_result = 1000000000000ll; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci videomode->name = mode->name; 2548c2ecf20Sopenharmony_ci videomode->refresh = mode->refresh; 2558c2ecf20Sopenharmony_ci videomode->xres = mode->xres; 2568c2ecf20Sopenharmony_ci videomode->yres = mode->yres; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci do_div(div_result, mode->pixclock_freq); 2598c2ecf20Sopenharmony_ci videomode->pixclock = (u32)div_result; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci videomode->left_margin = mode->left_margin; 2628c2ecf20Sopenharmony_ci videomode->right_margin = mode->right_margin; 2638c2ecf20Sopenharmony_ci videomode->upper_margin = mode->upper_margin; 2648c2ecf20Sopenharmony_ci videomode->lower_margin = mode->lower_margin; 2658c2ecf20Sopenharmony_ci videomode->hsync_len = mode->hsync_len; 2668c2ecf20Sopenharmony_ci videomode->vsync_len = mode->vsync_len; 2678c2ecf20Sopenharmony_ci videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0) 2688c2ecf20Sopenharmony_ci | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0); 2698c2ecf20Sopenharmony_ci videomode->vmode = mode->invert_pixclock ? 8 : 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int mmpfb_check_var(struct fb_var_screeninfo *var, 2738c2ecf20Sopenharmony_ci struct fb_info *info) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (var->bits_per_pixel == 8) 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Basic geometry sanity checks. 2818c2ecf20Sopenharmony_ci */ 2828c2ecf20Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual) 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci if (var->yoffset + var->yres > var->yres_virtual) 2858c2ecf20Sopenharmony_ci return -EINVAL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* 2888c2ecf20Sopenharmony_ci * Check size of framebuffer. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci if (var->xres_virtual * var->yres_virtual * 2918c2ecf20Sopenharmony_ci (var->bits_per_pixel >> 3) > fbi->fb_size) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic u32 to_rgb(u16 red, u16 green, u16 blue) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci red >>= 8; 3058c2ecf20Sopenharmony_ci green >>= 8; 3068c2ecf20Sopenharmony_ci blue >>= 8; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return (red << 16) | (green << 8) | blue; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int mmpfb_setcolreg(unsigned int regno, unsigned int red, 3128c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 3138c2ecf20Sopenharmony_ci unsigned int trans, struct fb_info *info) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 3168c2ecf20Sopenharmony_ci u32 val; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) { 3198c2ecf20Sopenharmony_ci val = chan_to_field(red, &info->var.red); 3208c2ecf20Sopenharmony_ci val |= chan_to_field(green, &info->var.green); 3218c2ecf20Sopenharmony_ci val |= chan_to_field(blue , &info->var.blue); 3228c2ecf20Sopenharmony_ci fbi->pseudo_palette[regno] = val; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { 3268c2ecf20Sopenharmony_ci val = to_rgb(red, green, blue); 3278c2ecf20Sopenharmony_ci /* TODO */ 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int mmpfb_pan_display(struct fb_var_screeninfo *var, 3348c2ecf20Sopenharmony_ci struct fb_info *info) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 3378c2ecf20Sopenharmony_ci struct mmp_addr addr; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 3408c2ecf20Sopenharmony_ci addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) 3418c2ecf20Sopenharmony_ci * var->bits_per_pixel / 8 + fbi->fb_start_dma; 3428c2ecf20Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic int var_update(struct fb_info *info) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 3508c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 3518c2ecf20Sopenharmony_ci struct fb_videomode *m; 3528c2ecf20Sopenharmony_ci int pix_fmt; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* set pix_fmt */ 3558c2ecf20Sopenharmony_ci pix_fmt = var_to_pixfmt(var); 3568c2ecf20Sopenharmony_ci if (pix_fmt < 0) 3578c2ecf20Sopenharmony_ci return -EINVAL; 3588c2ecf20Sopenharmony_ci pixfmt_to_var(var, pix_fmt); 3598c2ecf20Sopenharmony_ci fbi->pix_fmt = pix_fmt; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* set var according to best video mode*/ 3628c2ecf20Sopenharmony_ci m = (struct fb_videomode *)fb_match_mode(var, &info->modelist); 3638c2ecf20Sopenharmony_ci if (!m) { 3648c2ecf20Sopenharmony_ci dev_err(fbi->dev, "set par: no match mode, use best mode\n"); 3658c2ecf20Sopenharmony_ci m = (struct fb_videomode *)fb_find_best_mode(var, 3668c2ecf20Sopenharmony_ci &info->modelist); 3678c2ecf20Sopenharmony_ci fb_videomode_to_var(var, m); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci memcpy(&fbi->mode, m, sizeof(struct fb_videomode)); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* fix to 2* yres */ 3728c2ecf20Sopenharmony_ci var->yres_virtual = var->yres * 2; 3738c2ecf20Sopenharmony_ci info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ? 3748c2ecf20Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 3758c2ecf20Sopenharmony_ci info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; 3768c2ecf20Sopenharmony_ci info->fix.ypanstep = var->yres; 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void mmpfb_set_win(struct fb_info *info) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 3838c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 3848c2ecf20Sopenharmony_ci struct mmp_win win; 3858c2ecf20Sopenharmony_ci u32 stride; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci memset(&win, 0, sizeof(win)); 3888c2ecf20Sopenharmony_ci win.xsrc = win.xdst = fbi->mode.xres; 3898c2ecf20Sopenharmony_ci win.ysrc = win.ydst = fbi->mode.yres; 3908c2ecf20Sopenharmony_ci win.pix_fmt = fbi->pix_fmt; 3918c2ecf20Sopenharmony_ci stride = pixfmt_to_stride(win.pix_fmt); 3928c2ecf20Sopenharmony_ci win.pitch[0] = var->xres_virtual * stride; 3938c2ecf20Sopenharmony_ci win.pitch[1] = win.pitch[2] = 3948c2ecf20Sopenharmony_ci (stride == 1) ? (var->xres_virtual >> 1) : 0; 3958c2ecf20Sopenharmony_ci mmp_overlay_set_win(fbi->overlay, &win); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_cistatic int mmpfb_set_par(struct fb_info *info) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 4018c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 4028c2ecf20Sopenharmony_ci struct mmp_addr addr; 4038c2ecf20Sopenharmony_ci struct mmp_mode mode; 4048c2ecf20Sopenharmony_ci int ret; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ret = var_update(info); 4078c2ecf20Sopenharmony_ci if (ret != 0) 4088c2ecf20Sopenharmony_ci return ret; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* set window/path according to new videomode */ 4118c2ecf20Sopenharmony_ci fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt); 4128c2ecf20Sopenharmony_ci mmp_path_set_mode(fbi->path, &mode); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* set window related info */ 4158c2ecf20Sopenharmony_ci mmpfb_set_win(info); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* set address always */ 4188c2ecf20Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 4198c2ecf20Sopenharmony_ci addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) 4208c2ecf20Sopenharmony_ci * var->bits_per_pixel / 8 + fbi->fb_start_dma; 4218c2ecf20Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void mmpfb_power(struct mmpfb_info *fbi, int power) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct mmp_addr addr; 4298c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->fb_info->var; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* for power on, always set address/window again */ 4328c2ecf20Sopenharmony_ci if (power) { 4338c2ecf20Sopenharmony_ci /* set window related info */ 4348c2ecf20Sopenharmony_ci mmpfb_set_win(fbi->fb_info); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* set address always */ 4378c2ecf20Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 4388c2ecf20Sopenharmony_ci addr.phys[0] = fbi->fb_start_dma + 4398c2ecf20Sopenharmony_ci (var->yoffset * var->xres_virtual + var->xoffset) 4408c2ecf20Sopenharmony_ci * var->bits_per_pixel / 8; 4418c2ecf20Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci mmp_overlay_set_onoff(fbi->overlay, power); 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int mmpfb_blank(int blank, struct fb_info *info) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci struct mmpfb_info *fbi = info->par; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK)); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic const struct fb_ops mmpfb_ops = { 4568c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4578c2ecf20Sopenharmony_ci .fb_blank = mmpfb_blank, 4588c2ecf20Sopenharmony_ci .fb_check_var = mmpfb_check_var, 4598c2ecf20Sopenharmony_ci .fb_set_par = mmpfb_set_par, 4608c2ecf20Sopenharmony_ci .fb_setcolreg = mmpfb_setcolreg, 4618c2ecf20Sopenharmony_ci .fb_pan_display = mmpfb_pan_display, 4628c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 4638c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 4648c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 4658c2ecf20Sopenharmony_ci}; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int modes_setup(struct mmpfb_info *fbi) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci struct fb_videomode *videomodes; 4708c2ecf20Sopenharmony_ci struct mmp_mode *mmp_modes; 4718c2ecf20Sopenharmony_ci struct fb_info *info = fbi->fb_info; 4728c2ecf20Sopenharmony_ci int videomode_num, i; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* get videomodes from path */ 4758c2ecf20Sopenharmony_ci videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes); 4768c2ecf20Sopenharmony_ci if (!videomode_num) { 4778c2ecf20Sopenharmony_ci dev_warn(fbi->dev, "can't get videomode num\n"); 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci /* put videomode list to info structure */ 4818c2ecf20Sopenharmony_ci videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode), 4828c2ecf20Sopenharmony_ci GFP_KERNEL); 4838c2ecf20Sopenharmony_ci if (!videomodes) 4848c2ecf20Sopenharmony_ci return -ENOMEM; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci for (i = 0; i < videomode_num; i++) 4878c2ecf20Sopenharmony_ci mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]); 4888c2ecf20Sopenharmony_ci fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* set videomode[0] as default mode */ 4918c2ecf20Sopenharmony_ci memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode)); 4928c2ecf20Sopenharmony_ci fbi->output_fmt = mmp_modes[0].pix_fmt_out; 4938c2ecf20Sopenharmony_ci fb_videomode_to_var(&info->var, &fbi->mode); 4948c2ecf20Sopenharmony_ci mmp_path_set_mode(fbi->path, &mmp_modes[0]); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci kfree(videomodes); 4978c2ecf20Sopenharmony_ci return videomode_num; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int fb_info_setup(struct fb_info *info, 5018c2ecf20Sopenharmony_ci struct mmpfb_info *fbi) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci int ret = 0; 5048c2ecf20Sopenharmony_ci /* Initialise static fb parameters.*/ 5058c2ecf20Sopenharmony_ci info->flags = FBINFO_DEFAULT | FBINFO_PARTIAL_PAN_OK | 5068c2ecf20Sopenharmony_ci FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 5078c2ecf20Sopenharmony_ci info->node = -1; 5088c2ecf20Sopenharmony_ci strcpy(info->fix.id, fbi->name); 5098c2ecf20Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 5108c2ecf20Sopenharmony_ci info->fix.type_aux = 0; 5118c2ecf20Sopenharmony_ci info->fix.xpanstep = 0; 5128c2ecf20Sopenharmony_ci info->fix.ypanstep = info->var.yres; 5138c2ecf20Sopenharmony_ci info->fix.ywrapstep = 0; 5148c2ecf20Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 5158c2ecf20Sopenharmony_ci info->fix.smem_start = fbi->fb_start_dma; 5168c2ecf20Sopenharmony_ci info->fix.smem_len = fbi->fb_size; 5178c2ecf20Sopenharmony_ci info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ? 5188c2ecf20Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 5198c2ecf20Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * 5208c2ecf20Sopenharmony_ci info->var.bits_per_pixel / 8; 5218c2ecf20Sopenharmony_ci info->fbops = &mmpfb_ops; 5228c2ecf20Sopenharmony_ci info->pseudo_palette = fbi->pseudo_palette; 5238c2ecf20Sopenharmony_ci info->screen_buffer = fbi->fb_start; 5248c2ecf20Sopenharmony_ci info->screen_size = fbi->fb_size; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* For FB framework: Allocate color map and Register framebuffer*/ 5278c2ecf20Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) 5288c2ecf20Sopenharmony_ci ret = -ENOMEM; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return ret; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void fb_info_clear(struct fb_info *info) 5348c2ecf20Sopenharmony_ci{ 5358c2ecf20Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 5368c2ecf20Sopenharmony_ci} 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_cistatic int mmpfb_probe(struct platform_device *pdev) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct mmp_buffer_driver_mach_info *mi; 5418c2ecf20Sopenharmony_ci struct fb_info *info; 5428c2ecf20Sopenharmony_ci struct mmpfb_info *fbi; 5438c2ecf20Sopenharmony_ci int ret, modes_num; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci mi = pdev->dev.platform_data; 5468c2ecf20Sopenharmony_ci if (mi == NULL) { 5478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no platform data defined\n"); 5488c2ecf20Sopenharmony_ci return -EINVAL; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* initialize fb */ 5528c2ecf20Sopenharmony_ci info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev); 5538c2ecf20Sopenharmony_ci if (info == NULL) 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci fbi = info->par; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* init fb */ 5588c2ecf20Sopenharmony_ci fbi->fb_info = info; 5598c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, fbi); 5608c2ecf20Sopenharmony_ci fbi->dev = &pdev->dev; 5618c2ecf20Sopenharmony_ci fbi->name = mi->name; 5628c2ecf20Sopenharmony_ci fbi->pix_fmt = mi->default_pixfmt; 5638c2ecf20Sopenharmony_ci pixfmt_to_var(&info->var, fbi->pix_fmt); 5648c2ecf20Sopenharmony_ci mutex_init(&fbi->access_ok); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* get display path by name */ 5678c2ecf20Sopenharmony_ci fbi->path = mmp_get_path(mi->path_name); 5688c2ecf20Sopenharmony_ci if (!fbi->path) { 5698c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name); 5708c2ecf20Sopenharmony_ci ret = -EINVAL; 5718c2ecf20Sopenharmony_ci goto failed_destroy_mutex; 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci dev_info(fbi->dev, "path %s get\n", fbi->path->name); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* get overlay */ 5778c2ecf20Sopenharmony_ci fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id); 5788c2ecf20Sopenharmony_ci if (!fbi->overlay) { 5798c2ecf20Sopenharmony_ci ret = -EINVAL; 5808c2ecf20Sopenharmony_ci goto failed_destroy_mutex; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci /* set fetch used */ 5838c2ecf20Sopenharmony_ci mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci modes_num = modes_setup(fbi); 5868c2ecf20Sopenharmony_ci if (modes_num < 0) { 5878c2ecf20Sopenharmony_ci ret = modes_num; 5888c2ecf20Sopenharmony_ci goto failed_destroy_mutex; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* 5928c2ecf20Sopenharmony_ci * if get modes success, means not hotplug panels, use caculated buffer 5938c2ecf20Sopenharmony_ci * or use default size 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_ci if (modes_num > 0) { 5968c2ecf20Sopenharmony_ci /* fix to 2* yres */ 5978c2ecf20Sopenharmony_ci info->var.yres_virtual = info->var.yres * 2; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* Allocate framebuffer memory: size = modes xy *4 */ 6008c2ecf20Sopenharmony_ci fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual 6018c2ecf20Sopenharmony_ci * info->var.bits_per_pixel / 8; 6028c2ecf20Sopenharmony_ci } else { 6038c2ecf20Sopenharmony_ci fbi->fb_size = MMPFB_DEFAULT_SIZE; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), 6078c2ecf20Sopenharmony_ci &fbi->fb_start_dma, GFP_KERNEL); 6088c2ecf20Sopenharmony_ci if (fbi->fb_start == NULL) { 6098c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "can't alloc framebuffer\n"); 6108c2ecf20Sopenharmony_ci ret = -ENOMEM; 6118c2ecf20Sopenharmony_ci goto failed_destroy_mutex; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* fb power on */ 6168c2ecf20Sopenharmony_ci if (modes_num > 0) 6178c2ecf20Sopenharmony_ci mmpfb_power(fbi, 1); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ret = fb_info_setup(info, fbi); 6208c2ecf20Sopenharmony_ci if (ret < 0) 6218c2ecf20Sopenharmony_ci goto failed_free_buff; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ret = register_framebuffer(info); 6248c2ecf20Sopenharmony_ci if (ret < 0) { 6258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to register fb: %d\n", ret); 6268c2ecf20Sopenharmony_ci ret = -ENXIO; 6278c2ecf20Sopenharmony_ci goto failed_clear_info; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n", 6318c2ecf20Sopenharmony_ci info->node, info->fix.id); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci#ifdef CONFIG_LOGO 6348c2ecf20Sopenharmony_ci if (fbi->fb_start) { 6358c2ecf20Sopenharmony_ci fb_prepare_logo(info, 0); 6368c2ecf20Sopenharmony_ci fb_show_logo(info, 0); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci#endif 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci return 0; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_cifailed_clear_info: 6438c2ecf20Sopenharmony_ci fb_info_clear(info); 6448c2ecf20Sopenharmony_cifailed_free_buff: 6458c2ecf20Sopenharmony_ci dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start, 6468c2ecf20Sopenharmony_ci fbi->fb_start_dma); 6478c2ecf20Sopenharmony_cifailed_destroy_mutex: 6488c2ecf20Sopenharmony_ci mutex_destroy(&fbi->access_ok); 6498c2ecf20Sopenharmony_ci dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n"); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci framebuffer_release(info); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci return ret; 6548c2ecf20Sopenharmony_ci} 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic struct platform_driver mmpfb_driver = { 6578c2ecf20Sopenharmony_ci .driver = { 6588c2ecf20Sopenharmony_ci .name = "mmp-fb", 6598c2ecf20Sopenharmony_ci }, 6608c2ecf20Sopenharmony_ci .probe = mmpfb_probe, 6618c2ecf20Sopenharmony_ci}; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_cistatic int mmpfb_init(void) 6648c2ecf20Sopenharmony_ci{ 6658c2ecf20Sopenharmony_ci return platform_driver_register(&mmpfb_driver); 6668c2ecf20Sopenharmony_ci} 6678c2ecf20Sopenharmony_cimodule_init(mmpfb_init); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>"); 6708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Marvell displays"); 6718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 672