sdum.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. /*
  2. * drivers/video/pnx4008/sdum.c
  3. *
  4. * Display Update Master support
  5. *
  6. * Authors: Grigory Tolstolytkin <gtolstolytkin@ru.mvista.com>
  7. * Vitaly Wool <vitalywool@gmail.com>
  8. * Based on Philips Semiconductors's code
  9. *
  10. * Copyrght (c) 2005-2006 MontaVista Software, Inc.
  11. * Copyright (c) 2005 Philips Semiconductors
  12. * This file is licensed under the terms of the GNU General Public License
  13. * version 2. This program is licensed "as is" without any warranty of any
  14. * kind, whether express or implied.
  15. */
  16. #include <linux/module.h>
  17. #include <linux/kernel.h>
  18. #include <linux/errno.h>
  19. #include <linux/string.h>
  20. #include <linux/mm.h>
  21. #include <linux/tty.h>
  22. #include <linux/vmalloc.h>
  23. #include <linux/delay.h>
  24. #include <linux/interrupt.h>
  25. #include <linux/platform_device.h>
  26. #include <linux/fb.h>
  27. #include <linux/init.h>
  28. #include <linux/dma-mapping.h>
  29. #include <linux/clk.h>
  30. #include <linux/gfp.h>
  31. #include <asm/uaccess.h>
  32. #include <asm/gpio.h>
  33. #include "sdum.h"
  34. #include "fbcommon.h"
  35. #include "dum.h"
  36. /* Framebuffers we have */
  37. static struct pnx4008_fb_addr {
  38. int fb_type;
  39. long addr_offset;
  40. long fb_length;
  41. } fb_addr[] = {
  42. [0] = {
  43. FB_TYPE_YUV, 0, 0xB0000
  44. },
  45. [1] = {
  46. FB_TYPE_RGB, 0xB0000, 0x50000
  47. },
  48. };
  49. static struct dum_data {
  50. u32 lcd_phys_start;
  51. u32 lcd_virt_start;
  52. u32 slave_phys_base;
  53. u32 *slave_virt_base;
  54. int fb_owning_channel[MAX_DUM_CHANNELS];
  55. struct dumchannel_uf chan_uf_store[MAX_DUM_CHANNELS];
  56. } dum_data;
  57. /* Different local helper functions */
  58. static u32 nof_pixels_dx(struct dum_ch_setup *ch_setup)
  59. {
  60. return (ch_setup->xmax - ch_setup->xmin + 1);
  61. }
  62. static u32 nof_pixels_dy(struct dum_ch_setup *ch_setup)
  63. {
  64. return (ch_setup->ymax - ch_setup->ymin + 1);
  65. }
  66. static u32 nof_pixels_dxy(struct dum_ch_setup *ch_setup)
  67. {
  68. return (nof_pixels_dx(ch_setup) * nof_pixels_dy(ch_setup));
  69. }
  70. static u32 nof_bytes(struct dum_ch_setup *ch_setup)
  71. {
  72. u32 r = nof_pixels_dxy(ch_setup);
  73. switch (ch_setup->format) {
  74. case RGB888:
  75. case RGB666:
  76. r *= 4;
  77. break;
  78. default:
  79. r *= 2;
  80. break;
  81. }
  82. return r;
  83. }
  84. static u32 build_command(int disp_no, u32 reg, u32 val)
  85. {
  86. return ((disp_no << 26) | BIT(25) | (val << 16) | (disp_no << 10) |
  87. (reg << 0));
  88. }
  89. static u32 build_double_index(int disp_no, u32 val)
  90. {
  91. return ((disp_no << 26) | (val << 16) | (disp_no << 10) | (val << 0));
  92. }
  93. static void build_disp_window(struct dum_ch_setup * ch_setup, struct disp_window * dw)
  94. {
  95. dw->ymin = ch_setup->ymin;
  96. dw->ymax = ch_setup->ymax;
  97. dw->xmin_l = ch_setup->xmin & 0xFF;
  98. dw->xmin_h = (ch_setup->xmin & BIT(8)) >> 8;
  99. dw->xmax_l = ch_setup->xmax & 0xFF;
  100. dw->xmax_h = (ch_setup->xmax & BIT(8)) >> 8;
  101. }
  102. static int put_channel(struct dumchannel chan)
  103. {
  104. int i = chan.channelnr;
  105. if (i < 0 || i > MAX_DUM_CHANNELS)
  106. return -EINVAL;
  107. else {
  108. DUM_CH_MIN(i) = chan.dum_ch_min;
  109. DUM_CH_MAX(i) = chan.dum_ch_max;
  110. DUM_CH_CONF(i) = chan.dum_ch_conf;
  111. DUM_CH_CTRL(i) = chan.dum_ch_ctrl;
  112. }
  113. return 0;
  114. }
  115. static void clear_channel(int channr)
  116. {
  117. struct dumchannel chan;
  118. chan.channelnr = channr;
  119. chan.dum_ch_min = 0;
  120. chan.dum_ch_max = 0;
  121. chan.dum_ch_conf = 0;
  122. chan.dum_ch_ctrl = 0;
  123. put_channel(chan);
  124. }
  125. static int put_cmd_string(struct cmdstring cmds)
  126. {
  127. u16 *cmd_str_virtaddr;
  128. u32 *cmd_ptr0_virtaddr;
  129. u32 cmd_str_physaddr;
  130. int i = cmds.channelnr;
  131. if (i < 0 || i > MAX_DUM_CHANNELS)
  132. return -EINVAL;
  133. else if ((cmd_ptr0_virtaddr =
  134. (int *)ioremap_nocache(DUM_COM_BASE,
  135. sizeof(int) * MAX_DUM_CHANNELS)) ==
  136. NULL)
  137. return -EIOREMAPFAILED;
  138. else {
  139. cmd_str_physaddr = ioread32(&cmd_ptr0_virtaddr[cmds.channelnr]);
  140. if ((cmd_str_virtaddr =
  141. (u16 *) ioremap_nocache(cmd_str_physaddr,
  142. sizeof(cmds))) == NULL) {
  143. iounmap(cmd_ptr0_virtaddr);
  144. return -EIOREMAPFAILED;
  145. } else {
  146. int t;
  147. for (t = 0; t < 8; t++)
  148. iowrite16(*((u16 *)&cmds.prestringlen + t),
  149. cmd_str_virtaddr + t);
  150. for (t = 0; t < cmds.prestringlen / 2; t++)
  151. iowrite16(*((u16 *)&cmds.precmd + t),
  152. cmd_str_virtaddr + t + 8);
  153. for (t = 0; t < cmds.poststringlen / 2; t++)
  154. iowrite16(*((u16 *)&cmds.postcmd + t),
  155. cmd_str_virtaddr + t + 8 +
  156. cmds.prestringlen / 2);
  157. iounmap(cmd_ptr0_virtaddr);
  158. iounmap(cmd_str_virtaddr);
  159. }
  160. }
  161. return 0;
  162. }
  163. static u32 dum_ch_setup(int ch_no, struct dum_ch_setup * ch_setup)
  164. {
  165. struct cmdstring cmds_c;
  166. struct cmdstring *cmds = &cmds_c;
  167. struct disp_window dw;
  168. int standard;
  169. u32 orientation = 0;
  170. struct dumchannel chan = { 0 };
  171. int ret;
  172. if ((ch_setup->xmirror) || (ch_setup->ymirror) || (ch_setup->rotate)) {
  173. standard = 0;
  174. orientation = BIT(1); /* always set 9-bit-bus */
  175. if (ch_setup->xmirror)
  176. orientation |= BIT(4);
  177. if (ch_setup->ymirror)
  178. orientation |= BIT(3);
  179. if (ch_setup->rotate)
  180. orientation |= BIT(0);
  181. } else
  182. standard = 1;
  183. cmds->channelnr = ch_no;
  184. /* build command string header */
  185. if (standard) {
  186. cmds->prestringlen = 32;
  187. cmds->poststringlen = 0;
  188. } else {
  189. cmds->prestringlen = 48;
  190. cmds->poststringlen = 16;
  191. }
  192. cmds->format =
  193. (u16) ((ch_setup->disp_no << 4) | (BIT(3)) | (ch_setup->format));
  194. cmds->reserved = 0x0;
  195. cmds->startaddr_low = (ch_setup->minadr & 0xFFFF);
  196. cmds->startaddr_high = (ch_setup->minadr >> 16);
  197. if ((ch_setup->minadr == 0) && (ch_setup->maxadr == 0)
  198. && (ch_setup->xmin == 0)
  199. && (ch_setup->ymin == 0) && (ch_setup->xmax == 0)
  200. && (ch_setup->ymax == 0)) {
  201. cmds->pixdatlen_low = 0;
  202. cmds->pixdatlen_high = 0;
  203. } else {
  204. u32 nbytes = nof_bytes(ch_setup);
  205. cmds->pixdatlen_low = (nbytes & 0xFFFF);
  206. cmds->pixdatlen_high = (nbytes >> 16);
  207. }
  208. if (ch_setup->slave_trans)
  209. cmds->pixdatlen_high |= BIT(15);
  210. /* build pre-string */
  211. build_disp_window(ch_setup, &dw);
  212. if (standard) {
  213. cmds->precmd[0] =
  214. build_command(ch_setup->disp_no, DISP_XMIN_L_REG, 0x99);
  215. cmds->precmd[1] =
  216. build_command(ch_setup->disp_no, DISP_XMIN_L_REG,
  217. dw.xmin_l);
  218. cmds->precmd[2] =
  219. build_command(ch_setup->disp_no, DISP_XMIN_H_REG,
  220. dw.xmin_h);
  221. cmds->precmd[3] =
  222. build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin);
  223. cmds->precmd[4] =
  224. build_command(ch_setup->disp_no, DISP_XMAX_L_REG,
  225. dw.xmax_l);
  226. cmds->precmd[5] =
  227. build_command(ch_setup->disp_no, DISP_XMAX_H_REG,
  228. dw.xmax_h);
  229. cmds->precmd[6] =
  230. build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax);
  231. cmds->precmd[7] =
  232. build_double_index(ch_setup->disp_no, DISP_PIXEL_REG);
  233. } else {
  234. if (dw.xmin_l == ch_no)
  235. cmds->precmd[0] =
  236. build_command(ch_setup->disp_no, DISP_XMIN_L_REG,
  237. 0x99);
  238. else
  239. cmds->precmd[0] =
  240. build_command(ch_setup->disp_no, DISP_XMIN_L_REG,
  241. ch_no);
  242. cmds->precmd[1] =
  243. build_command(ch_setup->disp_no, DISP_XMIN_L_REG,
  244. dw.xmin_l);
  245. cmds->precmd[2] =
  246. build_command(ch_setup->disp_no, DISP_XMIN_H_REG,
  247. dw.xmin_h);
  248. cmds->precmd[3] =
  249. build_command(ch_setup->disp_no, DISP_YMIN_REG, dw.ymin);
  250. cmds->precmd[4] =
  251. build_command(ch_setup->disp_no, DISP_XMAX_L_REG,
  252. dw.xmax_l);
  253. cmds->precmd[5] =
  254. build_command(ch_setup->disp_no, DISP_XMAX_H_REG,
  255. dw.xmax_h);
  256. cmds->precmd[6] =
  257. build_command(ch_setup->disp_no, DISP_YMAX_REG, dw.ymax);
  258. cmds->precmd[7] =
  259. build_command(ch_setup->disp_no, DISP_1_REG, orientation);
  260. cmds->precmd[8] =
  261. build_double_index(ch_setup->disp_no, DISP_PIXEL_REG);
  262. cmds->precmd[9] =
  263. build_double_index(ch_setup->disp_no, DISP_PIXEL_REG);
  264. cmds->precmd[0xA] =
  265. build_double_index(ch_setup->disp_no, DISP_PIXEL_REG);
  266. cmds->precmd[0xB] =
  267. build_double_index(ch_setup->disp_no, DISP_PIXEL_REG);
  268. cmds->postcmd[0] =
  269. build_command(ch_setup->disp_no, DISP_1_REG, BIT(1));
  270. cmds->postcmd[1] =
  271. build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 1);
  272. cmds->postcmd[2] =
  273. build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 2);
  274. cmds->postcmd[3] =
  275. build_command(ch_setup->disp_no, DISP_DUMMY1_REG, 3);
  276. }
  277. if ((ret = put_cmd_string(cmds_c)) != 0) {
  278. return ret;
  279. }
  280. chan.channelnr = cmds->channelnr;
  281. chan.dum_ch_min = ch_setup->dirtybuffer + ch_setup->minadr;
  282. chan.dum_ch_max = ch_setup->dirtybuffer + ch_setup->maxadr;
  283. chan.dum_ch_conf = 0x002;
  284. chan.dum_ch_ctrl = 0x04;
  285. put_channel(chan);
  286. return 0;
  287. }
  288. static u32 display_open(int ch_no, int auto_update, u32 * dirty_buffer,
  289. u32 * frame_buffer, u32 xpos, u32 ypos, u32 w, u32 h)
  290. {
  291. struct dum_ch_setup k;
  292. int ret;
  293. /* keep width & height within display area */
  294. if ((xpos + w) > DISP_MAX_X_SIZE)
  295. w = DISP_MAX_X_SIZE - xpos;
  296. if ((ypos + h) > DISP_MAX_Y_SIZE)
  297. h = DISP_MAX_Y_SIZE - ypos;
  298. /* assume 1 display only */
  299. k.disp_no = 0;
  300. k.xmin = xpos;
  301. k.ymin = ypos;
  302. k.xmax = xpos + (w - 1);
  303. k.ymax = ypos + (h - 1);
  304. /* adjust min and max values if necessary */
  305. if (k.xmin > DISP_MAX_X_SIZE - 1)
  306. k.xmin = DISP_MAX_X_SIZE - 1;
  307. if (k.ymin > DISP_MAX_Y_SIZE - 1)
  308. k.ymin = DISP_MAX_Y_SIZE - 1;
  309. if (k.xmax > DISP_MAX_X_SIZE - 1)
  310. k.xmax = DISP_MAX_X_SIZE - 1;
  311. if (k.ymax > DISP_MAX_Y_SIZE - 1)
  312. k.ymax = DISP_MAX_Y_SIZE - 1;
  313. k.xmirror = 0;
  314. k.ymirror = 0;
  315. k.rotate = 0;
  316. k.minadr = (u32) frame_buffer;
  317. k.maxadr = (u32) frame_buffer + (((w - 1) << 10) | ((h << 2) - 2));
  318. k.pad = PAD_1024;
  319. k.dirtybuffer = (u32) dirty_buffer;
  320. k.format = RGB888;
  321. k.hwdirty = 0;
  322. k.slave_trans = 0;
  323. ret = dum_ch_setup(ch_no, &k);
  324. return ret;
  325. }
  326. static void lcd_reset(void)
  327. {
  328. u32 *dum_pio_base = (u32 *)IO_ADDRESS(PNX4008_PIO_BASE);
  329. udelay(1);
  330. iowrite32(BIT(19), &dum_pio_base[2]);
  331. udelay(1);
  332. iowrite32(BIT(19), &dum_pio_base[1]);
  333. udelay(1);
  334. }
  335. static int dum_init(struct platform_device *pdev)
  336. {
  337. struct clk *clk;
  338. /* enable DUM clock */
  339. clk = clk_get(&pdev->dev, "dum_ck");
  340. if (IS_ERR(clk)) {
  341. printk(KERN_ERR "pnx4008_dum: Unable to access DUM clock\n");
  342. return PTR_ERR(clk);
  343. }
  344. clk_set_rate(clk, 1);
  345. clk_put(clk);
  346. DUM_CTRL = V_DUM_RESET;
  347. /* set priority to "round-robin". All other params to "false" */
  348. DUM_CONF = BIT(9);
  349. /* Display 1 */
  350. DUM_WTCFG1 = PNX4008_DUM_WT_CFG;
  351. DUM_RTCFG1 = PNX4008_DUM_RT_CFG;
  352. DUM_TCFG = PNX4008_DUM_T_CFG;
  353. return 0;
  354. }
  355. static void dum_chan_init(void)
  356. {
  357. int i = 0, ch = 0;
  358. u32 *cmdptrs;
  359. u32 *cmdstrings;
  360. DUM_COM_BASE =
  361. CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS;
  362. if ((cmdptrs =
  363. (u32 *) ioremap_nocache(DUM_COM_BASE,
  364. sizeof(u32) * NR_OF_CMDSTRINGS)) == NULL)
  365. return;
  366. for (ch = 0; ch < NR_OF_CMDSTRINGS; ch++)
  367. iowrite32(CMDSTRING_BASEADDR + BYTES_PER_CMDSTRING * ch,
  368. cmdptrs + ch);
  369. for (ch = 0; ch < MAX_DUM_CHANNELS; ch++)
  370. clear_channel(ch);
  371. /* Clear the cmdstrings */
  372. cmdstrings =
  373. (u32 *)ioremap_nocache(*cmdptrs,
  374. BYTES_PER_CMDSTRING * NR_OF_CMDSTRINGS);
  375. if (!cmdstrings)
  376. goto out;
  377. for (i = 0; i < NR_OF_CMDSTRINGS * BYTES_PER_CMDSTRING / sizeof(u32);
  378. i++)
  379. iowrite32(0, cmdstrings + i);
  380. iounmap((u32 *)cmdstrings);
  381. out:
  382. iounmap((u32 *)cmdptrs);
  383. }
  384. static void lcd_init(void)
  385. {
  386. lcd_reset();
  387. DUM_OUTP_FORMAT1 = 0; /* RGB666 */
  388. udelay(1);
  389. iowrite32(V_LCD_STANDBY_OFF, dum_data.slave_virt_base);
  390. udelay(1);
  391. iowrite32(V_LCD_USE_9BIT_BUS, dum_data.slave_virt_base);
  392. udelay(1);
  393. iowrite32(V_LCD_SYNC_RISE_L, dum_data.slave_virt_base);
  394. udelay(1);
  395. iowrite32(V_LCD_SYNC_RISE_H, dum_data.slave_virt_base);
  396. udelay(1);
  397. iowrite32(V_LCD_SYNC_FALL_L, dum_data.slave_virt_base);
  398. udelay(1);
  399. iowrite32(V_LCD_SYNC_FALL_H, dum_data.slave_virt_base);
  400. udelay(1);
  401. iowrite32(V_LCD_SYNC_ENABLE, dum_data.slave_virt_base);
  402. udelay(1);
  403. iowrite32(V_LCD_DISPLAY_ON, dum_data.slave_virt_base);
  404. udelay(1);
  405. }
  406. /* Interface exported to framebuffer drivers */
  407. int pnx4008_get_fb_addresses(int fb_type, void **virt_addr,
  408. dma_addr_t *phys_addr, int *fb_length)
  409. {
  410. int i;
  411. int ret = -1;
  412. for (i = 0; i < ARRAY_SIZE(fb_addr); i++)
  413. if (fb_addr[i].fb_type == fb_type) {
  414. *virt_addr = (void *)(dum_data.lcd_virt_start +
  415. fb_addr[i].addr_offset);
  416. *phys_addr =
  417. dum_data.lcd_phys_start + fb_addr[i].addr_offset;
  418. *fb_length = fb_addr[i].fb_length;
  419. ret = 0;
  420. break;
  421. }
  422. return ret;
  423. }
  424. EXPORT_SYMBOL(pnx4008_get_fb_addresses);
  425. int pnx4008_alloc_dum_channel(int dev_id)
  426. {
  427. int i = 0;
  428. while ((i < MAX_DUM_CHANNELS) && (dum_data.fb_owning_channel[i] != -1))
  429. i++;
  430. if (i == MAX_DUM_CHANNELS)
  431. return -ENORESOURCESLEFT;
  432. else {
  433. dum_data.fb_owning_channel[i] = dev_id;
  434. return i;
  435. }
  436. }
  437. EXPORT_SYMBOL(pnx4008_alloc_dum_channel);
  438. int pnx4008_free_dum_channel(int channr, int dev_id)
  439. {
  440. if (channr < 0 || channr > MAX_DUM_CHANNELS)
  441. return -EINVAL;
  442. else if (dum_data.fb_owning_channel[channr] != dev_id)
  443. return -EFBNOTOWNER;
  444. else {
  445. clear_channel(channr);
  446. dum_data.fb_owning_channel[channr] = -1;
  447. }
  448. return 0;
  449. }
  450. EXPORT_SYMBOL(pnx4008_free_dum_channel);
  451. int pnx4008_put_dum_channel_uf(struct dumchannel_uf chan_uf, int dev_id)
  452. {
  453. int i = chan_uf.channelnr;
  454. int ret;
  455. if (i < 0 || i > MAX_DUM_CHANNELS)
  456. return -EINVAL;
  457. else if (dum_data.fb_owning_channel[i] != dev_id)
  458. return -EFBNOTOWNER;
  459. else if ((ret =
  460. display_open(chan_uf.channelnr, 0, chan_uf.dirty,
  461. chan_uf.source, chan_uf.y_offset,
  462. chan_uf.x_offset, chan_uf.height,
  463. chan_uf.width)) != 0)
  464. return ret;
  465. else {
  466. dum_data.chan_uf_store[i].dirty = chan_uf.dirty;
  467. dum_data.chan_uf_store[i].source = chan_uf.source;
  468. dum_data.chan_uf_store[i].x_offset = chan_uf.x_offset;
  469. dum_data.chan_uf_store[i].y_offset = chan_uf.y_offset;
  470. dum_data.chan_uf_store[i].width = chan_uf.width;
  471. dum_data.chan_uf_store[i].height = chan_uf.height;
  472. }
  473. return 0;
  474. }
  475. EXPORT_SYMBOL(pnx4008_put_dum_channel_uf);
  476. int pnx4008_set_dum_channel_sync(int channr, int val, int dev_id)
  477. {
  478. if (channr < 0 || channr > MAX_DUM_CHANNELS)
  479. return -EINVAL;
  480. else if (dum_data.fb_owning_channel[channr] != dev_id)
  481. return -EFBNOTOWNER;
  482. else {
  483. if (val == CONF_SYNC_ON) {
  484. DUM_CH_CONF(channr) |= CONF_SYNCENABLE;
  485. DUM_CH_CONF(channr) |= DUM_CHANNEL_CFG_SYNC_MASK |
  486. DUM_CHANNEL_CFG_SYNC_MASK_SET;
  487. } else if (val == CONF_SYNC_OFF)
  488. DUM_CH_CONF(channr) &= ~CONF_SYNCENABLE;
  489. else
  490. return -EINVAL;
  491. }
  492. return 0;
  493. }
  494. EXPORT_SYMBOL(pnx4008_set_dum_channel_sync);
  495. int pnx4008_set_dum_channel_dirty_detect(int channr, int val, int dev_id)
  496. {
  497. if (channr < 0 || channr > MAX_DUM_CHANNELS)
  498. return -EINVAL;
  499. else if (dum_data.fb_owning_channel[channr] != dev_id)
  500. return -EFBNOTOWNER;
  501. else {
  502. if (val == CONF_DIRTYDETECTION_ON)
  503. DUM_CH_CONF(channr) |= CONF_DIRTYENABLE;
  504. else if (val == CONF_DIRTYDETECTION_OFF)
  505. DUM_CH_CONF(channr) &= ~CONF_DIRTYENABLE;
  506. else
  507. return -EINVAL;
  508. }
  509. return 0;
  510. }
  511. EXPORT_SYMBOL(pnx4008_set_dum_channel_dirty_detect);
  512. #if 0 /* Functions not used currently, but likely to be used in future */
  513. static int get_channel(struct dumchannel *p_chan)
  514. {
  515. int i = p_chan->channelnr;
  516. if (i < 0 || i > MAX_DUM_CHANNELS)
  517. return -EINVAL;
  518. else {
  519. p_chan->dum_ch_min = DUM_CH_MIN(i);
  520. p_chan->dum_ch_max = DUM_CH_MAX(i);
  521. p_chan->dum_ch_conf = DUM_CH_CONF(i);
  522. p_chan->dum_ch_stat = DUM_CH_STAT(i);
  523. p_chan->dum_ch_ctrl = 0; /* WriteOnly control register */
  524. }
  525. return 0;
  526. }
  527. int pnx4008_get_dum_channel_uf(struct dumchannel_uf *p_chan_uf, int dev_id)
  528. {
  529. int i = p_chan_uf->channelnr;
  530. if (i < 0 || i > MAX_DUM_CHANNELS)
  531. return -EINVAL;
  532. else if (dum_data.fb_owning_channel[i] != dev_id)
  533. return -EFBNOTOWNER;
  534. else {
  535. p_chan_uf->dirty = dum_data.chan_uf_store[i].dirty;
  536. p_chan_uf->source = dum_data.chan_uf_store[i].source;
  537. p_chan_uf->x_offset = dum_data.chan_uf_store[i].x_offset;
  538. p_chan_uf->y_offset = dum_data.chan_uf_store[i].y_offset;
  539. p_chan_uf->width = dum_data.chan_uf_store[i].width;
  540. p_chan_uf->height = dum_data.chan_uf_store[i].height;
  541. }
  542. return 0;
  543. }
  544. EXPORT_SYMBOL(pnx4008_get_dum_channel_uf);
  545. int pnx4008_get_dum_channel_config(int channr, int dev_id)
  546. {
  547. int ret;
  548. struct dumchannel chan;
  549. if (channr < 0 || channr > MAX_DUM_CHANNELS)
  550. return -EINVAL;
  551. else if (dum_data.fb_owning_channel[channr] != dev_id)
  552. return -EFBNOTOWNER;
  553. else {
  554. chan.channelnr = channr;
  555. if ((ret = get_channel(&chan)) != 0)
  556. return ret;
  557. }
  558. return (chan.dum_ch_conf & DUM_CHANNEL_CFG_MASK);
  559. }
  560. EXPORT_SYMBOL(pnx4008_get_dum_channel_config);
  561. int pnx4008_force_update_dum_channel(int channr, int dev_id)
  562. {
  563. if (channr < 0 || channr > MAX_DUM_CHANNELS)
  564. return -EINVAL;
  565. else if (dum_data.fb_owning_channel[channr] != dev_id)
  566. return -EFBNOTOWNER;
  567. else
  568. DUM_CH_CTRL(channr) = CTRL_SETDIRTY;
  569. return 0;
  570. }
  571. EXPORT_SYMBOL(pnx4008_force_update_dum_channel);
  572. #endif
  573. int pnx4008_sdum_mmap(struct fb_info *info, struct vm_area_struct *vma,
  574. struct device *dev)
  575. {
  576. unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
  577. if (off < info->fix.smem_len) {
  578. vma->vm_pgoff += 1;
  579. return dma_mmap_writecombine(dev, vma,
  580. (void *)dum_data.lcd_virt_start,
  581. dum_data.lcd_phys_start,
  582. FB_DMA_SIZE);
  583. }
  584. return -EINVAL;
  585. }
  586. EXPORT_SYMBOL(pnx4008_sdum_mmap);
  587. int pnx4008_set_dum_exit_notification(int dev_id)
  588. {
  589. int i;
  590. for (i = 0; i < MAX_DUM_CHANNELS; i++)
  591. if (dum_data.fb_owning_channel[i] == dev_id)
  592. return -ERESOURCESNOTFREED;
  593. return 0;
  594. }
  595. EXPORT_SYMBOL(pnx4008_set_dum_exit_notification);
  596. /* Platform device driver for DUM */
  597. static int sdum_suspend(struct platform_device *pdev, pm_message_t state)
  598. {
  599. int retval = 0;
  600. struct clk *clk;
  601. clk = clk_get(0, "dum_ck");
  602. if (!IS_ERR(clk)) {
  603. clk_set_rate(clk, 0);
  604. clk_put(clk);
  605. } else
  606. retval = PTR_ERR(clk);
  607. /* disable BAC */
  608. DUM_CTRL = V_BAC_DISABLE_IDLE;
  609. /* LCD standby & turn off display */
  610. lcd_reset();
  611. return retval;
  612. }
  613. static int sdum_resume(struct platform_device *pdev)
  614. {
  615. int retval = 0;
  616. struct clk *clk;
  617. clk = clk_get(0, "dum_ck");
  618. if (!IS_ERR(clk)) {
  619. clk_set_rate(clk, 1);
  620. clk_put(clk);
  621. } else
  622. retval = PTR_ERR(clk);
  623. /* wait for BAC disable */
  624. DUM_CTRL = V_BAC_DISABLE_TRIG;
  625. while (DUM_CTRL & BAC_ENABLED)
  626. udelay(10);
  627. /* re-init LCD */
  628. lcd_init();
  629. /* enable BAC and reset MUX */
  630. DUM_CTRL = V_BAC_ENABLE;
  631. udelay(1);
  632. DUM_CTRL = V_MUX_RESET;
  633. return 0;
  634. }
  635. static int __devinit sdum_probe(struct platform_device *pdev)
  636. {
  637. int ret = 0, i = 0;
  638. /* map frame buffer */
  639. dum_data.lcd_virt_start = (u32) dma_alloc_writecombine(&pdev->dev,
  640. FB_DMA_SIZE,
  641. &dum_data.lcd_phys_start,
  642. GFP_KERNEL);
  643. if (!dum_data.lcd_virt_start) {
  644. ret = -ENOMEM;
  645. goto out_3;
  646. }
  647. /* map slave registers */
  648. dum_data.slave_phys_base = PNX4008_DUM_SLAVE_BASE;
  649. dum_data.slave_virt_base =
  650. (u32 *) ioremap_nocache(dum_data.slave_phys_base, sizeof(u32));
  651. if (dum_data.slave_virt_base == NULL) {
  652. ret = -ENOMEM;
  653. goto out_2;
  654. }
  655. /* initialize DUM and LCD display */
  656. ret = dum_init(pdev);
  657. if (ret)
  658. goto out_1;
  659. dum_chan_init();
  660. lcd_init();
  661. DUM_CTRL = V_BAC_ENABLE;
  662. udelay(1);
  663. DUM_CTRL = V_MUX_RESET;
  664. /* set decode address and sync clock divider */
  665. DUM_DECODE = dum_data.lcd_phys_start & DUM_DECODE_MASK;
  666. DUM_CLK_DIV = PNX4008_DUM_CLK_DIV;
  667. for (i = 0; i < MAX_DUM_CHANNELS; i++)
  668. dum_data.fb_owning_channel[i] = -1;
  669. /*setup wakeup interrupt */
  670. start_int_set_rising_edge(SE_DISP_SYNC_INT);
  671. start_int_ack(SE_DISP_SYNC_INT);
  672. start_int_umask(SE_DISP_SYNC_INT);
  673. return 0;
  674. out_1:
  675. iounmap((void *)dum_data.slave_virt_base);
  676. out_2:
  677. dma_free_writecombine(&pdev->dev, FB_DMA_SIZE,
  678. (void *)dum_data.lcd_virt_start,
  679. dum_data.lcd_phys_start);
  680. out_3:
  681. return ret;
  682. }
  683. static int sdum_remove(struct platform_device *pdev)
  684. {
  685. struct clk *clk;
  686. start_int_mask(SE_DISP_SYNC_INT);
  687. clk = clk_get(0, "dum_ck");
  688. if (!IS_ERR(clk)) {
  689. clk_set_rate(clk, 0);
  690. clk_put(clk);
  691. }
  692. iounmap((void *)dum_data.slave_virt_base);
  693. dma_free_writecombine(&pdev->dev, FB_DMA_SIZE,
  694. (void *)dum_data.lcd_virt_start,
  695. dum_data.lcd_phys_start);
  696. return 0;
  697. }
  698. static struct platform_driver sdum_driver = {
  699. .driver = {
  700. .name = "pnx4008-sdum",
  701. },
  702. .probe = sdum_probe,
  703. .remove = sdum_remove,
  704. .suspend = sdum_suspend,
  705. .resume = sdum_resume,
  706. };
  707. module_platform_driver(sdum_driver);
  708. MODULE_LICENSE("GPL");