162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/video/mmp/fb/mmpfb.c 462306a36Sopenharmony_ci * Framebuffer driver for Marvell Display controller. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2012 Marvell Technology Group Ltd. 762306a36Sopenharmony_ci * Authors: Zhou Zhu <zzhu3@marvell.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include "mmpfb.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int var_to_pixfmt(struct fb_var_screeninfo *var) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci /* 1762306a36Sopenharmony_ci * Pseudocolor mode? 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci if (var->bits_per_pixel == 8) 2062306a36Sopenharmony_ci return PIXFMT_PSEUDOCOLOR; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci /* 2362306a36Sopenharmony_ci * Check for YUV422PLANAR. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length == 8 && 2662306a36Sopenharmony_ci var->green.length == 4 && var->blue.length == 4) { 2762306a36Sopenharmony_ci if (var->green.offset >= var->blue.offset) 2862306a36Sopenharmony_ci return PIXFMT_YUV422P; 2962306a36Sopenharmony_ci else 3062306a36Sopenharmony_ci return PIXFMT_YVU422P; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci /* 3462306a36Sopenharmony_ci * Check for YUV420PLANAR. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci if (var->bits_per_pixel == 12 && var->red.length == 8 && 3762306a36Sopenharmony_ci var->green.length == 2 && var->blue.length == 2) { 3862306a36Sopenharmony_ci if (var->green.offset >= var->blue.offset) 3962306a36Sopenharmony_ci return PIXFMT_YUV420P; 4062306a36Sopenharmony_ci else 4162306a36Sopenharmony_ci return PIXFMT_YVU420P; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * Check for YUV422PACK. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length == 16 && 4862306a36Sopenharmony_ci var->green.length == 16 && var->blue.length == 16) { 4962306a36Sopenharmony_ci if (var->red.offset == 0) 5062306a36Sopenharmony_ci return PIXFMT_YUYV; 5162306a36Sopenharmony_ci else if (var->green.offset >= var->blue.offset) 5262306a36Sopenharmony_ci return PIXFMT_UYVY; 5362306a36Sopenharmony_ci else 5462306a36Sopenharmony_ci return PIXFMT_VYUY; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* 5862306a36Sopenharmony_ci * Check for 565/1555. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci if (var->bits_per_pixel == 16 && var->red.length <= 5 && 6162306a36Sopenharmony_ci var->green.length <= 6 && var->blue.length <= 5) { 6262306a36Sopenharmony_ci if (var->transp.length == 0) { 6362306a36Sopenharmony_ci if (var->red.offset >= var->blue.offset) 6462306a36Sopenharmony_ci return PIXFMT_RGB565; 6562306a36Sopenharmony_ci else 6662306a36Sopenharmony_ci return PIXFMT_BGR565; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * Check for 888/A888. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_ci if (var->bits_per_pixel <= 32 && var->red.length <= 8 && 7462306a36Sopenharmony_ci var->green.length <= 8 && var->blue.length <= 8) { 7562306a36Sopenharmony_ci if (var->bits_per_pixel == 24 && var->transp.length == 0) { 7662306a36Sopenharmony_ci if (var->red.offset >= var->blue.offset) 7762306a36Sopenharmony_ci return PIXFMT_RGB888PACK; 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci return PIXFMT_BGR888PACK; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (var->bits_per_pixel == 32 && var->transp.offset == 24) { 8362306a36Sopenharmony_ci if (var->red.offset >= var->blue.offset) 8462306a36Sopenharmony_ci return PIXFMT_RGBA888; 8562306a36Sopenharmony_ci else 8662306a36Sopenharmony_ci return PIXFMT_BGRA888; 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci if (var->red.offset >= var->blue.offset) 8962306a36Sopenharmony_ci return PIXFMT_RGB888UNPACK; 9062306a36Sopenharmony_ci else 9162306a36Sopenharmony_ci return PIXFMT_BGR888UNPACK; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return -EINVAL; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic void pixfmt_to_var(struct fb_var_screeninfo *var, int pix_fmt) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci switch (pix_fmt) { 10162306a36Sopenharmony_ci case PIXFMT_RGB565: 10262306a36Sopenharmony_ci var->bits_per_pixel = 16; 10362306a36Sopenharmony_ci var->red.offset = 11; var->red.length = 5; 10462306a36Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 10562306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 5; 10662306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 10762306a36Sopenharmony_ci break; 10862306a36Sopenharmony_ci case PIXFMT_BGR565: 10962306a36Sopenharmony_ci var->bits_per_pixel = 16; 11062306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 5; 11162306a36Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 11262306a36Sopenharmony_ci var->blue.offset = 11; var->blue.length = 5; 11362306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci case PIXFMT_RGB888UNPACK: 11662306a36Sopenharmony_ci var->bits_per_pixel = 32; 11762306a36Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 11862306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 11962306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 12062306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci case PIXFMT_BGR888UNPACK: 12362306a36Sopenharmony_ci var->bits_per_pixel = 32; 12462306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 12562306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 12662306a36Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 12762306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 12862306a36Sopenharmony_ci break; 12962306a36Sopenharmony_ci case PIXFMT_RGBA888: 13062306a36Sopenharmony_ci var->bits_per_pixel = 32; 13162306a36Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 13262306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 13362306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 13462306a36Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci case PIXFMT_BGRA888: 13762306a36Sopenharmony_ci var->bits_per_pixel = 32; 13862306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 13962306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 14062306a36Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 14162306a36Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 14262306a36Sopenharmony_ci break; 14362306a36Sopenharmony_ci case PIXFMT_RGB888PACK: 14462306a36Sopenharmony_ci var->bits_per_pixel = 24; 14562306a36Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 14662306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 14762306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 14862306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 14962306a36Sopenharmony_ci break; 15062306a36Sopenharmony_ci case PIXFMT_BGR888PACK: 15162306a36Sopenharmony_ci var->bits_per_pixel = 24; 15262306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 15362306a36Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 15462306a36Sopenharmony_ci var->blue.offset = 16; var->blue.length = 8; 15562306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 15662306a36Sopenharmony_ci break; 15762306a36Sopenharmony_ci case PIXFMT_YUV420P: 15862306a36Sopenharmony_ci var->bits_per_pixel = 12; 15962306a36Sopenharmony_ci var->red.offset = 4; var->red.length = 8; 16062306a36Sopenharmony_ci var->green.offset = 2; var->green.length = 2; 16162306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 2; 16262306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 16362306a36Sopenharmony_ci break; 16462306a36Sopenharmony_ci case PIXFMT_YVU420P: 16562306a36Sopenharmony_ci var->bits_per_pixel = 12; 16662306a36Sopenharmony_ci var->red.offset = 4; var->red.length = 8; 16762306a36Sopenharmony_ci var->green.offset = 0; var->green.length = 2; 16862306a36Sopenharmony_ci var->blue.offset = 2; var->blue.length = 2; 16962306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case PIXFMT_YUV422P: 17262306a36Sopenharmony_ci var->bits_per_pixel = 16; 17362306a36Sopenharmony_ci var->red.offset = 8; var->red.length = 8; 17462306a36Sopenharmony_ci var->green.offset = 4; var->green.length = 4; 17562306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 4; 17662306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 17762306a36Sopenharmony_ci break; 17862306a36Sopenharmony_ci case PIXFMT_YVU422P: 17962306a36Sopenharmony_ci var->bits_per_pixel = 16; 18062306a36Sopenharmony_ci var->red.offset = 8; var->red.length = 8; 18162306a36Sopenharmony_ci var->green.offset = 0; var->green.length = 4; 18262306a36Sopenharmony_ci var->blue.offset = 4; var->blue.length = 4; 18362306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case PIXFMT_UYVY: 18662306a36Sopenharmony_ci var->bits_per_pixel = 16; 18762306a36Sopenharmony_ci var->red.offset = 8; var->red.length = 16; 18862306a36Sopenharmony_ci var->green.offset = 4; var->green.length = 16; 18962306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 16; 19062306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci case PIXFMT_VYUY: 19362306a36Sopenharmony_ci var->bits_per_pixel = 16; 19462306a36Sopenharmony_ci var->red.offset = 8; var->red.length = 16; 19562306a36Sopenharmony_ci var->green.offset = 0; var->green.length = 16; 19662306a36Sopenharmony_ci var->blue.offset = 4; var->blue.length = 16; 19762306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case PIXFMT_YUYV: 20062306a36Sopenharmony_ci var->bits_per_pixel = 16; 20162306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 16; 20262306a36Sopenharmony_ci var->green.offset = 4; var->green.length = 16; 20362306a36Sopenharmony_ci var->blue.offset = 8; var->blue.length = 16; 20462306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 20562306a36Sopenharmony_ci break; 20662306a36Sopenharmony_ci case PIXFMT_PSEUDOCOLOR: 20762306a36Sopenharmony_ci var->bits_per_pixel = 8; 20862306a36Sopenharmony_ci var->red.offset = 0; var->red.length = 8; 20962306a36Sopenharmony_ci var->green.offset = 0; var->green.length = 8; 21062306a36Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 21162306a36Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * fb framework has its limitation: 21862306a36Sopenharmony_ci * 1. input color/output color is not seprated 21962306a36Sopenharmony_ci * 2. fb_videomode not include output color 22062306a36Sopenharmony_ci * so for fb usage, we keep a output format which is not changed 22162306a36Sopenharmony_ci * then it's added for mmpmode 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_cistatic void fbmode_to_mmpmode(struct mmp_mode *mode, 22462306a36Sopenharmony_ci struct fb_videomode *videomode, int output_fmt) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci u64 div_result = 1000000000000ll; 22762306a36Sopenharmony_ci mode->name = videomode->name; 22862306a36Sopenharmony_ci mode->refresh = videomode->refresh; 22962306a36Sopenharmony_ci mode->xres = videomode->xres; 23062306a36Sopenharmony_ci mode->yres = videomode->yres; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci do_div(div_result, videomode->pixclock); 23362306a36Sopenharmony_ci mode->pixclock_freq = (u32)div_result; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci mode->left_margin = videomode->left_margin; 23662306a36Sopenharmony_ci mode->right_margin = videomode->right_margin; 23762306a36Sopenharmony_ci mode->upper_margin = videomode->upper_margin; 23862306a36Sopenharmony_ci mode->lower_margin = videomode->lower_margin; 23962306a36Sopenharmony_ci mode->hsync_len = videomode->hsync_len; 24062306a36Sopenharmony_ci mode->vsync_len = videomode->vsync_len; 24162306a36Sopenharmony_ci mode->hsync_invert = !!(videomode->sync & FB_SYNC_HOR_HIGH_ACT); 24262306a36Sopenharmony_ci mode->vsync_invert = !!(videomode->sync & FB_SYNC_VERT_HIGH_ACT); 24362306a36Sopenharmony_ci /* no defined flag in fb, use vmode>>3*/ 24462306a36Sopenharmony_ci mode->invert_pixclock = !!(videomode->vmode & 8); 24562306a36Sopenharmony_ci mode->pix_fmt_out = output_fmt; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void mmpmode_to_fbmode(struct fb_videomode *videomode, 24962306a36Sopenharmony_ci struct mmp_mode *mode) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci u64 div_result = 1000000000000ll; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci videomode->name = mode->name; 25462306a36Sopenharmony_ci videomode->refresh = mode->refresh; 25562306a36Sopenharmony_ci videomode->xres = mode->xres; 25662306a36Sopenharmony_ci videomode->yres = mode->yres; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci do_div(div_result, mode->pixclock_freq); 25962306a36Sopenharmony_ci videomode->pixclock = (u32)div_result; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci videomode->left_margin = mode->left_margin; 26262306a36Sopenharmony_ci videomode->right_margin = mode->right_margin; 26362306a36Sopenharmony_ci videomode->upper_margin = mode->upper_margin; 26462306a36Sopenharmony_ci videomode->lower_margin = mode->lower_margin; 26562306a36Sopenharmony_ci videomode->hsync_len = mode->hsync_len; 26662306a36Sopenharmony_ci videomode->vsync_len = mode->vsync_len; 26762306a36Sopenharmony_ci videomode->sync = (mode->hsync_invert ? FB_SYNC_HOR_HIGH_ACT : 0) 26862306a36Sopenharmony_ci | (mode->vsync_invert ? FB_SYNC_VERT_HIGH_ACT : 0); 26962306a36Sopenharmony_ci videomode->vmode = mode->invert_pixclock ? 8 : 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic int mmpfb_check_var(struct fb_var_screeninfo *var, 27362306a36Sopenharmony_ci struct fb_info *info) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (var->bits_per_pixel == 8) 27862306a36Sopenharmony_ci return -EINVAL; 27962306a36Sopenharmony_ci /* 28062306a36Sopenharmony_ci * Basic geometry sanity checks. 28162306a36Sopenharmony_ci */ 28262306a36Sopenharmony_ci if (var->xoffset + var->xres > var->xres_virtual) 28362306a36Sopenharmony_ci return -EINVAL; 28462306a36Sopenharmony_ci if (var->yoffset + var->yres > var->yres_virtual) 28562306a36Sopenharmony_ci return -EINVAL; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * Check size of framebuffer. 28962306a36Sopenharmony_ci */ 29062306a36Sopenharmony_ci if (var->xres_virtual * var->yres_virtual * 29162306a36Sopenharmony_ci (var->bits_per_pixel >> 3) > fbi->fb_size) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci return ((chan & 0xffff) >> (16 - bf->length)) << bf->offset; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic u32 to_rgb(u16 red, u16 green, u16 blue) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci red >>= 8; 30562306a36Sopenharmony_ci green >>= 8; 30662306a36Sopenharmony_ci blue >>= 8; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return (red << 16) | (green << 8) | blue; 30962306a36Sopenharmony_ci} 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic int mmpfb_setcolreg(unsigned int regno, unsigned int red, 31262306a36Sopenharmony_ci unsigned int green, unsigned int blue, 31362306a36Sopenharmony_ci unsigned int trans, struct fb_info *info) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 31662306a36Sopenharmony_ci u32 val; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR && regno < 16) { 31962306a36Sopenharmony_ci val = chan_to_field(red, &info->var.red); 32062306a36Sopenharmony_ci val |= chan_to_field(green, &info->var.green); 32162306a36Sopenharmony_ci val |= chan_to_field(blue , &info->var.blue); 32262306a36Sopenharmony_ci fbi->pseudo_palette[regno] = val; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_PSEUDOCOLOR && regno < 256) { 32662306a36Sopenharmony_ci val = to_rgb(red, green, blue); 32762306a36Sopenharmony_ci /* TODO */ 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci return 0; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic int mmpfb_pan_display(struct fb_var_screeninfo *var, 33462306a36Sopenharmony_ci struct fb_info *info) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 33762306a36Sopenharmony_ci struct mmp_addr addr; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 34062306a36Sopenharmony_ci addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) 34162306a36Sopenharmony_ci * var->bits_per_pixel / 8 + fbi->fb_start_dma; 34262306a36Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return 0; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic int var_update(struct fb_info *info) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 35062306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 35162306a36Sopenharmony_ci struct fb_videomode *m; 35262306a36Sopenharmony_ci int pix_fmt; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* set pix_fmt */ 35562306a36Sopenharmony_ci pix_fmt = var_to_pixfmt(var); 35662306a36Sopenharmony_ci if (pix_fmt < 0) 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci pixfmt_to_var(var, pix_fmt); 35962306a36Sopenharmony_ci fbi->pix_fmt = pix_fmt; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci /* set var according to best video mode*/ 36262306a36Sopenharmony_ci m = (struct fb_videomode *)fb_match_mode(var, &info->modelist); 36362306a36Sopenharmony_ci if (!m) { 36462306a36Sopenharmony_ci dev_err(fbi->dev, "set par: no match mode, use best mode\n"); 36562306a36Sopenharmony_ci m = (struct fb_videomode *)fb_find_best_mode(var, 36662306a36Sopenharmony_ci &info->modelist); 36762306a36Sopenharmony_ci fb_videomode_to_var(var, m); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci memcpy(&fbi->mode, m, sizeof(struct fb_videomode)); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* fix to 2* yres */ 37262306a36Sopenharmony_ci var->yres_virtual = var->yres * 2; 37362306a36Sopenharmony_ci info->fix.visual = (pix_fmt == PIXFMT_PSEUDOCOLOR) ? 37462306a36Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 37562306a36Sopenharmony_ci info->fix.line_length = var->xres_virtual * var->bits_per_pixel / 8; 37662306a36Sopenharmony_ci info->fix.ypanstep = var->yres; 37762306a36Sopenharmony_ci return 0; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void mmpfb_set_win(struct fb_info *info) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 38362306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 38462306a36Sopenharmony_ci struct mmp_win win; 38562306a36Sopenharmony_ci u32 stride; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci memset(&win, 0, sizeof(win)); 38862306a36Sopenharmony_ci win.xsrc = win.xdst = fbi->mode.xres; 38962306a36Sopenharmony_ci win.ysrc = win.ydst = fbi->mode.yres; 39062306a36Sopenharmony_ci win.pix_fmt = fbi->pix_fmt; 39162306a36Sopenharmony_ci stride = pixfmt_to_stride(win.pix_fmt); 39262306a36Sopenharmony_ci win.pitch[0] = var->xres_virtual * stride; 39362306a36Sopenharmony_ci win.pitch[1] = win.pitch[2] = 39462306a36Sopenharmony_ci (stride == 1) ? (var->xres_virtual >> 1) : 0; 39562306a36Sopenharmony_ci mmp_overlay_set_win(fbi->overlay, &win); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int mmpfb_set_par(struct fb_info *info) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 40162306a36Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 40262306a36Sopenharmony_ci struct mmp_addr addr; 40362306a36Sopenharmony_ci struct mmp_mode mode; 40462306a36Sopenharmony_ci int ret; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ret = var_update(info); 40762306a36Sopenharmony_ci if (ret != 0) 40862306a36Sopenharmony_ci return ret; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* set window/path according to new videomode */ 41162306a36Sopenharmony_ci fbmode_to_mmpmode(&mode, &fbi->mode, fbi->output_fmt); 41262306a36Sopenharmony_ci mmp_path_set_mode(fbi->path, &mode); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* set window related info */ 41562306a36Sopenharmony_ci mmpfb_set_win(info); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* set address always */ 41862306a36Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 41962306a36Sopenharmony_ci addr.phys[0] = (var->yoffset * var->xres_virtual + var->xoffset) 42062306a36Sopenharmony_ci * var->bits_per_pixel / 8 + fbi->fb_start_dma; 42162306a36Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void mmpfb_power(struct mmpfb_info *fbi, int power) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct mmp_addr addr; 42962306a36Sopenharmony_ci struct fb_var_screeninfo *var = &fbi->fb_info->var; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* for power on, always set address/window again */ 43262306a36Sopenharmony_ci if (power) { 43362306a36Sopenharmony_ci /* set window related info */ 43462306a36Sopenharmony_ci mmpfb_set_win(fbi->fb_info); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* set address always */ 43762306a36Sopenharmony_ci memset(&addr, 0, sizeof(addr)); 43862306a36Sopenharmony_ci addr.phys[0] = fbi->fb_start_dma + 43962306a36Sopenharmony_ci (var->yoffset * var->xres_virtual + var->xoffset) 44062306a36Sopenharmony_ci * var->bits_per_pixel / 8; 44162306a36Sopenharmony_ci mmp_overlay_set_addr(fbi->overlay, &addr); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci mmp_overlay_set_onoff(fbi->overlay, power); 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int mmpfb_blank(int blank, struct fb_info *info) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct mmpfb_info *fbi = info->par; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci mmpfb_power(fbi, (blank == FB_BLANK_UNBLANK)); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic const struct fb_ops mmpfb_ops = { 45662306a36Sopenharmony_ci .owner = THIS_MODULE, 45762306a36Sopenharmony_ci FB_DEFAULT_IOMEM_OPS, 45862306a36Sopenharmony_ci .fb_blank = mmpfb_blank, 45962306a36Sopenharmony_ci .fb_check_var = mmpfb_check_var, 46062306a36Sopenharmony_ci .fb_set_par = mmpfb_set_par, 46162306a36Sopenharmony_ci .fb_setcolreg = mmpfb_setcolreg, 46262306a36Sopenharmony_ci .fb_pan_display = mmpfb_pan_display, 46362306a36Sopenharmony_ci}; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int modes_setup(struct mmpfb_info *fbi) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci struct fb_videomode *videomodes; 46862306a36Sopenharmony_ci struct mmp_mode *mmp_modes; 46962306a36Sopenharmony_ci struct fb_info *info = fbi->fb_info; 47062306a36Sopenharmony_ci int videomode_num, i; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* get videomodes from path */ 47362306a36Sopenharmony_ci videomode_num = mmp_path_get_modelist(fbi->path, &mmp_modes); 47462306a36Sopenharmony_ci if (!videomode_num) { 47562306a36Sopenharmony_ci dev_warn(fbi->dev, "can't get videomode num\n"); 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci /* put videomode list to info structure */ 47962306a36Sopenharmony_ci videomodes = kcalloc(videomode_num, sizeof(struct fb_videomode), 48062306a36Sopenharmony_ci GFP_KERNEL); 48162306a36Sopenharmony_ci if (!videomodes) 48262306a36Sopenharmony_ci return -ENOMEM; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci for (i = 0; i < videomode_num; i++) 48562306a36Sopenharmony_ci mmpmode_to_fbmode(&videomodes[i], &mmp_modes[i]); 48662306a36Sopenharmony_ci fb_videomode_to_modelist(videomodes, videomode_num, &info->modelist); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* set videomode[0] as default mode */ 48962306a36Sopenharmony_ci memcpy(&fbi->mode, &videomodes[0], sizeof(struct fb_videomode)); 49062306a36Sopenharmony_ci fbi->output_fmt = mmp_modes[0].pix_fmt_out; 49162306a36Sopenharmony_ci fb_videomode_to_var(&info->var, &fbi->mode); 49262306a36Sopenharmony_ci mmp_path_set_mode(fbi->path, &mmp_modes[0]); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci kfree(videomodes); 49562306a36Sopenharmony_ci return videomode_num; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic int fb_info_setup(struct fb_info *info, 49962306a36Sopenharmony_ci struct mmpfb_info *fbi) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci int ret = 0; 50262306a36Sopenharmony_ci /* Initialise static fb parameters.*/ 50362306a36Sopenharmony_ci info->flags = FBINFO_PARTIAL_PAN_OK | 50462306a36Sopenharmony_ci FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; 50562306a36Sopenharmony_ci info->node = -1; 50662306a36Sopenharmony_ci strcpy(info->fix.id, fbi->name); 50762306a36Sopenharmony_ci info->fix.type = FB_TYPE_PACKED_PIXELS; 50862306a36Sopenharmony_ci info->fix.type_aux = 0; 50962306a36Sopenharmony_ci info->fix.xpanstep = 0; 51062306a36Sopenharmony_ci info->fix.ypanstep = info->var.yres; 51162306a36Sopenharmony_ci info->fix.ywrapstep = 0; 51262306a36Sopenharmony_ci info->fix.accel = FB_ACCEL_NONE; 51362306a36Sopenharmony_ci info->fix.smem_start = fbi->fb_start_dma; 51462306a36Sopenharmony_ci info->fix.smem_len = fbi->fb_size; 51562306a36Sopenharmony_ci info->fix.visual = (fbi->pix_fmt == PIXFMT_PSEUDOCOLOR) ? 51662306a36Sopenharmony_ci FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; 51762306a36Sopenharmony_ci info->fix.line_length = info->var.xres_virtual * 51862306a36Sopenharmony_ci info->var.bits_per_pixel / 8; 51962306a36Sopenharmony_ci info->fbops = &mmpfb_ops; 52062306a36Sopenharmony_ci info->pseudo_palette = fbi->pseudo_palette; 52162306a36Sopenharmony_ci info->screen_buffer = fbi->fb_start; 52262306a36Sopenharmony_ci info->screen_size = fbi->fb_size; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* For FB framework: Allocate color map and Register framebuffer*/ 52562306a36Sopenharmony_ci if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) 52662306a36Sopenharmony_ci ret = -ENOMEM; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void fb_info_clear(struct fb_info *info) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci fb_dealloc_cmap(&info->cmap); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int mmpfb_probe(struct platform_device *pdev) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct mmp_buffer_driver_mach_info *mi; 53962306a36Sopenharmony_ci struct fb_info *info; 54062306a36Sopenharmony_ci struct mmpfb_info *fbi; 54162306a36Sopenharmony_ci int ret, modes_num; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci mi = pdev->dev.platform_data; 54462306a36Sopenharmony_ci if (mi == NULL) { 54562306a36Sopenharmony_ci dev_err(&pdev->dev, "no platform data defined\n"); 54662306a36Sopenharmony_ci return -EINVAL; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* initialize fb */ 55062306a36Sopenharmony_ci info = framebuffer_alloc(sizeof(struct mmpfb_info), &pdev->dev); 55162306a36Sopenharmony_ci if (info == NULL) 55262306a36Sopenharmony_ci return -ENOMEM; 55362306a36Sopenharmony_ci fbi = info->par; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* init fb */ 55662306a36Sopenharmony_ci fbi->fb_info = info; 55762306a36Sopenharmony_ci platform_set_drvdata(pdev, fbi); 55862306a36Sopenharmony_ci fbi->dev = &pdev->dev; 55962306a36Sopenharmony_ci fbi->name = mi->name; 56062306a36Sopenharmony_ci fbi->pix_fmt = mi->default_pixfmt; 56162306a36Sopenharmony_ci pixfmt_to_var(&info->var, fbi->pix_fmt); 56262306a36Sopenharmony_ci mutex_init(&fbi->access_ok); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* get display path by name */ 56562306a36Sopenharmony_ci fbi->path = mmp_get_path(mi->path_name); 56662306a36Sopenharmony_ci if (!fbi->path) { 56762306a36Sopenharmony_ci dev_err(&pdev->dev, "can't get the path %s\n", mi->path_name); 56862306a36Sopenharmony_ci ret = -EINVAL; 56962306a36Sopenharmony_ci goto failed_destroy_mutex; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci dev_info(fbi->dev, "path %s get\n", fbi->path->name); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* get overlay */ 57562306a36Sopenharmony_ci fbi->overlay = mmp_path_get_overlay(fbi->path, mi->overlay_id); 57662306a36Sopenharmony_ci if (!fbi->overlay) { 57762306a36Sopenharmony_ci ret = -EINVAL; 57862306a36Sopenharmony_ci goto failed_destroy_mutex; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci /* set fetch used */ 58162306a36Sopenharmony_ci mmp_overlay_set_fetch(fbi->overlay, mi->dmafetch_id); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci modes_num = modes_setup(fbi); 58462306a36Sopenharmony_ci if (modes_num < 0) { 58562306a36Sopenharmony_ci ret = modes_num; 58662306a36Sopenharmony_ci goto failed_destroy_mutex; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* 59062306a36Sopenharmony_ci * if get modes success, means not hotplug panels, use caculated buffer 59162306a36Sopenharmony_ci * or use default size 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci if (modes_num > 0) { 59462306a36Sopenharmony_ci /* fix to 2* yres */ 59562306a36Sopenharmony_ci info->var.yres_virtual = info->var.yres * 2; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Allocate framebuffer memory: size = modes xy *4 */ 59862306a36Sopenharmony_ci fbi->fb_size = info->var.xres_virtual * info->var.yres_virtual 59962306a36Sopenharmony_ci * info->var.bits_per_pixel / 8; 60062306a36Sopenharmony_ci } else { 60162306a36Sopenharmony_ci fbi->fb_size = MMPFB_DEFAULT_SIZE; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci fbi->fb_start = dma_alloc_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), 60562306a36Sopenharmony_ci &fbi->fb_start_dma, GFP_KERNEL); 60662306a36Sopenharmony_ci if (fbi->fb_start == NULL) { 60762306a36Sopenharmony_ci dev_err(&pdev->dev, "can't alloc framebuffer\n"); 60862306a36Sopenharmony_ci ret = -ENOMEM; 60962306a36Sopenharmony_ci goto failed_destroy_mutex; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci dev_info(fbi->dev, "fb %dk allocated\n", fbi->fb_size/1024); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* fb power on */ 61462306a36Sopenharmony_ci if (modes_num > 0) 61562306a36Sopenharmony_ci mmpfb_power(fbi, 1); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci ret = fb_info_setup(info, fbi); 61862306a36Sopenharmony_ci if (ret < 0) 61962306a36Sopenharmony_ci goto failed_free_buff; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ret = register_framebuffer(info); 62262306a36Sopenharmony_ci if (ret < 0) { 62362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register fb: %d\n", ret); 62462306a36Sopenharmony_ci ret = -ENXIO; 62562306a36Sopenharmony_ci goto failed_clear_info; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dev_info(fbi->dev, "loaded to /dev/fb%d <%s>.\n", 62962306a36Sopenharmony_ci info->node, info->fix.id); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci#ifdef CONFIG_LOGO 63262306a36Sopenharmony_ci if (fbi->fb_start) { 63362306a36Sopenharmony_ci fb_prepare_logo(info, 0); 63462306a36Sopenharmony_ci fb_show_logo(info, 0); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci#endif 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_cifailed_clear_info: 64162306a36Sopenharmony_ci fb_info_clear(info); 64262306a36Sopenharmony_cifailed_free_buff: 64362306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, PAGE_ALIGN(fbi->fb_size), fbi->fb_start, 64462306a36Sopenharmony_ci fbi->fb_start_dma); 64562306a36Sopenharmony_cifailed_destroy_mutex: 64662306a36Sopenharmony_ci mutex_destroy(&fbi->access_ok); 64762306a36Sopenharmony_ci dev_err(fbi->dev, "mmp-fb: frame buffer device init failed\n"); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci framebuffer_release(info); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return ret; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic struct platform_driver mmpfb_driver = { 65562306a36Sopenharmony_ci .driver = { 65662306a36Sopenharmony_ci .name = "mmp-fb", 65762306a36Sopenharmony_ci }, 65862306a36Sopenharmony_ci .probe = mmpfb_probe, 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int mmpfb_init(void) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci return platform_driver_register(&mmpfb_driver); 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_cimodule_init(mmpfb_init); 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ciMODULE_AUTHOR("Zhou Zhu <zhou.zhu@marvell.com>"); 66862306a36Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for Marvell displays"); 66962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 670