gx1fb_core.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*
  2. * drivers/video/geode/gx1fb_core.c
  3. * -- Geode GX1 framebuffer driver
  4. *
  5. * Copyright (C) 2005 Arcom Control Systems Ltd.
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/kernel.h>
  14. #include <linux/errno.h>
  15. #include <linux/string.h>
  16. #include <linux/mm.h>
  17. #include <linux/tty.h>
  18. #include <linux/slab.h>
  19. #include <linux/delay.h>
  20. #include <linux/fb.h>
  21. #include <linux/init.h>
  22. #include <linux/pci.h>
  23. #include "geodefb.h"
  24. #include "display_gx1.h"
  25. #include "video_cs5530.h"
  26. static char mode_option[32] = "640x480-16@60";
  27. static int crt_option = 1;
  28. static char panel_option[32] = "";
  29. static int gx1_line_delta(int xres, int bpp)
  30. {
  31. int line_delta = xres * (bpp >> 3);
  32. if (line_delta > 2048)
  33. line_delta = 4096;
  34. else if (line_delta > 1024)
  35. line_delta = 2048;
  36. else
  37. line_delta = 1024;
  38. return line_delta;
  39. }
  40. static int gx1fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
  41. {
  42. struct geodefb_par *par = info->par;
  43. printk(KERN_DEBUG "%s()\n", __FUNCTION__);
  44. /* Maximum resolution is 1280x1024. */
  45. if (var->xres > 1280 || var->yres > 1024)
  46. return -EINVAL;
  47. if (par->panel_x && (var->xres > par->panel_x || var->yres > par->panel_y))
  48. return -EINVAL;
  49. /* Only 16 bpp and 8 bpp is supported by the hardware. */
  50. if (var->bits_per_pixel == 16) {
  51. var->red.offset = 11; var->red.length = 5;
  52. var->green.offset = 5; var->green.length = 6;
  53. var->blue.offset = 0; var->blue.length = 5;
  54. var->transp.offset = 0; var->transp.length = 0;
  55. } else if (var->bits_per_pixel == 8) {
  56. var->red.offset = 0; var->red.length = 8;
  57. var->green.offset = 0; var->green.length = 8;
  58. var->blue.offset = 0; var->blue.length = 8;
  59. var->transp.offset = 0; var->transp.length = 0;
  60. } else
  61. return -EINVAL;
  62. /* Enough video memory? */
  63. if (gx1_line_delta(var->xres, var->bits_per_pixel) * var->yres > info->fix.smem_len)
  64. return -EINVAL;
  65. /* FIXME: Check timing parameters here? */
  66. return 0;
  67. }
  68. static int gx1fb_set_par(struct fb_info *info)
  69. {
  70. struct geodefb_par *par = info->par;
  71. if (info->var.bits_per_pixel == 16) {
  72. info->fix.visual = FB_VISUAL_TRUECOLOR;
  73. fb_dealloc_cmap(&info->cmap);
  74. } else {
  75. info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
  76. fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
  77. }
  78. info->fix.line_length = gx1_line_delta(info->var.xres, info->var.bits_per_pixel);
  79. par->dc_ops->set_mode(info);
  80. return 0;
  81. }
  82. static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
  83. {
  84. chan &= 0xffff;
  85. chan >>= 16 - bf->length;
  86. return chan << bf->offset;
  87. }
  88. static int gx1fb_setcolreg(unsigned regno, unsigned red, unsigned green,
  89. unsigned blue, unsigned transp,
  90. struct fb_info *info)
  91. {
  92. struct geodefb_par *par = info->par;
  93. if (info->var.grayscale) {
  94. /* grayscale = 0.30*R + 0.59*G + 0.11*B */
  95. red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
  96. }
  97. /* Truecolor has hardware independent palette */
  98. if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
  99. u32 *pal = info->pseudo_palette;
  100. u32 v;
  101. if (regno >= 16)
  102. return -EINVAL;
  103. v = chan_to_field(red, &info->var.red);
  104. v |= chan_to_field(green, &info->var.green);
  105. v |= chan_to_field(blue, &info->var.blue);
  106. pal[regno] = v;
  107. } else {
  108. if (regno >= 256)
  109. return -EINVAL;
  110. par->dc_ops->set_palette_reg(info, regno, red, green, blue);
  111. }
  112. return 0;
  113. }
  114. static int gx1fb_blank(int blank_mode, struct fb_info *info)
  115. {
  116. struct geodefb_par *par = info->par;
  117. return par->vid_ops->blank_display(info, blank_mode);
  118. }
  119. static int __init gx1fb_map_video_memory(struct fb_info *info)
  120. {
  121. struct geodefb_par *par = info->par;
  122. unsigned gx_base;
  123. int fb_len;
  124. gx_base = gx1_gx_base();
  125. if (!gx_base)
  126. return -ENODEV;
  127. par->vid_dev = pci_get_device(PCI_VENDOR_ID_CYRIX,
  128. PCI_DEVICE_ID_CYRIX_5530_VIDEO, NULL);
  129. if (!par->vid_dev)
  130. return -ENODEV;
  131. par->vid_regs = ioremap(pci_resource_start(par->vid_dev, 1),
  132. pci_resource_len(par->vid_dev, 1));
  133. if (!par->vid_regs)
  134. return -ENOMEM;
  135. par->dc_regs = ioremap(gx_base + 0x8300, 0x100);
  136. if (!par->dc_regs)
  137. return -ENOMEM;
  138. info->fix.smem_start = gx_base + 0x800000;
  139. if ((fb_len = gx1_frame_buffer_size()) < 0)
  140. return -ENOMEM;
  141. info->fix.smem_len = fb_len;
  142. info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len);
  143. if (!info->screen_base)
  144. return -ENOMEM;
  145. printk(KERN_INFO "%s: %d Kibyte of video memory at 0x%lx\n",
  146. info->fix.id, info->fix.smem_len / 1024, info->fix.smem_start);
  147. return 0;
  148. }
  149. static int parse_panel_option(struct fb_info *info)
  150. {
  151. struct geodefb_par *par = info->par;
  152. if (strcmp(panel_option, "") != 0) {
  153. int x, y;
  154. char *s;
  155. x = simple_strtol(panel_option, &s, 10);
  156. if (!x)
  157. return -EINVAL;
  158. y = simple_strtol(s + 1, NULL, 10);
  159. if (!y)
  160. return -EINVAL;
  161. par->panel_x = x;
  162. par->panel_y = y;
  163. }
  164. return 0;
  165. }
  166. static struct fb_ops gx1fb_ops = {
  167. .owner = THIS_MODULE,
  168. .fb_check_var = gx1fb_check_var,
  169. .fb_set_par = gx1fb_set_par,
  170. .fb_setcolreg = gx1fb_setcolreg,
  171. .fb_blank = gx1fb_blank,
  172. /* No HW acceleration for now. */
  173. .fb_fillrect = cfb_fillrect,
  174. .fb_copyarea = cfb_copyarea,
  175. .fb_imageblit = cfb_imageblit,
  176. .fb_cursor = soft_cursor,
  177. };
  178. static struct fb_info * __init gx1fb_init_fbinfo(void)
  179. {
  180. struct fb_info *info;
  181. struct geodefb_par *par;
  182. /* Alloc enough space for the pseudo palette. */
  183. info = framebuffer_alloc(sizeof(struct geodefb_par) + sizeof(u32) * 16, NULL);
  184. if (!info)
  185. return NULL;
  186. par = info->par;
  187. strcpy(info->fix.id, "GX1");
  188. info->fix.type = FB_TYPE_PACKED_PIXELS;
  189. info->fix.type_aux = 0;
  190. info->fix.xpanstep = 0;
  191. info->fix.ypanstep = 0;
  192. info->fix.ywrapstep = 0;
  193. info->fix.accel = FB_ACCEL_NONE;
  194. info->var.nonstd = 0;
  195. info->var.activate = FB_ACTIVATE_NOW;
  196. info->var.height = -1;
  197. info->var.width = -1;
  198. info->var.accel_flags = 0;
  199. info->var.vmode = FB_VMODE_NONINTERLACED;
  200. info->fbops = &gx1fb_ops;
  201. info->flags = FBINFO_DEFAULT;
  202. info->node = -1;
  203. info->pseudo_palette = (void *)par + sizeof(struct geodefb_par);
  204. info->var.grayscale = 0;
  205. /* CRT and panel options */
  206. par->enable_crt = crt_option;
  207. if (parse_panel_option(info) < 0)
  208. printk(KERN_WARNING "%s: invalid 'panel' option -- disabling flat panel\n",
  209. info->fix.id);
  210. if (!par->panel_x)
  211. par->enable_crt = 1; /* fall back to CRT if no panel is specified */
  212. return info;
  213. }
  214. static struct fb_info *gx1fb_info;
  215. static int __init gx1fb_init(void)
  216. {
  217. struct fb_info *info;
  218. struct geodefb_par *par;
  219. int ret;
  220. #ifndef MODULE
  221. if (fb_get_options("gx1fb", NULL))
  222. return -ENODEV;
  223. #endif
  224. info = gx1fb_init_fbinfo();
  225. if (!info)
  226. return -ENOMEM;
  227. gx1fb_info = info;
  228. par = info->par;
  229. /* GX1 display controller and CS5530 video device */
  230. par->dc_ops = &gx1_dc_ops;
  231. par->vid_ops = &cs5530_vid_ops;
  232. if ((ret = gx1fb_map_video_memory(info)) < 0) {
  233. printk(KERN_ERR "%s: gx1fb_map_video_memory() failed\n", info->fix.id);
  234. goto err;
  235. }
  236. ret = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 16);
  237. if (ret == 0 || ret == 4) {
  238. printk(KERN_ERR "%s: could not find valid video mode\n", info->fix.id);
  239. ret = -EINVAL;
  240. goto err;
  241. }
  242. /* Clear the frame buffer of garbage. */
  243. memset_io(info->screen_base, 0, info->fix.smem_len);
  244. gx1fb_check_var(&info->var, info);
  245. gx1fb_set_par(info);
  246. if (register_framebuffer(info) < 0) {
  247. ret = -EINVAL;
  248. goto err;
  249. }
  250. printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
  251. return 0;
  252. err:
  253. if (info->screen_base)
  254. iounmap(info->screen_base);
  255. if (par->vid_regs)
  256. iounmap(par->vid_regs);
  257. if (par->dc_regs)
  258. iounmap(par->dc_regs);
  259. if (par->vid_dev)
  260. pci_dev_put(par->vid_dev);
  261. if (info)
  262. framebuffer_release(info);
  263. return ret;
  264. }
  265. static void __exit gx1fb_cleanup(void)
  266. {
  267. struct fb_info *info = gx1fb_info;
  268. struct geodefb_par *par = gx1fb_info->par;
  269. unregister_framebuffer(info);
  270. iounmap((void __iomem *)info->screen_base);
  271. iounmap(par->vid_regs);
  272. iounmap(par->dc_regs);
  273. pci_dev_put(par->vid_dev);
  274. framebuffer_release(info);
  275. }
  276. module_init(gx1fb_init);
  277. module_exit(gx1fb_cleanup);
  278. module_param_string(mode, mode_option, sizeof(mode_option), 0444);
  279. MODULE_PARM_DESC(mode, "video mode (<x>x<y>[-<bpp>][@<refr>])");
  280. module_param_named(crt, crt_option, int, 0444);
  281. MODULE_PARM_DESC(crt, "enable CRT output. 0 = off, 1 = on (default)");
  282. module_param_string(panel, panel_option, sizeof(panel_option), 0444);
  283. MODULE_PARM_DESC(panel, "size of attached flat panel (<x>x<y>)");
  284. MODULE_DESCRIPTION("framebuffer driver for the AMD Geode GX1");
  285. MODULE_LICENSE("GPL");