diff options
Diffstat (limited to 'drivers/video/fsl-diu-fb.c')
-rw-r--r-- | drivers/video/fsl-diu-fb.c | 157 |
1 files changed, 155 insertions, 2 deletions
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c index 41fbd9453c5f..6c278056fc60 100644 --- a/drivers/video/fsl-diu-fb.c +++ b/drivers/video/fsl-diu-fb.c | |||
@@ -375,7 +375,10 @@ struct fsl_diu_data { | |||
375 | struct diu_ad dummy_ad __aligned(8); | 375 | struct diu_ad dummy_ad __aligned(8); |
376 | struct diu_ad ad[NUM_AOIS] __aligned(8); | 376 | struct diu_ad ad[NUM_AOIS] __aligned(8); |
377 | u8 gamma[256 * 3] __aligned(32); | 377 | u8 gamma[256 * 3] __aligned(32); |
378 | u8 cursor[MAX_CURS * MAX_CURS * 2] __aligned(32); | 378 | /* It's easier to parse the cursor data as little-endian */ |
379 | __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32); | ||
380 | /* Blank cursor data -- used to hide the cursor */ | ||
381 | __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32); | ||
379 | uint8_t edid_data[EDID_LENGTH]; | 382 | uint8_t edid_data[EDID_LENGTH]; |
380 | bool has_edid; | 383 | bool has_edid; |
381 | } __aligned(32); | 384 | } __aligned(32); |
@@ -824,7 +827,6 @@ static void update_lcdc(struct fb_info *info) | |||
824 | /* Program DIU registers */ | 827 | /* Program DIU registers */ |
825 | 828 | ||
826 | out_be32(&hw->gamma, DMA_ADDR(data, gamma)); | 829 | out_be32(&hw->gamma, DMA_ADDR(data, gamma)); |
827 | out_be32(&hw->cursor, DMA_ADDR(data, cursor)); | ||
828 | 830 | ||
829 | out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ | 831 | out_be32(&hw->bgnd, 0x007F7F7F); /* Set background to grey */ |
830 | out_be32(&hw->disp_size, (var->yres << 16) | var->xres); | 832 | out_be32(&hw->disp_size, (var->yres << 16) | var->xres); |
@@ -968,6 +970,156 @@ static u32 fsl_diu_get_pixel_format(unsigned int bits_per_pixel) | |||
968 | } | 970 | } |
969 | 971 | ||
970 | /* | 972 | /* |
973 | * Copies a cursor image from user space to the proper place in driver | ||
974 | * memory so that the hardware can display the cursor image. | ||
975 | * | ||
976 | * Cursor data is represented as a sequence of 'width' bits packed into bytes. | ||
977 | * That is, the first 8 bits are in the first byte, the second 8 bits in the | ||
978 | * second byte, and so on. Therefore, the each row of the cursor is (width + | ||
979 | * 7) / 8 bytes of 'data' | ||
980 | * | ||
981 | * The DIU only supports cursors up to 32x32 (MAX_CURS). We reject cursors | ||
982 | * larger than this, so we already know that 'width' <= 32. Therefore, we can | ||
983 | * simplify our code by using a 32-bit big-endian integer ("line") to read in | ||
984 | * a single line of pixels, and only look at the top 'width' bits of that | ||
985 | * integer. | ||
986 | * | ||
987 | * This could result in an unaligned 32-bit read. For example, if the cursor | ||
988 | * is 24x24, then the first three bytes of 'image' contain the pixel data for | ||
989 | * the top line of the cursor. We do a 32-bit read of 'image', but we look | ||
990 | * only at the top 24 bits. Then we increment 'image' by 3 bytes. The next | ||
991 | * read is unaligned. The only problem is that we might read past the end of | ||
992 | * 'image' by 1-3 bytes, but that should not cause any problems. | ||
993 | */ | ||
994 | static void fsl_diu_load_cursor_image(struct fb_info *info, | ||
995 | const void *image, uint16_t bg, uint16_t fg, | ||
996 | unsigned int width, unsigned int height) | ||
997 | { | ||
998 | struct mfb_info *mfbi = info->par; | ||
999 | struct fsl_diu_data *data = mfbi->parent; | ||
1000 | __le16 *cursor = data->cursor; | ||
1001 | __le16 _fg = cpu_to_le16(fg); | ||
1002 | __le16 _bg = cpu_to_le16(bg); | ||
1003 | unsigned int h, w; | ||
1004 | |||
1005 | for (h = 0; h < height; h++) { | ||
1006 | uint32_t mask = 1 << 31; | ||
1007 | uint32_t line = be32_to_cpup(image); | ||
1008 | |||
1009 | for (w = 0; w < width; w++) { | ||
1010 | cursor[w] = (line & mask) ? _fg : _bg; | ||
1011 | mask >>= 1; | ||
1012 | } | ||
1013 | |||
1014 | cursor += MAX_CURS; | ||
1015 | image += DIV_ROUND_UP(width, 8); | ||
1016 | } | ||
1017 | } | ||
1018 | |||
1019 | /* | ||
1020 | * Set a hardware cursor. The image data for the cursor is passed via the | ||
1021 | * fb_cursor object. | ||
1022 | */ | ||
1023 | static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
1024 | { | ||
1025 | struct mfb_info *mfbi = info->par; | ||
1026 | struct fsl_diu_data *data = mfbi->parent; | ||
1027 | struct diu __iomem *hw = data->diu_reg; | ||
1028 | |||
1029 | if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS) | ||
1030 | return -EINVAL; | ||
1031 | |||
1032 | /* The cursor size has changed */ | ||
1033 | if (cursor->set & FB_CUR_SETSIZE) { | ||
1034 | /* | ||
1035 | * The DIU cursor is a fixed size, so when we get this | ||
1036 | * message, instead of resizing the cursor, we just clear | ||
1037 | * all the image data, in expectation of new data. However, | ||
1038 | * in tests this control does not appear to be normally | ||
1039 | * called. | ||
1040 | */ | ||
1041 | memset(data->cursor, 0, sizeof(data->cursor)); | ||
1042 | } | ||
1043 | |||
1044 | /* The cursor position has changed (cursor->image.dx|dy) */ | ||
1045 | if (cursor->set & FB_CUR_SETPOS) { | ||
1046 | uint32_t xx, yy; | ||
1047 | |||
1048 | yy = (cursor->image.dy - info->var.yoffset) & 0x7ff; | ||
1049 | xx = (cursor->image.dx - info->var.xoffset) & 0x7ff; | ||
1050 | |||
1051 | out_be32(&hw->curs_pos, yy << 16 | xx); | ||
1052 | } | ||
1053 | |||
1054 | /* | ||
1055 | * FB_CUR_SETIMAGE - the cursor image has changed | ||
1056 | * FB_CUR_SETCMAP - the cursor colors has changed | ||
1057 | * FB_CUR_SETSHAPE - the cursor bitmask has changed | ||
1058 | */ | ||
1059 | if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) { | ||
1060 | unsigned int image_size = | ||
1061 | DIV_ROUND_UP(cursor->image.width, 8) * cursor->image.height; | ||
1062 | unsigned int image_words = | ||
1063 | DIV_ROUND_UP(image_size, sizeof(uint32_t)); | ||
1064 | unsigned int bg_idx = cursor->image.bg_color; | ||
1065 | unsigned int fg_idx = cursor->image.fg_color; | ||
1066 | uint8_t buffer[image_size]; | ||
1067 | uint32_t *image, *source, *mask; | ||
1068 | uint16_t fg, bg; | ||
1069 | unsigned int i; | ||
1070 | |||
1071 | if (info->state != FBINFO_STATE_RUNNING) | ||
1072 | return 0; | ||
1073 | |||
1074 | /* | ||
1075 | * Determine the size of the cursor image data. Normally, | ||
1076 | * it's 8x16. | ||
1077 | */ | ||
1078 | image_size = DIV_ROUND_UP(cursor->image.width, 8) * | ||
1079 | cursor->image.height; | ||
1080 | |||
1081 | bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) | | ||
1082 | ((info->cmap.green[bg_idx] & 0xf8) << 2) | | ||
1083 | ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | | ||
1084 | 1 << 15; | ||
1085 | |||
1086 | fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) | | ||
1087 | ((info->cmap.green[fg_idx] & 0xf8) << 2) | | ||
1088 | ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | | ||
1089 | 1 << 15; | ||
1090 | |||
1091 | /* Use 32-bit operations on the data to improve performance */ | ||
1092 | image = (uint32_t *)buffer; | ||
1093 | source = (uint32_t *)cursor->image.data; | ||
1094 | mask = (uint32_t *)cursor->mask; | ||
1095 | |||
1096 | if (cursor->rop == ROP_XOR) | ||
1097 | for (i = 0; i < image_words; i++) | ||
1098 | image[i] = source[i] ^ mask[i]; | ||
1099 | else | ||
1100 | for (i = 0; i < image_words; i++) | ||
1101 | image[i] = source[i] & mask[i]; | ||
1102 | |||
1103 | fsl_diu_load_cursor_image(info, image, bg, fg, | ||
1104 | cursor->image.width, cursor->image.height); | ||
1105 | }; | ||
1106 | |||
1107 | /* | ||
1108 | * Show or hide the cursor. The cursor data is always stored in the | ||
1109 | * 'cursor' memory block, and the actual cursor position is always in | ||
1110 | * the DIU's CURS_POS register. To hide the cursor, we redirect the | ||
1111 | * CURSOR register to a blank cursor. The show the cursor, we | ||
1112 | * redirect the CURSOR register to the real cursor data. | ||
1113 | */ | ||
1114 | if (cursor->enable) | ||
1115 | out_be32(&hw->cursor, DMA_ADDR(data, cursor)); | ||
1116 | else | ||
1117 | out_be32(&hw->cursor, DMA_ADDR(data, blank_cursor)); | ||
1118 | |||
1119 | return 0; | ||
1120 | } | ||
1121 | |||
1122 | /* | ||
971 | * Using the fb_var_screeninfo in fb_info we set the resolution of this | 1123 | * Using the fb_var_screeninfo in fb_info we set the resolution of this |
972 | * particular framebuffer. This function alters the fb_fix_screeninfo stored | 1124 | * particular framebuffer. This function alters the fb_fix_screeninfo stored |
973 | * in fb_info. It does not alter var in fb_info since we are using that | 1125 | * in fb_info. It does not alter var in fb_info since we are using that |
@@ -1312,6 +1464,7 @@ static struct fb_ops fsl_diu_ops = { | |||
1312 | .fb_ioctl = fsl_diu_ioctl, | 1464 | .fb_ioctl = fsl_diu_ioctl, |
1313 | .fb_open = fsl_diu_open, | 1465 | .fb_open = fsl_diu_open, |
1314 | .fb_release = fsl_diu_release, | 1466 | .fb_release = fsl_diu_release, |
1467 | .fb_cursor = fsl_diu_cursor, | ||
1315 | }; | 1468 | }; |
1316 | 1469 | ||
1317 | static int install_fb(struct fb_info *info) | 1470 | static int install_fb(struct fb_info *info) |