memoryCall.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * am200epd.c -- Platform device for AM200 EPD kit
  3. *
  4. * Copyright (C) 2008, Jaya Kumar
  5. *
  6. * This file is subject to the terms and conditions of the GNU General Public
  7. * License. See the file COPYING in the main directory of this archive for
  8. * more details.
  9. *
  10. * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
  11. *
  12. * This work was made possible by help and equipment support from E-Ink
  13. * Corporation. http://support.eink.com/community
  14. *
  15. * This driver is written to be used with the Metronome display controller.
  16. * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600
  17. * Vizplex EPD on a Gumstix board using the Lyre interface board.
  18. *
  19. */
  20. #include <linux/module.h>
  21. #include <linux/kernel.h>
  22. #include <linux/errno.h>
  23. #include <linux/string.h>
  24. #include <linux/delay.h>
  25. #include <linux/interrupt.h>
  26. #include <linux/fb.h>
  27. #include <linux/init.h>
  28. #include <linux/platform_device.h>
  29. #include <linux/irq.h>
  30. #include <linux/gpio.h>
  31. #include <mach/pxa25x.h>
  32. #include <mach/gumstix.h>
  33. #include <linux/platform_data/video-pxafb.h>
  34. #include "generic.h"
  35. #include <video/metronomefb.h>
  36. static unsigned int panel_type = 6;
  37. static struct platform_device *am200_device;
  38. static struct metronome_board am200_board;
  39. static struct pxafb_mode_info am200_fb_mode_9inch7 = {
  40. .pixclock = 40000,
  41. .xres = 1200,
  42. .yres = 842,
  43. .bpp = 16,
  44. .hsync_len = 2,
  45. .left_margin = 2,
  46. .right_margin = 2,
  47. .vsync_len = 1,
  48. .upper_margin = 2,
  49. .lower_margin = 25,
  50. .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  51. };
  52. static struct pxafb_mode_info am200_fb_mode_8inch = {
  53. .pixclock = 40000,
  54. .xres = 1088,
  55. .yres = 791,
  56. .bpp = 16,
  57. .hsync_len = 28,
  58. .left_margin = 8,
  59. .right_margin = 30,
  60. .vsync_len = 8,
  61. .upper_margin = 10,
  62. .lower_margin = 8,
  63. .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  64. };
  65. static struct pxafb_mode_info am200_fb_mode_6inch = {
  66. .pixclock = 40189,
  67. .xres = 832,
  68. .yres = 622,
  69. .bpp = 16,
  70. .hsync_len = 28,
  71. .left_margin = 34,
  72. .right_margin = 34,
  73. .vsync_len = 25,
  74. .upper_margin = 0,
  75. .lower_margin = 2,
  76. .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
  77. };
  78. static struct pxafb_mach_info am200_fb_info = {
  79. .modes = &am200_fb_mode_6inch,
  80. .num_modes = 1,
  81. .lcd_conn = LCD_TYPE_COLOR_TFT | LCD_PCLK_EDGE_FALL |
  82. LCD_AC_BIAS_FREQ(24),
  83. };
  84. /* register offsets for gpio control */
  85. #define LED_GPIO_PIN 51
  86. #define STDBY_GPIO_PIN 48
  87. #define RST_GPIO_PIN 49
  88. #define RDY_GPIO_PIN 32
  89. #define ERR_GPIO_PIN 17
  90. #define PCBPWR_GPIO_PIN 16
  91. static int gpios[] = { LED_GPIO_PIN , STDBY_GPIO_PIN , RST_GPIO_PIN,
  92. RDY_GPIO_PIN, ERR_GPIO_PIN, PCBPWR_GPIO_PIN };
  93. static char *gpio_names[] = { "LED" , "STDBY" , "RST", "RDY", "ERR", "PCBPWR" };
  94. static int am200_init_gpio_regs(struct metronomefb_par *par)
  95. {
  96. int i;
  97. int err;
  98. for (i = 0; i < ARRAY_SIZE(gpios); i++) {
  99. err = gpio_request(gpios[i], gpio_names[i]);
  100. if (err) {
  101. dev_err(&am200_device->dev, "failed requesting "
  102. "gpio %s, err=%d\n", gpio_names[i], err);
  103. goto err_req_gpio;
  104. }
  105. }
  106. gpio_direction_output(LED_GPIO_PIN, 0);
  107. gpio_direction_output(STDBY_GPIO_PIN, 0);
  108. gpio_direction_output(RST_GPIO_PIN, 0);
  109. gpio_direction_input(RDY_GPIO_PIN);
  110. gpio_direction_input(ERR_GPIO_PIN);
  111. gpio_direction_output(PCBPWR_GPIO_PIN, 0);
  112. return 0;
  113. err_req_gpio:
  114. while (--i >= 0)
  115. gpio_free(gpios[i]);
  116. return err;
  117. }
  118. static void am200_cleanup(struct metronomefb_par *par)
  119. {
  120. int i;
  121. free_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), par);
  122. for (i = 0; i < ARRAY_SIZE(gpios); i++)
  123. gpio_free(gpios[i]);
  124. }
  125. static int am200_share_video_mem(struct fb_info *info)
  126. {
  127. /* rough check if this is our desired fb and not something else */
  128. if ((info->var.xres != am200_fb_info.modes->xres)
  129. || (info->var.yres != am200_fb_info.modes->yres))
  130. return 0;
  131. /* we've now been notified that we have our new fb */
  132. am200_board.metromem = info->screen_base;
  133. am200_board.host_fbinfo = info;
  134. /* try to refcount host drv since we are the consumer after this */
  135. if (!try_module_get(info->fbops->owner))
  136. return -ENODEV;
  137. return 0;
  138. }
  139. static int am200_unshare_video_mem(struct fb_info *info)
  140. {
  141. dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
  142. if (info != am200_board.host_fbinfo)
  143. return 0;
  144. module_put(am200_board.host_fbinfo->fbops->owner);
  145. return 0;
  146. }
  147. static int am200_fb_notifier_callback(struct notifier_block *self,
  148. unsigned long event, void *data)
  149. {
  150. struct fb_event *evdata = data;
  151. struct fb_info *info = evdata->info;
  152. dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
  153. if (event == FB_EVENT_FB_REGISTERED)
  154. return am200_share_video_mem(info);
  155. else if (event == FB_EVENT_FB_UNREGISTERED)
  156. return am200_unshare_video_mem(info);
  157. return 0;
  158. }
  159. static struct notifier_block am200_fb_notif = {
  160. .notifier_call = am200_fb_notifier_callback,
  161. };
  162. /* this gets called as part of our init. these steps must be done now so
  163. * that we can use pxa_set_fb_info */
  164. static void __init am200_presetup_fb(void)
  165. {
  166. int fw;
  167. int fh;
  168. int padding_size;
  169. int totalsize;
  170. switch (panel_type) {
  171. case 6:
  172. am200_fb_info.modes = &am200_fb_mode_6inch;
  173. break;
  174. case 8:
  175. am200_fb_info.modes = &am200_fb_mode_8inch;
  176. break;
  177. case 97:
  178. am200_fb_info.modes = &am200_fb_mode_9inch7;
  179. break;
  180. default:
  181. dev_err(&am200_device->dev, "invalid panel_type selection,"
  182. " setting to 6\n");
  183. am200_fb_info.modes = &am200_fb_mode_6inch;
  184. break;
  185. }
  186. /* the frame buffer is divided as follows:
  187. command | CRC | padding
  188. 16kb waveform data | CRC | padding
  189. image data | CRC
  190. */
  191. fw = am200_fb_info.modes->xres;
  192. fh = am200_fb_info.modes->yres;
  193. /* waveform must be 16k + 2 for checksum */
  194. am200_board.wfm_size = roundup(16*1024 + 2, fw);
  195. padding_size = PAGE_SIZE + (4 * fw);
  196. /* total is 1 cmd , 1 wfm, padding and image */
  197. totalsize = fw + am200_board.wfm_size + padding_size + (fw*fh);
  198. /* save this off because we're manipulating fw after this and
  199. * we'll need it when we're ready to setup the framebuffer */
  200. am200_board.fw = fw;
  201. am200_board.fh = fh;
  202. /* the reason we do this adjustment is because we want to acquire
  203. * more framebuffer memory without imposing custom awareness on the
  204. * underlying pxafb driver */
  205. am200_fb_info.modes->yres = DIV_ROUND_UP(totalsize, fw);
  206. /* we divide since we told the LCD controller we're 16bpp */
  207. am200_fb_info.modes->xres /= 2;
  208. pxa_set_fb_info(NULL, &am200_fb_info);
  209. }
  210. /* this gets called by metronomefb as part of its init, in our case, we
  211. * have already completed initial framebuffer init in presetup_fb so we
  212. * can just setup the fb access pointers */
  213. static int am200_setup_fb(struct metronomefb_par *par)
  214. {
  215. int fw;
  216. int fh;
  217. fw = am200_board.fw;
  218. fh = am200_board.fh;
  219. /* metromem was set up by the notifier in share_video_mem so now
  220. * we can use its value to calculate the other entries */
  221. par->metromem_cmd = (struct metromem_cmd *) am200_board.metromem;