162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * drivers/mb862xx/mb862xxfb_accel.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver acceleration support 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * (C) 2007 Alexander Shishkin <virtuoso@slind.org> 862306a36Sopenharmony_ci * (C) 2009 Valentin Sitdikov <v.sitdikov@gmail.com> 962306a36Sopenharmony_ci * (C) 2009 Siemens AG 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/fb.h> 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "mb862xxfb.h" 2062306a36Sopenharmony_ci#include "mb862xx_reg.h" 2162306a36Sopenharmony_ci#include "mb862xxfb_accel.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void mb862xxfb_write_fifo(u32 count, u32 *data, struct fb_info *info) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct mb862xxfb_par *par = info->par; 2662306a36Sopenharmony_ci static u32 free; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci u32 total = 0; 2962306a36Sopenharmony_ci while (total < count) { 3062306a36Sopenharmony_ci if (free) { 3162306a36Sopenharmony_ci outreg(geo, GDC_GEO_REG_INPUT_FIFO, data[total]); 3262306a36Sopenharmony_ci total++; 3362306a36Sopenharmony_ci free--; 3462306a36Sopenharmony_ci } else { 3562306a36Sopenharmony_ci free = (u32) inreg(draw, GDC_REG_FIFO_COUNT); 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic void mb86290fb_copyarea(struct fb_info *info, 4162306a36Sopenharmony_ci const struct fb_copyarea *area) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci __u32 cmd[6]; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; 4662306a36Sopenharmony_ci /* Set raster operation */ 4762306a36Sopenharmony_ci cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); 4862306a36Sopenharmony_ci cmd[2] = GDC_TYPE_BLTCOPYP << 24; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (area->sx >= area->dx && area->sy >= area->dy) 5162306a36Sopenharmony_ci cmd[2] |= GDC_CMD_BLTCOPY_TOP_LEFT << 16; 5262306a36Sopenharmony_ci else if (area->sx >= area->dx && area->sy <= area->dy) 5362306a36Sopenharmony_ci cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_LEFT << 16; 5462306a36Sopenharmony_ci else if (area->sx <= area->dx && area->sy >= area->dy) 5562306a36Sopenharmony_ci cmd[2] |= GDC_CMD_BLTCOPY_TOP_RIGHT << 16; 5662306a36Sopenharmony_ci else 5762306a36Sopenharmony_ci cmd[2] |= GDC_CMD_BLTCOPY_BOTTOM_RIGHT << 16; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci cmd[3] = (area->sy << 16) | area->sx; 6062306a36Sopenharmony_ci cmd[4] = (area->dy << 16) | area->dx; 6162306a36Sopenharmony_ci cmd[5] = (area->height << 16) | area->width; 6262306a36Sopenharmony_ci mb862xxfb_write_fifo(6, cmd, info); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * Fill in the cmd array /GDC FIFO commands/ to draw a 1bit image. 6762306a36Sopenharmony_ci * Make sure cmd has enough room! 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_cistatic void mb86290fb_imageblit1(u32 *cmd, u16 step, u16 dx, u16 dy, 7062306a36Sopenharmony_ci u16 width, u16 height, u32 fgcolor, 7162306a36Sopenharmony_ci u32 bgcolor, const struct fb_image *image, 7262306a36Sopenharmony_ci struct fb_info *info) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci int i; 7562306a36Sopenharmony_ci unsigned const char *line; 7662306a36Sopenharmony_ci u16 bytes; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* set colors and raster operation regs */ 7962306a36Sopenharmony_ci cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; 8062306a36Sopenharmony_ci /* Set raster operation */ 8162306a36Sopenharmony_ci cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); 8262306a36Sopenharmony_ci cmd[2] = 8362306a36Sopenharmony_ci (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16); 8462306a36Sopenharmony_ci cmd[3] = fgcolor; 8562306a36Sopenharmony_ci cmd[4] = 8662306a36Sopenharmony_ci (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_BACK_COLOR << 16); 8762306a36Sopenharmony_ci cmd[5] = bgcolor; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci i = 0; 9062306a36Sopenharmony_ci line = image->data; 9162306a36Sopenharmony_ci bytes = (image->width + 7) >> 3; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* and the image */ 9462306a36Sopenharmony_ci cmd[6] = (GDC_TYPE_DRAWBITMAPP << 24) | 9562306a36Sopenharmony_ci (GDC_CMD_BITMAP << 16) | (2 + (step * height)); 9662306a36Sopenharmony_ci cmd[7] = (dy << 16) | dx; 9762306a36Sopenharmony_ci cmd[8] = (height << 16) | width; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci while (i < height) { 10062306a36Sopenharmony_ci memcpy(&cmd[9 + i * step], line, step << 2); 10162306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 10262306a36Sopenharmony_ci { 10362306a36Sopenharmony_ci int k = 0; 10462306a36Sopenharmony_ci for (k = 0; k < step; k++) 10562306a36Sopenharmony_ci cmd[9 + i * step + k] = 10662306a36Sopenharmony_ci cpu_to_be32(cmd[9 + i * step + k]); 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci#endif 10962306a36Sopenharmony_ci line += bytes; 11062306a36Sopenharmony_ci i++; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* 11562306a36Sopenharmony_ci * Fill in the cmd array /GDC FIFO commands/ to draw a 8bit image. 11662306a36Sopenharmony_ci * Make sure cmd has enough room! 11762306a36Sopenharmony_ci */ 11862306a36Sopenharmony_cistatic void mb86290fb_imageblit8(u32 *cmd, u16 step, u16 dx, u16 dy, 11962306a36Sopenharmony_ci u16 width, u16 height, u32 fgcolor, 12062306a36Sopenharmony_ci u32 bgcolor, const struct fb_image *image, 12162306a36Sopenharmony_ci struct fb_info *info) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int i, j; 12462306a36Sopenharmony_ci unsigned const char *line, *ptr; 12562306a36Sopenharmony_ci u16 bytes; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) | 12862306a36Sopenharmony_ci (GDC_CMD_BLT_DRAW << 16) | (2 + (height * step)); 12962306a36Sopenharmony_ci cmd[1] = (dy << 16) | dx; 13062306a36Sopenharmony_ci cmd[2] = (height << 16) | width; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci i = 0; 13362306a36Sopenharmony_ci line = image->data; 13462306a36Sopenharmony_ci bytes = image->width; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci while (i < height) { 13762306a36Sopenharmony_ci ptr = line; 13862306a36Sopenharmony_ci for (j = 0; j < step; j++) { 13962306a36Sopenharmony_ci cmd[3 + i * step + j] = 14062306a36Sopenharmony_ci (((u32 *) (info->pseudo_palette))[*ptr]) & 0xffff; 14162306a36Sopenharmony_ci ptr++; 14262306a36Sopenharmony_ci cmd[3 + i * step + j] |= 14362306a36Sopenharmony_ci ((((u32 *) (info-> 14462306a36Sopenharmony_ci pseudo_palette))[*ptr]) & 0xffff) << 16; 14562306a36Sopenharmony_ci ptr++; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci line += bytes; 14962306a36Sopenharmony_ci i++; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* 15462306a36Sopenharmony_ci * Fill in the cmd array /GDC FIFO commands/ to draw a 16bit image. 15562306a36Sopenharmony_ci * Make sure cmd has enough room! 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cistatic void mb86290fb_imageblit16(u32 *cmd, u16 step, u16 dx, u16 dy, 15862306a36Sopenharmony_ci u16 width, u16 height, u32 fgcolor, 15962306a36Sopenharmony_ci u32 bgcolor, const struct fb_image *image, 16062306a36Sopenharmony_ci struct fb_info *info) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int i; 16362306a36Sopenharmony_ci unsigned const char *line; 16462306a36Sopenharmony_ci u16 bytes; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci i = 0; 16762306a36Sopenharmony_ci line = image->data; 16862306a36Sopenharmony_ci bytes = image->width << 1; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci cmd[0] = (GDC_TYPE_DRAWBITMAPP << 24) | 17162306a36Sopenharmony_ci (GDC_CMD_BLT_DRAW << 16) | (2 + step * height); 17262306a36Sopenharmony_ci cmd[1] = (dy << 16) | dx; 17362306a36Sopenharmony_ci cmd[2] = (height << 16) | width; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci while (i < height) { 17662306a36Sopenharmony_ci memcpy(&cmd[3 + i * step], line, step); 17762306a36Sopenharmony_ci line += bytes; 17862306a36Sopenharmony_ci i++; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic void mb86290fb_imageblit(struct fb_info *info, 18362306a36Sopenharmony_ci const struct fb_image *image) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci u32 *cmd = NULL; 18662306a36Sopenharmony_ci void (*cmdfn) (u32 *, u16, u16, u16, u16, u16, u32, u32, 18762306a36Sopenharmony_ci const struct fb_image *, struct fb_info *) = NULL; 18862306a36Sopenharmony_ci u32 cmdlen; 18962306a36Sopenharmony_ci u32 fgcolor = 0, bgcolor = 0; 19062306a36Sopenharmony_ci u16 step; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci u16 width = image->width, height = image->height; 19362306a36Sopenharmony_ci u16 dx = image->dx, dy = image->dy; 19462306a36Sopenharmony_ci int x2, y2, vxres, vyres; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci x2 = image->dx + image->width; 19762306a36Sopenharmony_ci y2 = image->dy + image->height; 19862306a36Sopenharmony_ci vxres = info->var.xres_virtual; 19962306a36Sopenharmony_ci vyres = info->var.yres_virtual; 20062306a36Sopenharmony_ci x2 = min(x2, vxres); 20162306a36Sopenharmony_ci y2 = min(y2, vyres); 20262306a36Sopenharmony_ci width = x2 - dx; 20362306a36Sopenharmony_ci height = y2 - dy; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci switch (image->depth) { 20662306a36Sopenharmony_ci case 1: 20762306a36Sopenharmony_ci step = (width + 31) >> 5; 20862306a36Sopenharmony_ci cmdlen = 9 + height * step; 20962306a36Sopenharmony_ci cmdfn = mb86290fb_imageblit1; 21062306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR || 21162306a36Sopenharmony_ci info->fix.visual == FB_VISUAL_DIRECTCOLOR) { 21262306a36Sopenharmony_ci fgcolor = 21362306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[image->fg_color]; 21462306a36Sopenharmony_ci bgcolor = 21562306a36Sopenharmony_ci ((u32 *) (info->pseudo_palette))[image->bg_color]; 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci fgcolor = image->fg_color; 21862306a36Sopenharmony_ci bgcolor = image->bg_color; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci case 8: 22462306a36Sopenharmony_ci step = (width + 1) >> 1; 22562306a36Sopenharmony_ci cmdlen = 3 + height * step; 22662306a36Sopenharmony_ci cmdfn = mb86290fb_imageblit8; 22762306a36Sopenharmony_ci break; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci case 16: 23062306a36Sopenharmony_ci step = (width + 1) >> 1; 23162306a36Sopenharmony_ci cmdlen = 3 + height * step; 23262306a36Sopenharmony_ci cmdfn = mb86290fb_imageblit16; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci default: 23662306a36Sopenharmony_ci cfb_imageblit(info, image); 23762306a36Sopenharmony_ci return; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci cmd = kmalloc_array(cmdlen, 4, GFP_DMA); 24162306a36Sopenharmony_ci if (!cmd) 24262306a36Sopenharmony_ci return cfb_imageblit(info, image); 24362306a36Sopenharmony_ci cmdfn(cmd, step, dx, dy, width, height, fgcolor, bgcolor, image, info); 24462306a36Sopenharmony_ci mb862xxfb_write_fifo(cmdlen, cmd, info); 24562306a36Sopenharmony_ci kfree(cmd); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void mb86290fb_fillrect(struct fb_info *info, 24962306a36Sopenharmony_ci const struct fb_fillrect *rect) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci u32 x2, y2, vxres, vyres, height, width, fg; 25362306a36Sopenharmony_ci u32 cmd[7]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci vxres = info->var.xres_virtual; 25662306a36Sopenharmony_ci vyres = info->var.yres_virtual; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci if (!rect->width || !rect->height || rect->dx > vxres 25962306a36Sopenharmony_ci || rect->dy > vyres) 26062306a36Sopenharmony_ci return; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* We could use hardware clipping but on many cards you get around 26362306a36Sopenharmony_ci * hardware clipping by writing to framebuffer directly. */ 26462306a36Sopenharmony_ci x2 = rect->dx + rect->width; 26562306a36Sopenharmony_ci y2 = rect->dy + rect->height; 26662306a36Sopenharmony_ci x2 = min(x2, vxres); 26762306a36Sopenharmony_ci y2 = min(y2, vyres); 26862306a36Sopenharmony_ci width = x2 - rect->dx; 26962306a36Sopenharmony_ci height = y2 - rect->dy; 27062306a36Sopenharmony_ci if (info->fix.visual == FB_VISUAL_TRUECOLOR || 27162306a36Sopenharmony_ci info->fix.visual == FB_VISUAL_DIRECTCOLOR) 27262306a36Sopenharmony_ci fg = ((u32 *) (info->pseudo_palette))[rect->color]; 27362306a36Sopenharmony_ci else 27462306a36Sopenharmony_ci fg = rect->color; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci switch (rect->rop) { 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci case ROP_XOR: 27962306a36Sopenharmony_ci /* Set raster operation */ 28062306a36Sopenharmony_ci cmd[1] = (2 << 7) | (GDC_ROP_XOR << 9); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci case ROP_COPY: 28462306a36Sopenharmony_ci /* Set raster operation */ 28562306a36Sopenharmony_ci cmd[1] = (2 << 7) | (GDC_ROP_COPY << 9); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci cmd[0] = (GDC_TYPE_SETREGISTER << 24) | (1 << 16) | GDC_REG_MODE_BITMAP; 29162306a36Sopenharmony_ci /* cmd[1] set earlier */ 29262306a36Sopenharmony_ci cmd[2] = 29362306a36Sopenharmony_ci (GDC_TYPE_SETCOLORREGISTER << 24) | (GDC_CMD_BODY_FORE_COLOR << 16); 29462306a36Sopenharmony_ci cmd[3] = fg; 29562306a36Sopenharmony_ci cmd[4] = (GDC_TYPE_DRAWRECTP << 24) | (GDC_CMD_BLT_FILL << 16); 29662306a36Sopenharmony_ci cmd[5] = (rect->dy << 16) | (rect->dx); 29762306a36Sopenharmony_ci cmd[6] = (height << 16) | width; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci mb862xxfb_write_fifo(7, cmd, info); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_civoid mb862xxfb_init_accel(struct fb_info *info, struct fb_ops *fbops, int xres) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct mb862xxfb_par *par = info->par; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (info->var.bits_per_pixel == 32) { 30762306a36Sopenharmony_ci fbops->fb_fillrect = cfb_fillrect; 30862306a36Sopenharmony_ci fbops->fb_copyarea = cfb_copyarea; 30962306a36Sopenharmony_ci fbops->fb_imageblit = cfb_imageblit; 31062306a36Sopenharmony_ci } else { 31162306a36Sopenharmony_ci outreg(disp, GC_L0EM, 3); 31262306a36Sopenharmony_ci fbops->fb_fillrect = mb86290fb_fillrect; 31362306a36Sopenharmony_ci fbops->fb_copyarea = mb86290fb_copyarea; 31462306a36Sopenharmony_ci fbops->fb_imageblit = mb86290fb_imageblit; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci outreg(draw, GDC_REG_DRAW_BASE, 0); 31762306a36Sopenharmony_ci outreg(draw, GDC_REG_MODE_MISC, 0x8000); 31862306a36Sopenharmony_ci outreg(draw, GDC_REG_X_RESOLUTION, xres); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci info->flags |= 32162306a36Sopenharmony_ci FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | 32262306a36Sopenharmony_ci FBINFO_HWACCEL_IMAGEBLIT; 32362306a36Sopenharmony_ci info->fix.accel = 0xff; /*FIXME: add right define */ 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 327