pl110.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. /*
  2. * Arm PrimeCell PL110 Color LCD Controller
  3. *
  4. * Copyright (c) 2005-2009 CodeSourcery.
  5. * Written by Paul Brook
  6. *
  7. * This code is licensed under the GNU LGPL
  8. */
  9. #include "sysbus.h"
  10. #include "console.h"
  11. #include "framebuffer.h"
  12. #define PL110_CR_EN 0x001
  13. #define PL110_CR_BGR 0x100
  14. #define PL110_CR_BEBO 0x200
  15. #define PL110_CR_BEPO 0x400
  16. #define PL110_CR_PWR 0x800
  17. enum pl110_bppmode
  18. {
  19. BPP_1,
  20. BPP_2,
  21. BPP_4,
  22. BPP_8,
  23. BPP_16,
  24. BPP_32,
  25. BPP_16_565, /* PL111 only */
  26. BPP_12 /* PL111 only */
  27. };
  28. /* The Versatile/PB uses a slightly modified PL110 controller. */
  29. enum pl110_version
  30. {
  31. PL110,
  32. PL110_VERSATILE,
  33. PL111
  34. };
  35. typedef struct {
  36. SysBusDevice busdev;
  37. DisplayState *ds;
  38. int version;
  39. uint32_t timing[4];
  40. uint32_t cr;
  41. uint32_t upbase;
  42. uint32_t lpbase;
  43. uint32_t int_status;
  44. uint32_t int_mask;
  45. int cols;
  46. int rows;
  47. enum pl110_bppmode bpp;
  48. int invalidate;
  49. uint32_t mux_ctrl;
  50. uint32_t pallette[256];
  51. uint32_t raw_pallette[128];
  52. qemu_irq irq;
  53. } pl110_state;
  54. static const VMStateDescription vmstate_pl110 = {
  55. .name = "pl110",
  56. .version_id = 2,
  57. .minimum_version_id = 1,
  58. .fields = (VMStateField[]) {
  59. VMSTATE_INT32(version, pl110_state),
  60. VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
  61. VMSTATE_UINT32(cr, pl110_state),
  62. VMSTATE_UINT32(upbase, pl110_state),
  63. VMSTATE_UINT32(lpbase, pl110_state),
  64. VMSTATE_UINT32(int_status, pl110_state),
  65. VMSTATE_UINT32(int_mask, pl110_state),
  66. VMSTATE_INT32(cols, pl110_state),
  67. VMSTATE_INT32(rows, pl110_state),
  68. VMSTATE_UINT32(bpp, pl110_state),
  69. VMSTATE_INT32(invalidate, pl110_state),
  70. VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256),
  71. VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128),
  72. VMSTATE_UINT32_V(mux_ctrl, pl110_state, 2),
  73. VMSTATE_END_OF_LIST()
  74. }
  75. };
  76. static const unsigned char pl110_id[] =
  77. { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  78. /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
  79. has a different ID. However Linux only looks for the normal ID. */
  80. #if 0
  81. static const unsigned char pl110_versatile_id[] =
  82. { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  83. #else
  84. #define pl110_versatile_id pl110_id
  85. #endif
  86. static const unsigned char pl111_id[] = {
  87. 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
  88. };
  89. /* Indexed by pl110_version */
  90. static const unsigned char *idregs[] = {
  91. pl110_id,
  92. pl110_versatile_id,
  93. pl111_id
  94. };
  95. #include "pixel_ops.h"
  96. #define BITS 8
  97. #include "pl110_template.h"
  98. #define BITS 15
  99. #include "pl110_template.h"
  100. #define BITS 16
  101. #include "pl110_template.h"
  102. #define BITS 24
  103. #include "pl110_template.h"
  104. #define BITS 32
  105. #include "pl110_template.h"
  106. static int pl110_enabled(pl110_state *s)
  107. {
  108. return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
  109. }
  110. static void pl110_update_display(void *opaque)
  111. {
  112. pl110_state *s = (pl110_state *)opaque;
  113. drawfn* fntable;
  114. drawfn fn;
  115. int dest_width;
  116. int src_width;
  117. int bpp_offset;
  118. int first;
  119. int last;
  120. if (!pl110_enabled(s))
  121. return;
  122. switch (ds_get_bits_per_pixel(s->ds)) {
  123. case 0:
  124. return;
  125. case 8:
  126. fntable = pl110_draw_fn_8;
  127. dest_width = 1;
  128. break;
  129. case 15:
  130. fntable = pl110_draw_fn_15;
  131. dest_width = 2;
  132. break;
  133. case 16:
  134. fntable = pl110_draw_fn_16;
  135. dest_width = 2;
  136. break;
  137. case 24:
  138. fntable = pl110_draw_fn_24;
  139. dest_width = 3;
  140. break;
  141. case 32:
  142. fntable = pl110_draw_fn_32;
  143. dest_width = 4;
  144. break;
  145. default:
  146. fprintf(stderr, "pl110: Bad color depth\n");
  147. exit(1);
  148. }
  149. if (s->cr & PL110_CR_BGR)
  150. bpp_offset = 0;
  151. else
  152. bpp_offset = 24;
  153. if ((s->version != PL111) && (s->bpp == BPP_16)) {
  154. /* The PL110's native 16 bit mode is 5551; however
  155. * most boards with a PL110 implement an external
  156. * mux which allows bits to be reshuffled to give
  157. * 565 format. The mux is typically controlled by
  158. * an external system register.
  159. * This is controlled by a GPIO input pin
  160. * so boards can wire it up to their register.
  161. *
  162. * The PL111 straightforwardly implements both
  163. * 5551 and 565 under control of the bpp field
  164. * in the LCDControl register.
  165. */
  166. switch (s->mux_ctrl) {
  167. case 3: /* 565 BGR */
  168. bpp_offset = (BPP_16_565 - BPP_16);
  169. break;
  170. case 1: /* 5551 */
  171. break;
  172. case 0: /* 888; also if we have loaded vmstate from an old version */
  173. case 2: /* 565 RGB */
  174. default:
  175. /* treat as 565 but honour BGR bit */
  176. bpp_offset += (BPP_16_565 - BPP_16);
  177. break;
  178. }
  179. }
  180. if (s->cr & PL110_CR_BEBO)
  181. fn = fntable[s->bpp + 8 + bpp_offset];
  182. else if (s->cr & PL110_CR_BEPO)
  183. fn = fntable[s->bpp + 16 + bpp_offset];
  184. else
  185. fn = fntable[s->bpp + bpp_offset];
  186. src_width = s->cols;
  187. switch (s->bpp) {
  188. case BPP_1:
  189. src_width >>= 3;
  190. break;
  191. case BPP_2:
  192. src_width >>= 2;
  193. break;
  194. case BPP_4:
  195. src_width >>= 1;
  196. break;
  197. case BPP_8:
  198. break;
  199. case BPP_16:
  200. case BPP_16_565:
  201. case BPP_12:
  202. src_width <<= 1;
  203. break;
  204. case BPP_32:
  205. src_width <<= 2;
  206. break;
  207. }
  208. dest_width *= s->cols;
  209. first = 0;
  210. framebuffer_update_display(s->ds,
  211. s->upbase, s->cols, s->rows,
  212. src_width, dest_width, 0,
  213. s->invalidate,
  214. fn, s->pallette,
  215. &first, &last);
  216. if (first >= 0) {
  217. dpy_update(s->ds, 0, first, s->cols, last - first + 1);
  218. }
  219. s->invalidate = 0;
  220. }
  221. static void pl110_invalidate_display(void * opaque)
  222. {
  223. pl110_state *s = (pl110_state *)opaque;
  224. s->invalidate = 1;
  225. if (pl110_enabled(s)) {
  226. qemu_console_resize(s->ds, s->cols, s->rows);
  227. }
  228. }
  229. static void pl110_update_pallette(pl110_state *s, int n)
  230. {
  231. int i;
  232. uint32_t raw;
  233. unsigned int r, g, b;
  234. raw = s->raw_pallette[n];
  235. n <<= 1;
  236. for (i = 0; i < 2; i++) {
  237. r = (raw & 0x1f) << 3;
  238. raw >>= 5;
  239. g = (raw & 0x1f) << 3;
  240. raw >>= 5;
  241. b = (raw & 0x1f) << 3;
  242. /* The I bit is ignored. */
  243. raw >>= 6;
  244. switch (ds_get_bits_per_pixel(s->ds)) {
  245. case 8:
  246. s->pallette[n] = rgb_to_pixel8(r, g, b);
  247. break;
  248. case 15:
  249. s->pallette[n] = rgb_to_pixel15(r, g, b);
  250. break;
  251. case 16:
  252. s->pallette[n] = rgb_to_pixel16(r, g, b);
  253. break;
  254. case 24:
  255. case 32:
  256. s->pallette[n] = rgb_to_pixel32(r, g, b);
  257. break;
  258. }
  259. n++;
  260. }
  261. }
  262. static void pl110_resize(pl110_state *s, int width, int height)
  263. {
  264. if (width != s->cols || height != s->rows) {
  265. if (pl110_enabled(s)) {
  266. qemu_console_resize(s->ds, width, height);
  267. }
  268. }
  269. s->cols = width;
  270. s->rows = height;
  271. }
  272. /* Update interrupts. */
  273. static void pl110_update(pl110_state *s)
  274. {
  275. /* TODO: Implement interrupts. */
  276. }
  277. static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
  278. {
  279. pl110_state *s = (pl110_state *)opaque;
  280. if (offset >= 0xfe0 && offset < 0x1000) {
  281. return idregs[s->version][(offset - 0xfe0) >> 2];
  282. }
  283. if (offset >= 0x200 && offset < 0x400) {
  284. return s->raw_pallette[(offset - 0x200) >> 2];
  285. }
  286. switch (offset >> 2) {
  287. case 0: /* LCDTiming0 */
  288. return s->timing[0];
  289. case 1: /* LCDTiming1 */
  290. return s->timing[1];
  291. case 2: /* LCDTiming2 */
  292. return s->timing[2];
  293. case 3: /* LCDTiming3 */
  294. return s->timing[3];
  295. case 4: /* LCDUPBASE */
  296. return s->upbase;
  297. case 5: /* LCDLPBASE */
  298. return s->lpbase;
  299. case 6: /* LCDIMSC */
  300. if (s->version != PL110) {
  301. return s->cr;
  302. }
  303. return s->int_mask;
  304. case 7: /* LCDControl */
  305. if (s->version != PL110) {
  306. return s->int_mask;
  307. }
  308. return s->cr;
  309. case 8: /* LCDRIS */
  310. return s->int_status;
  311. case 9: /* LCDMIS */
  312. return s->int_status & s->int_mask;
  313. case 11: /* LCDUPCURR */
  314. /* TODO: Implement vertical refresh. */
  315. return s->upbase;
  316. case 12: /* LCDLPCURR */
  317. return s->lpbase;
  318. default:
  319. hw_error("pl110_read: Bad offset %x\n", (int)offset);
  320. return 0;
  321. }
  322. }
  323. static void pl110_write(void *opaque, target_phys_addr_t offset,
  324. uint32_t val)
  325. {
  326. pl110_state *s = (pl110_state *)opaque;
  327. int n;
  328. /* For simplicity invalidate the display whenever a control register
  329. is writen to. */
  330. s->invalidate = 1;
  331. if (offset >= 0x200 && offset < 0x400) {
  332. /* Pallette. */
  333. n = (offset - 0x200) >> 2;
  334. s->raw_pallette[(offset - 0x200) >> 2] = val;
  335. pl110_update_pallette(s, n);
  336. return;
  337. }
  338. switch (offset >> 2) {
  339. case 0: /* LCDTiming0 */
  340. s->timing[0] = val;
  341. n = ((val & 0xfc) + 4) * 4;
  342. pl110_resize(s, n, s->rows);
  343. break;
  344. case 1: /* LCDTiming1 */
  345. s->timing[1] = val;
  346. n = (val & 0x3ff) + 1;
  347. pl110_resize(s, s->cols, n);
  348. break;
  349. case 2: /* LCDTiming2 */
  350. s->timing[2] = val;
  351. break;
  352. case 3: /* LCDTiming3 */
  353. s->timing[3] = val;
  354. break;
  355. case 4: /* LCDUPBASE */
  356. s->upbase = val;
  357. break;
  358. case 5: /* LCDLPBASE */
  359. s->lpbase = val;
  360. break;
  361. case 6: /* LCDIMSC */
  362. if (s->version != PL110) {
  363. goto control;
  364. }
  365. imsc:
  366. s->int_mask = val;
  367. pl110_update(s);
  368. break;
  369. case 7: /* LCDControl */
  370. if (s->version != PL110) {
  371. goto imsc;
  372. }
  373. control:
  374. s->cr = val;
  375. s->bpp = (val >> 1) & 7;
  376. if (pl110_enabled(s)) {
  377. qemu_console_resize(s->ds, s->cols, s->rows);
  378. }
  379. break;
  380. case 10: /* LCDICR */
  381. s->int_status &= ~val;
  382. pl110_update(s);
  383. break;
  384. default:
  385. hw_error("pl110_write: Bad offset %x\n", (int)offset);
  386. }
  387. }
  388. static CPUReadMemoryFunc * const pl110_readfn[] = {
  389. pl110_read,
  390. pl110_read,
  391. pl110_read
  392. };
  393. static CPUWriteMemoryFunc * const pl110_writefn[] = {
  394. pl110_write,
  395. pl110_write,
  396. pl110_write
  397. };
  398. static void pl110_mux_ctrl_set(void *opaque, int line, int level)
  399. {
  400. pl110_state *s = (pl110_state *)opaque;
  401. s->mux_ctrl = level;
  402. }
  403. static int pl110_init(SysBusDevice *dev)
  404. {
  405. pl110_state *s = FROM_SYSBUS(pl110_state, dev);
  406. int iomemtype;
  407. iomemtype = cpu_register_io_memory(pl110_readfn,
  408. pl110_writefn, s,
  409. DEVICE_NATIVE_ENDIAN);
  410. sysbus_init_mmio(dev, 0x1000, iomemtype);
  411. sysbus_init_irq(dev, &s->irq);
  412. qdev_init_gpio_in(&s->busdev.qdev, pl110_mux_ctrl_set, 1);
  413. s->ds = graphic_console_init(pl110_update_display,
  414. pl110_invalidate_display,
  415. NULL, NULL, s);
  416. return 0;
  417. }
  418. static int pl110_versatile_init(SysBusDevice *dev)
  419. {
  420. pl110_state *s = FROM_SYSBUS(pl110_state, dev);
  421. s->version = PL110_VERSATILE;
  422. return pl110_init(dev);
  423. }
  424. static int pl111_init(SysBusDevice *dev)
  425. {
  426. pl110_state *s = FROM_SYSBUS(pl110_state, dev);
  427. s->version = PL111;
  428. return pl110_init(dev);
  429. }
  430. static SysBusDeviceInfo pl110_info = {
  431. .init = pl110_init,
  432. .qdev.name = "pl110",
  433. .qdev.size = sizeof(pl110_state),
  434. .qdev.vmsd = &vmstate_pl110,
  435. .qdev.no_user = 1,
  436. };
  437. static SysBusDeviceInfo pl110_versatile_info = {
  438. .init = pl110_versatile_init,
  439. .qdev.name = "pl110_versatile",
  440. .qdev.size = sizeof(pl110_state),
  441. .qdev.vmsd = &vmstate_pl110,
  442. .qdev.no_user = 1,
  443. };
  444. static SysBusDeviceInfo pl111_info = {
  445. .init = pl111_init,
  446. .qdev.name = "pl111",
  447. .qdev.size = sizeof(pl110_state),
  448. .qdev.vmsd = &vmstate_pl110,
  449. .qdev.no_user = 1,
  450. };
  451. static void pl110_register_devices(void)
  452. {
  453. sysbus_register_withprop(&pl110_info);
  454. sysbus_register_withprop(&pl110_versatile_info);
  455. sysbus_register_withprop(&pl111_info);
  456. }
  457. device_init(pl110_register_devices)