pl110.c 13 KB

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