am200epd.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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 "pxa25x.h"
  32. #include "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;
  222. par->metromem_wfm = am200_board.metromem + fw;
  223. par->metromem_img = par->metromem_wfm + am200_board.wfm_size;
  224. par->metromem_img_csum = (u16 *) (par->metromem_img + (fw * fh));
  225. par->metromem_dma = am200_board.host_fbinfo->fix.smem_start;
  226. return 0;
  227. }
  228. static int am200_get_panel_type(void)
  229. {
  230. return panel_type;
  231. }
  232. static irqreturn_t am200_handle_irq(int irq, void *dev_id)
  233. {
  234. struct metronomefb_par *par = dev_id;
  235. wake_up_interruptible(&par->waitq);
  236. return IRQ_HANDLED;
  237. }
  238. static int am200_setup_irq(struct fb_info *info)
  239. {
  240. int ret;
  241. ret = request_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), am200_handle_irq,
  242. IRQF_TRIGGER_FALLING, "AM200", info->par);
  243. if (ret)
  244. dev_err(&am200_device->dev, "request_irq failed: %d\n", ret);
  245. return ret;
  246. }
  247. static void am200_set_rst(struct metronomefb_par *par, int state)
  248. {
  249. gpio_set_value(RST_GPIO_PIN, state);
  250. }
  251. static void am200_set_stdby(struct metronomefb_par *par, int state)
  252. {
  253. gpio_set_value(STDBY_GPIO_PIN, state);
  254. }
  255. static int am200_wait_event(struct metronomefb_par *par)
  256. {
  257. return wait_event_timeout(par->waitq, gpio_get_value(RDY_GPIO_PIN), HZ);
  258. }
  259. static int am200_wait_event_intr(struct metronomefb_par *par)
  260. {
  261. return wait_event_interruptible_timeout(par->waitq,
  262. gpio_get_value(RDY_GPIO_PIN), HZ);
  263. }
  264. static struct metronome_board am200_board = {
  265. .owner = THIS_MODULE,
  266. .setup_irq = am200_setup_irq,
  267. .setup_io = am200_init_gpio_regs,
  268. .setup_fb = am200_setup_fb,
  269. .set_rst = am200_set_rst,
  270. .set_stdby = am200_set_stdby,
  271. .met_wait_event = am200_wait_event,
  272. .met_wait_event_intr = am200_wait_event_intr,
  273. .get_panel_type = am200_get_panel_type,
  274. .cleanup = am200_cleanup,
  275. };
  276. static unsigned long am200_pin_config[] __initdata = {
  277. GPIO51_GPIO,
  278. GPIO49_GPIO,
  279. GPIO48_GPIO,
  280. GPIO32_GPIO,
  281. GPIO17_GPIO,
  282. GPIO16_GPIO,
  283. };
  284. int __init am200_init(void)
  285. {
  286. int ret;
  287. /* before anything else, we request notification for any fb
  288. * creation events */
  289. fb_register_client(&am200_fb_notif);
  290. pxa2xx_mfp_config(ARRAY_AND_SIZE(am200_pin_config));
  291. /* request our platform independent driver */
  292. request_module("metronomefb");
  293. am200_device = platform_device_alloc("metronomefb", -1);
  294. if (!am200_device)
  295. return -ENOMEM;
  296. /* the am200_board that will be seen by metronomefb is a copy */
  297. platform_device_add_data(am200_device, &am200_board,
  298. sizeof(am200_board));
  299. /* this _add binds metronomefb to am200. metronomefb refcounts am200 */
  300. ret = platform_device_add(am200_device);
  301. if (ret) {
  302. platform_device_put(am200_device);
  303. fb_unregister_client(&am200_fb_notif);
  304. return ret;
  305. }
  306. am200_presetup_fb();
  307. return 0;
  308. }
  309. module_param(panel_type, uint, 0);
  310. MODULE_PARM_DESC(panel_type, "Select the panel type: 6, 8, 97");
  311. MODULE_DESCRIPTION("board driver for am200 metronome epd kit");
  312. MODULE_AUTHOR("Jaya Kumar");
  313. MODULE_LICENSE("GPL");