sh_mobile_lcdcfb.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759
  1. /*
  2. * SuperH Mobile LCDC Framebuffer
  3. *
  4. * Copyright (c) 2008 Magnus Damm
  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
  8. * for more details.
  9. */
  10. #include <linux/kernel.h>
  11. #include <linux/init.h>
  12. #include <linux/delay.h>
  13. #include <linux/mm.h>
  14. #include <linux/fb.h>
  15. #include <linux/clk.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/dma-mapping.h>
  18. #include <video/sh_mobile_lcdc.h>
  19. #define PALETTE_NR 16
  20. struct sh_mobile_lcdc_priv;
  21. struct sh_mobile_lcdc_chan {
  22. struct sh_mobile_lcdc_priv *lcdc;
  23. unsigned long *reg_offs;
  24. unsigned long ldmt1r_value;
  25. unsigned long enabled; /* ME and SE in LDCNT2R */
  26. struct sh_mobile_lcdc_chan_cfg cfg;
  27. u32 pseudo_palette[PALETTE_NR];
  28. struct fb_info info;
  29. dma_addr_t dma_handle;
  30. };
  31. struct sh_mobile_lcdc_priv {
  32. void __iomem *base;
  33. #ifdef CONFIG_HAVE_CLK
  34. struct clk *dot_clk;
  35. struct clk *clk;
  36. #endif
  37. unsigned long lddckr;
  38. struct sh_mobile_lcdc_chan ch[2];
  39. };
  40. /* shared registers */
  41. #define _LDDCKR 0x410
  42. #define _LDDCKSTPR 0x414
  43. #define _LDINTR 0x468
  44. #define _LDSR 0x46c
  45. #define _LDCNT1R 0x470
  46. #define _LDCNT2R 0x474
  47. #define _LDDDSR 0x47c
  48. #define _LDDWD0R 0x800
  49. #define _LDDRDR 0x840
  50. #define _LDDWAR 0x900
  51. #define _LDDRAR 0x904
  52. /* per-channel registers */
  53. enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
  54. LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR };
  55. static unsigned long lcdc_offs_mainlcd[] = {
  56. [LDDCKPAT1R] = 0x400,
  57. [LDDCKPAT2R] = 0x404,
  58. [LDMT1R] = 0x418,
  59. [LDMT2R] = 0x41c,
  60. [LDMT3R] = 0x420,
  61. [LDDFR] = 0x424,
  62. [LDSM1R] = 0x428,
  63. [LDSA1R] = 0x430,
  64. [LDMLSR] = 0x438,
  65. [LDHCNR] = 0x448,
  66. [LDHSYNR] = 0x44c,
  67. [LDVLNR] = 0x450,
  68. [LDVSYNR] = 0x454,
  69. [LDPMR] = 0x460,
  70. };
  71. static unsigned long lcdc_offs_sublcd[] = {
  72. [LDDCKPAT1R] = 0x408,
  73. [LDDCKPAT2R] = 0x40c,
  74. [LDMT1R] = 0x600,
  75. [LDMT2R] = 0x604,
  76. [LDMT3R] = 0x608,
  77. [LDDFR] = 0x60c,
  78. [LDSM1R] = 0x610,
  79. [LDSA1R] = 0x618,
  80. [LDMLSR] = 0x620,
  81. [LDHCNR] = 0x624,
  82. [LDHSYNR] = 0x628,
  83. [LDVLNR] = 0x62c,
  84. [LDVSYNR] = 0x630,
  85. [LDPMR] = 0x63c,
  86. };
  87. #define START_LCDC 0x00000001
  88. #define LCDC_RESET 0x00000100
  89. #define DISPLAY_BEU 0x00000008
  90. #define LCDC_ENABLE 0x00000001
  91. static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
  92. int reg_nr, unsigned long data)
  93. {
  94. iowrite32(data, chan->lcdc->base + chan->reg_offs[reg_nr]);
  95. }
  96. static unsigned long lcdc_read_chan(struct sh_mobile_lcdc_chan *chan,
  97. int reg_nr)
  98. {
  99. return ioread32(chan->lcdc->base + chan->reg_offs[reg_nr]);
  100. }
  101. static void lcdc_write(struct sh_mobile_lcdc_priv *priv,
  102. unsigned long reg_offs, unsigned long data)
  103. {
  104. iowrite32(data, priv->base + reg_offs);
  105. }
  106. static unsigned long lcdc_read(struct sh_mobile_lcdc_priv *priv,
  107. unsigned long reg_offs)
  108. {
  109. return ioread32(priv->base + reg_offs);
  110. }
  111. static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
  112. unsigned long reg_offs,
  113. unsigned long mask, unsigned long until)
  114. {
  115. while ((lcdc_read(priv, reg_offs) & mask) != until)
  116. cpu_relax();
  117. }
  118. static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
  119. {
  120. return chan->cfg.chan == LCDC_CHAN_SUBLCD;
  121. }
  122. static void lcdc_sys_write_index(void *handle, unsigned long data)
  123. {
  124. struct sh_mobile_lcdc_chan *ch = handle;
  125. lcdc_write(ch->lcdc, _LDDWD0R, data | 0x10000000);
  126. lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
  127. lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
  128. }
  129. static void lcdc_sys_write_data(void *handle, unsigned long data)
  130. {
  131. struct sh_mobile_lcdc_chan *ch = handle;
  132. lcdc_write(ch->lcdc, _LDDWD0R, data | 0x11000000);
  133. lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
  134. lcdc_write(ch->lcdc, _LDDWAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
  135. }
  136. static unsigned long lcdc_sys_read_data(void *handle)
  137. {
  138. struct sh_mobile_lcdc_chan *ch = handle;
  139. lcdc_write(ch->lcdc, _LDDRDR, 0x01000000);
  140. lcdc_wait_bit(ch->lcdc, _LDSR, 2, 0);
  141. lcdc_write(ch->lcdc, _LDDRAR, 1 | (lcdc_chan_is_sublcd(ch) ? 2 : 0));
  142. udelay(1);
  143. return lcdc_read(ch->lcdc, _LDDRDR) & 0xffff;
  144. }
  145. struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
  146. lcdc_sys_write_index,
  147. lcdc_sys_write_data,
  148. lcdc_sys_read_data,
  149. };
  150. static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
  151. int start)
  152. {
  153. unsigned long tmp = lcdc_read(priv, _LDCNT2R);
  154. int k;
  155. /* start or stop the lcdc */
  156. if (start)
  157. lcdc_write(priv, _LDCNT2R, tmp | START_LCDC);
  158. else
  159. lcdc_write(priv, _LDCNT2R, tmp & ~START_LCDC);
  160. /* wait until power is applied/stopped on all channels */
  161. for (k = 0; k < ARRAY_SIZE(priv->ch); k++)
  162. if (lcdc_read(priv, _LDCNT2R) & priv->ch[k].enabled)
  163. while (1) {
  164. tmp = lcdc_read_chan(&priv->ch[k], LDPMR) & 3;
  165. if (start && tmp == 3)
  166. break;
  167. if (!start && tmp == 0)
  168. break;
  169. cpu_relax();
  170. }
  171. if (!start)
  172. lcdc_write(priv, _LDDCKSTPR, 1); /* stop dotclock */
  173. }
  174. static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
  175. {
  176. struct sh_mobile_lcdc_chan *ch;
  177. struct fb_videomode *lcd_cfg;
  178. struct sh_mobile_lcdc_board_cfg *board_cfg;
  179. unsigned long tmp;
  180. int k, m;
  181. int ret = 0;
  182. #ifdef CONFIG_HAVE_CLK
  183. clk_enable(priv->clk);
  184. if (priv->dot_clk)
  185. clk_enable(priv->dot_clk);
  186. #endif
  187. /* reset */
  188. lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) | LCDC_RESET);
  189. lcdc_wait_bit(priv, _LDCNT2R, LCDC_RESET, 0);
  190. /* enable LCDC channels */
  191. tmp = lcdc_read(priv, _LDCNT2R);
  192. tmp |= priv->ch[0].enabled;
  193. tmp |= priv->ch[1].enabled;
  194. lcdc_write(priv, _LDCNT2R, tmp);
  195. /* read data from external memory, avoid using the BEU for now */
  196. lcdc_write(priv, _LDCNT2R, lcdc_read(priv, _LDCNT2R) & ~DISPLAY_BEU);
  197. /* stop the lcdc first */
  198. sh_mobile_lcdc_start_stop(priv, 0);
  199. /* configure clocks */
  200. tmp = priv->lddckr;
  201. for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  202. ch = &priv->ch[k];
  203. if (!priv->ch[k].enabled)
  204. continue;
  205. m = ch->cfg.clock_divider;
  206. if (!m)
  207. continue;
  208. if (m == 1)
  209. m = 1 << 6;
  210. tmp |= m << (lcdc_chan_is_sublcd(ch) ? 8 : 0);
  211. lcdc_write_chan(ch, LDDCKPAT1R, 0x00000000);
  212. lcdc_write_chan(ch, LDDCKPAT2R, (1 << (m/2)) - 1);
  213. }
  214. lcdc_write(priv, _LDDCKR, tmp);
  215. /* start dotclock again */
  216. lcdc_write(priv, _LDDCKSTPR, 0);
  217. lcdc_wait_bit(priv, _LDDCKSTPR, ~0, 0);
  218. /* interrupts are disabled */
  219. lcdc_write(priv, _LDINTR, 0);
  220. for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  221. ch = &priv->ch[k];
  222. lcd_cfg = &ch->cfg.lcd_cfg;
  223. if (!ch->enabled)
  224. continue;
  225. tmp = ch->ldmt1r_value;
  226. tmp |= (lcd_cfg->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : 1 << 28;
  227. tmp |= (lcd_cfg->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : 1 << 27;
  228. tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? 1 << 26 : 0;
  229. tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? 1 << 25 : 0;
  230. tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? 1 << 24 : 0;
  231. tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? 1 << 17 : 0;
  232. tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? 1 << 16 : 0;
  233. lcdc_write_chan(ch, LDMT1R, tmp);
  234. /* setup SYS bus */
  235. lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
  236. lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
  237. /* horizontal configuration */
  238. tmp = lcd_cfg->xres + lcd_cfg->hsync_len;
  239. tmp += lcd_cfg->left_margin;
  240. tmp += lcd_cfg->right_margin;
  241. tmp /= 8; /* HTCN */
  242. tmp |= (lcd_cfg->xres / 8) << 16; /* HDCN */
  243. lcdc_write_chan(ch, LDHCNR, tmp);
  244. tmp = lcd_cfg->xres;
  245. tmp += lcd_cfg->right_margin;
  246. tmp /= 8; /* HSYNP */
  247. tmp |= (lcd_cfg->hsync_len / 8) << 16; /* HSYNW */
  248. lcdc_write_chan(ch, LDHSYNR, tmp);
  249. /* power supply */
  250. lcdc_write_chan(ch, LDPMR, 0);
  251. /* vertical configuration */
  252. tmp = lcd_cfg->yres + lcd_cfg->vsync_len;
  253. tmp += lcd_cfg->upper_margin;
  254. tmp += lcd_cfg->lower_margin; /* VTLN */
  255. tmp |= lcd_cfg->yres << 16; /* VDLN */
  256. lcdc_write_chan(ch, LDVLNR, tmp);
  257. tmp = lcd_cfg->yres;
  258. tmp += lcd_cfg->lower_margin; /* VSYNP */
  259. tmp |= lcd_cfg->vsync_len << 16; /* VSYNW */
  260. lcdc_write_chan(ch, LDVSYNR, tmp);
  261. board_cfg = &ch->cfg.board_cfg;
  262. if (board_cfg->setup_sys)
  263. ret = board_cfg->setup_sys(board_cfg->board_data, ch,
  264. &sh_mobile_lcdc_sys_bus_ops);
  265. if (ret)
  266. return ret;
  267. }
  268. /* --- display_lcdc_data() --- */
  269. lcdc_write(priv, _LDINTR, 0x00000f00);
  270. /* word and long word swap */
  271. lcdc_write(priv, _LDDDSR, lcdc_read(priv, _LDDDSR) | 6);
  272. for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  273. ch = &priv->ch[k];
  274. if (!priv->ch[k].enabled)
  275. continue;
  276. /* set bpp format in PKF[4:0] */
  277. tmp = lcdc_read_chan(ch, LDDFR);
  278. tmp &= ~(0x0001001f);
  279. tmp |= (priv->ch[k].info.var.bits_per_pixel == 16) ? 3 : 0;
  280. lcdc_write_chan(ch, LDDFR, tmp);
  281. /* point out our frame buffer */
  282. lcdc_write_chan(ch, LDSA1R, ch->info.fix.smem_start);
  283. /* set line size */
  284. lcdc_write_chan(ch, LDMLSR, ch->info.fix.line_length);
  285. /* continuous read mode */
  286. lcdc_write_chan(ch, LDSM1R, 0);
  287. }
  288. /* display output */
  289. lcdc_write(priv, _LDCNT1R, LCDC_ENABLE);
  290. /* start the lcdc */
  291. sh_mobile_lcdc_start_stop(priv, 1);
  292. /* tell the board code to enable the panel */
  293. for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  294. ch = &priv->ch[k];
  295. board_cfg = &ch->cfg.board_cfg;
  296. if (board_cfg->display_on)
  297. board_cfg->display_on(board_cfg->board_data);
  298. }
  299. return 0;
  300. }
  301. static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
  302. {
  303. struct sh_mobile_lcdc_chan *ch;
  304. struct sh_mobile_lcdc_board_cfg *board_cfg;
  305. int k;
  306. /* tell the board code to disable the panel */
  307. for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
  308. ch = &priv->ch[k];
  309. board_cfg = &ch->cfg.board_cfg;
  310. if (board_cfg->display_off)
  311. board_cfg->display_off(board_cfg->board_data);
  312. }
  313. /* stop the lcdc */
  314. sh_mobile_lcdc_start_stop(priv, 0);
  315. #ifdef CONFIG_HAVE_CLK
  316. if (priv->dot_clk)
  317. clk_disable(priv->dot_clk);
  318. clk_disable(priv->clk);
  319. #endif
  320. }
  321. static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
  322. {
  323. int ifm, miftyp;
  324. switch (ch->cfg.interface_type) {
  325. case RGB8: ifm = 0; miftyp = 0; break;
  326. case RGB9: ifm = 0; miftyp = 4; break;
  327. case RGB12A: ifm = 0; miftyp = 5; break;
  328. case RGB12B: ifm = 0; miftyp = 6; break;
  329. case RGB16: ifm = 0; miftyp = 7; break;
  330. case RGB18: ifm = 0; miftyp = 10; break;
  331. case RGB24: ifm = 0; miftyp = 11; break;
  332. case SYS8A: ifm = 1; miftyp = 0; break;
  333. case SYS8B: ifm = 1; miftyp = 1; break;
  334. case SYS8C: ifm = 1; miftyp = 2; break;
  335. case SYS8D: ifm = 1; miftyp = 3; break;
  336. case SYS9: ifm = 1; miftyp = 4; break;
  337. case SYS12: ifm = 1; miftyp = 5; break;
  338. case SYS16A: ifm = 1; miftyp = 7; break;
  339. case SYS16B: ifm = 1; miftyp = 8; break;
  340. case SYS16C: ifm = 1; miftyp = 9; break;
  341. case SYS18: ifm = 1; miftyp = 10; break;
  342. case SYS24: ifm = 1; miftyp = 11; break;
  343. default: goto bad;
  344. }
  345. /* SUBLCD only supports SYS interface */
  346. if (lcdc_chan_is_sublcd(ch)) {
  347. if (ifm == 0)
  348. goto bad;
  349. else
  350. ifm = 0;
  351. }
  352. ch->ldmt1r_value = (ifm << 12) | miftyp;
  353. return 0;
  354. bad:
  355. return -EINVAL;
  356. }
  357. static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
  358. int clock_source,
  359. struct sh_mobile_lcdc_priv *priv)
  360. {
  361. #ifdef CONFIG_HAVE_CLK
  362. char clk_name[8];
  363. #endif
  364. char *str;
  365. int icksel;
  366. switch (clock_source) {
  367. case LCDC_CLK_BUS: str = "bus_clk"; icksel = 0; break;
  368. case LCDC_CLK_PERIPHERAL: str = "peripheral_clk"; icksel = 1; break;
  369. case LCDC_CLK_EXTERNAL: str = NULL; icksel = 2; break;
  370. default:
  371. return -EINVAL;
  372. }
  373. priv->lddckr = icksel << 16;
  374. #ifdef CONFIG_HAVE_CLK
  375. snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id);
  376. priv->clk = clk_get(&pdev->dev, clk_name);
  377. if (IS_ERR(priv->clk)) {
  378. dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name);
  379. return PTR_ERR(priv->clk);
  380. }
  381. if (str) {
  382. priv->dot_clk = clk_get(&pdev->dev, str);
  383. if (IS_ERR(priv->dot_clk)) {
  384. dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
  385. clk_put(priv->clk);
  386. return PTR_ERR(priv->dot_clk);
  387. }
  388. }
  389. #endif
  390. return 0;
  391. }
  392. static int sh_mobile_lcdc_setcolreg(u_int regno,
  393. u_int red, u_int green, u_int blue,
  394. u_int transp, struct fb_info *info)
  395. {
  396. u32 *palette = info->pseudo_palette;
  397. if (regno >= PALETTE_NR)
  398. return -EINVAL;
  399. /* only FB_VISUAL_TRUECOLOR supported */
  400. red >>= 16 - info->var.red.length;
  401. green >>= 16 - info->var.green.length;
  402. blue >>= 16 - info->var.blue.length;
  403. transp >>= 16 - info->var.transp.length;
  404. palette[regno] = (red << info->var.red.offset) |
  405. (green << info->var.green.offset) |
  406. (blue << info->var.blue.offset) |
  407. (transp << info->var.transp.offset);
  408. return 0;
  409. }
  410. static struct fb_fix_screeninfo sh_mobile_lcdc_fix = {
  411. .id = "SH Mobile LCDC",
  412. .type = FB_TYPE_PACKED_PIXELS,
  413. .visual = FB_VISUAL_TRUECOLOR,
  414. .accel = FB_ACCEL_NONE,
  415. };
  416. static struct fb_ops sh_mobile_lcdc_ops = {
  417. .fb_setcolreg = sh_mobile_lcdc_setcolreg,
  418. .fb_read = fb_sys_read,
  419. .fb_write = fb_sys_write,
  420. .fb_fillrect = sys_fillrect,
  421. .fb_copyarea = sys_copyarea,
  422. .fb_imageblit = sys_imageblit,
  423. };
  424. static int sh_mobile_lcdc_set_bpp(struct fb_var_screeninfo *var, int bpp)
  425. {
  426. switch (bpp) {
  427. case 16: /* PKF[4:0] = 00011 - RGB 565 */
  428. var->red.offset = 11;
  429. var->red.length = 5;
  430. var->green.offset = 5;
  431. var->green.length = 6;
  432. var->blue.offset = 0;
  433. var->blue.length = 5;
  434. var->transp.offset = 0;
  435. var->transp.length = 0;
  436. break;
  437. case 32: /* PKF[4:0] = 00000 - RGB 888
  438. * sh7722 pdf says 00RRGGBB but reality is GGBB00RR
  439. * this may be because LDDDSR has word swap enabled..
  440. */
  441. var->red.offset = 0;
  442. var->red.length = 8;
  443. var->green.offset = 24;
  444. var->green.length = 8;
  445. var->blue.offset = 16;
  446. var->blue.length = 8;
  447. var->transp.offset = 0;
  448. var->transp.length = 0;
  449. break;
  450. default:
  451. return -EINVAL;
  452. }
  453. var->bits_per_pixel = bpp;
  454. var->red.msb_right = 0;
  455. var->green.msb_right = 0;
  456. var->blue.msb_right = 0;
  457. var->transp.msb_right = 0;
  458. return 0;
  459. }
  460. static int sh_mobile_lcdc_remove(struct platform_device *pdev);
  461. static int __init sh_mobile_lcdc_probe(struct platform_device *pdev)
  462. {
  463. struct fb_info *info;
  464. struct sh_mobile_lcdc_priv *priv;
  465. struct sh_mobile_lcdc_info *pdata;
  466. struct sh_mobile_lcdc_chan_cfg *cfg;
  467. struct resource *res;
  468. int error;
  469. void *buf;
  470. int i, j;
  471. if (!pdev->dev.platform_data) {
  472. dev_err(&pdev->dev, "no platform data defined\n");
  473. error = -EINVAL;
  474. goto err0;
  475. }
  476. res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  477. if (res == NULL) {
  478. dev_err(&pdev->dev, "cannot find IO resource\n");
  479. error = -ENOENT;
  480. goto err0;
  481. }
  482. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  483. if (!priv) {
  484. dev_err(&pdev->dev, "cannot allocate device data\n");
  485. error = -ENOMEM;
  486. goto err0;
  487. }
  488. platform_set_drvdata(pdev, priv);
  489. pdata = pdev->dev.platform_data;
  490. j = 0;
  491. for (i = 0; i < ARRAY_SIZE(pdata->ch); i++) {
  492. priv->ch[j].lcdc = priv;
  493. memcpy(&priv->ch[j].cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
  494. error = sh_mobile_lcdc_check_interface(&priv->ch[i]);
  495. if (error) {
  496. dev_err(&pdev->dev, "unsupported interface type\n");
  497. goto err1;
  498. }
  499. switch (pdata->ch[i].chan) {
  500. case LCDC_CHAN_MAINLCD:
  501. priv->ch[j].enabled = 1 << 1;
  502. priv->ch[j].reg_offs = lcdc_offs_mainlcd;
  503. j++;
  504. break;
  505. case LCDC_CHAN_SUBLCD:
  506. priv->ch[j].enabled = 1 << 2;
  507. priv->ch[j].reg_offs = lcdc_offs_sublcd;
  508. j++;
  509. break;
  510. }
  511. }
  512. if (!j) {
  513. dev_err(&pdev->dev, "no channels defined\n");
  514. error = -EINVAL;
  515. goto err1;
  516. }
  517. error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
  518. if (error) {
  519. dev_err(&pdev->dev, "unable to setup clocks\n");
  520. goto err1;
  521. }
  522. priv->base = ioremap_nocache(res->start, (res->end - res->start) + 1);
  523. for (i = 0; i < j; i++) {
  524. info = &priv->ch[i].info;
  525. cfg = &priv->ch[i].cfg;
  526. info->fbops = &sh_mobile_lcdc_ops;
  527. info->var.xres = info->var.xres_virtual = cfg->lcd_cfg.xres;
  528. info->var.yres = info->var.yres_virtual = cfg->lcd_cfg.yres;
  529. info->var.width = cfg->lcd_size_cfg.width;
  530. info->var.height = cfg->lcd_size_cfg.height;
  531. info->var.activate = FB_ACTIVATE_NOW;
  532. error = sh_mobile_lcdc_set_bpp(&info->var, cfg->bpp);
  533. if (error)
  534. break;
  535. info->fix = sh_mobile_lcdc_fix;
  536. info->fix.line_length = cfg->lcd_cfg.xres * (cfg->bpp / 8);
  537. info->fix.smem_len = info->fix.line_length * cfg->lcd_cfg.yres;
  538. buf = dma_alloc_coherent(&pdev->dev, info->fix.smem_len,
  539. &priv->ch[i].dma_handle, GFP_KERNEL);
  540. if (!buf) {
  541. dev_err(&pdev->dev, "unable to allocate buffer\n");
  542. error = -ENOMEM;
  543. break;
  544. }
  545. info->pseudo_palette = &priv->ch[i].pseudo_palette;
  546. info->flags = FBINFO_FLAG_DEFAULT;
  547. error = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
  548. if (error < 0) {
  549. dev_err(&pdev->dev, "unable to allocate cmap\n");
  550. dma_free_coherent(&pdev->dev, info->fix.smem_len,
  551. buf, priv->ch[i].dma_handle);
  552. break;
  553. }
  554. memset(buf, 0, info->fix.smem_len);
  555. info->fix.smem_start = priv->ch[i].dma_handle;
  556. info->screen_base = buf;
  557. info->device = &pdev->dev;
  558. }
  559. if (error)
  560. goto err1;
  561. error = sh_mobile_lcdc_start(priv);
  562. if (error) {
  563. dev_err(&pdev->dev, "unable to start hardware\n");
  564. goto err1;
  565. }
  566. for (i = 0; i < j; i++) {
  567. error = register_framebuffer(&priv->ch[i].info);
  568. if (error < 0)
  569. goto err1;
  570. }
  571. for (i = 0; i < j; i++) {
  572. info = &priv->ch[i].info;
  573. dev_info(info->dev,
  574. "registered %s/%s as %dx%d %dbpp.\n",
  575. pdev->name,
  576. (priv->ch[i].cfg.chan == LCDC_CHAN_MAINLCD) ?
  577. "mainlcd" : "sublcd",
  578. (int) priv->ch[i].cfg.lcd_cfg.xres,
  579. (int) priv->ch[i].cfg.lcd_cfg.yres,
  580. priv->ch[i].cfg.bpp);
  581. }
  582. return 0;
  583. err1:
  584. sh_mobile_lcdc_remove(pdev);
  585. err0:
  586. return error;
  587. }
  588. static int sh_mobile_lcdc_remove(struct platform_device *pdev)
  589. {
  590. struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
  591. struct fb_info *info;
  592. int i;
  593. for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
  594. if (priv->ch[i].info.dev)
  595. unregister_framebuffer(&priv->ch[i].info);
  596. sh_mobile_lcdc_stop(priv);
  597. for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
  598. info = &priv->ch[i].info;
  599. if (!info->device)
  600. continue;
  601. dma_free_coherent(&pdev->dev, info->fix.smem_len,
  602. info->screen_base, priv->ch[i].dma_handle);
  603. fb_dealloc_cmap(&info->cmap);
  604. }
  605. #ifdef CONFIG_HAVE_CLK
  606. if (priv->dot_clk)
  607. clk_put(priv->dot_clk);
  608. clk_put(priv->clk);
  609. #endif
  610. if (priv->base)
  611. iounmap(priv->base);
  612. kfree(priv);
  613. return 0;
  614. }
  615. static struct platform_driver sh_mobile_lcdc_driver = {
  616. .driver = {
  617. .name = "sh_mobile_lcdc_fb",
  618. .owner = THIS_MODULE,
  619. },
  620. .probe = sh_mobile_lcdc_probe,
  621. .remove = sh_mobile_lcdc_remove,
  622. };
  623. static int __init sh_mobile_lcdc_init(void)
  624. {
  625. return platform_driver_register(&sh_mobile_lcdc_driver);
  626. }
  627. static void __exit sh_mobile_lcdc_exit(void)
  628. {
  629. platform_driver_unregister(&sh_mobile_lcdc_driver);
  630. }
  631. module_init(sh_mobile_lcdc_init);
  632. module_exit(sh_mobile_lcdc_exit);
  633. MODULE_DESCRIPTION("SuperH Mobile LCDC Framebuffer driver");
  634. MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
  635. MODULE_LICENSE("GPL v2");