pl110.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  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 "qemu/osdep.h"
  10. #include "hw/irq.h"
  11. #include "hw/sysbus.h"
  12. #include "hw/qdev-properties.h"
  13. #include "migration/vmstate.h"
  14. #include "ui/console.h"
  15. #include "framebuffer.h"
  16. #include "ui/pixel_ops.h"
  17. #include "qemu/timer.h"
  18. #include "qemu/log.h"
  19. #include "qemu/module.h"
  20. #include "qapi/error.h"
  21. #include "qom/object.h"
  22. #define PL110_CR_EN 0x001
  23. #define PL110_CR_BGR 0x100
  24. #define PL110_CR_BEBO 0x200
  25. #define PL110_CR_BEPO 0x400
  26. #define PL110_CR_PWR 0x800
  27. #define PL110_IE_NB 0x004
  28. #define PL110_IE_VC 0x008
  29. enum pl110_bppmode
  30. {
  31. BPP_1,
  32. BPP_2,
  33. BPP_4,
  34. BPP_8,
  35. BPP_16,
  36. BPP_32,
  37. BPP_16_565, /* PL111 only */
  38. BPP_12 /* PL111 only */
  39. };
  40. /* The Versatile/PB uses a slightly modified PL110 controller. */
  41. enum pl110_version
  42. {
  43. VERSION_PL110,
  44. VERSION_PL110_VERSATILE,
  45. VERSION_PL111
  46. };
  47. #define TYPE_PL110 "pl110"
  48. OBJECT_DECLARE_SIMPLE_TYPE(PL110State, PL110)
  49. struct PL110State {
  50. SysBusDevice parent_obj;
  51. MemoryRegion iomem;
  52. MemoryRegionSection fbsection;
  53. QemuConsole *con;
  54. QEMUTimer *vblank_timer;
  55. int version;
  56. uint32_t timing[4];
  57. uint32_t cr;
  58. uint32_t upbase;
  59. uint32_t lpbase;
  60. uint32_t int_status;
  61. uint32_t int_mask;
  62. int cols;
  63. int rows;
  64. enum pl110_bppmode bpp;
  65. int invalidate;
  66. uint32_t mux_ctrl;
  67. uint32_t palette[256];
  68. uint32_t raw_palette[128];
  69. qemu_irq irq;
  70. MemoryRegion *fbmem;
  71. };
  72. static int vmstate_pl110_post_load(void *opaque, int version_id);
  73. static const VMStateDescription vmstate_pl110 = {
  74. .name = "pl110",
  75. .version_id = 2,
  76. .minimum_version_id = 1,
  77. .post_load = vmstate_pl110_post_load,
  78. .fields = (const VMStateField[]) {
  79. VMSTATE_INT32(version, PL110State),
  80. VMSTATE_UINT32_ARRAY(timing, PL110State, 4),
  81. VMSTATE_UINT32(cr, PL110State),
  82. VMSTATE_UINT32(upbase, PL110State),
  83. VMSTATE_UINT32(lpbase, PL110State),
  84. VMSTATE_UINT32(int_status, PL110State),
  85. VMSTATE_UINT32(int_mask, PL110State),
  86. VMSTATE_INT32(cols, PL110State),
  87. VMSTATE_INT32(rows, PL110State),
  88. VMSTATE_UINT32(bpp, PL110State),
  89. VMSTATE_INT32(invalidate, PL110State),
  90. VMSTATE_UINT32_ARRAY(palette, PL110State, 256),
  91. VMSTATE_UINT32_ARRAY(raw_palette, PL110State, 128),
  92. VMSTATE_UINT32_V(mux_ctrl, PL110State, 2),
  93. VMSTATE_END_OF_LIST()
  94. }
  95. };
  96. static const unsigned char pl110_id[] =
  97. { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  98. static const unsigned char pl111_id[] = {
  99. 0x11, 0x11, 0x24, 0x00, 0x0d, 0xf0, 0x05, 0xb1
  100. };
  101. /* Indexed by pl110_version */
  102. static const unsigned char *idregs[] = {
  103. pl110_id,
  104. /* The ARM documentation (DDI0224C) says the CLCDC on the Versatile board
  105. * has a different ID (0x93, 0x10, 0x04, 0x00, ...). However the hardware
  106. * itself has the same ID values as a stock PL110, and guests (in
  107. * particular Linux) rely on this. We emulate what the hardware does,
  108. * rather than what the docs claim it ought to do.
  109. */
  110. pl110_id,
  111. pl111_id
  112. };
  113. #define COPY_PIXEL(to, from) do { *(uint32_t *)to = from; to += 4; } while (0)
  114. #undef RGB
  115. #define BORDER bgr
  116. #define ORDER 0
  117. #include "pl110_template.h"
  118. #define ORDER 1
  119. #include "pl110_template.h"
  120. #define ORDER 2
  121. #include "pl110_template.h"
  122. #undef BORDER
  123. #define RGB
  124. #define BORDER rgb
  125. #define ORDER 0
  126. #include "pl110_template.h"
  127. #define ORDER 1
  128. #include "pl110_template.h"
  129. #define ORDER 2
  130. #include "pl110_template.h"
  131. #undef BORDER
  132. #undef COPY_PIXEL
  133. static drawfn pl110_draw_fn_32[48] = {
  134. pl110_draw_line1_lblp_bgr,
  135. pl110_draw_line2_lblp_bgr,
  136. pl110_draw_line4_lblp_bgr,
  137. pl110_draw_line8_lblp_bgr,
  138. pl110_draw_line16_555_lblp_bgr,
  139. pl110_draw_line32_lblp_bgr,
  140. pl110_draw_line16_lblp_bgr,
  141. pl110_draw_line12_lblp_bgr,
  142. pl110_draw_line1_bbbp_bgr,
  143. pl110_draw_line2_bbbp_bgr,
  144. pl110_draw_line4_bbbp_bgr,
  145. pl110_draw_line8_bbbp_bgr,
  146. pl110_draw_line16_555_bbbp_bgr,
  147. pl110_draw_line32_bbbp_bgr,
  148. pl110_draw_line16_bbbp_bgr,
  149. pl110_draw_line12_bbbp_bgr,
  150. pl110_draw_line1_lbbp_bgr,
  151. pl110_draw_line2_lbbp_bgr,
  152. pl110_draw_line4_lbbp_bgr,
  153. pl110_draw_line8_lbbp_bgr,
  154. pl110_draw_line16_555_lbbp_bgr,
  155. pl110_draw_line32_lbbp_bgr,
  156. pl110_draw_line16_lbbp_bgr,
  157. pl110_draw_line12_lbbp_bgr,
  158. pl110_draw_line1_lblp_rgb,
  159. pl110_draw_line2_lblp_rgb,
  160. pl110_draw_line4_lblp_rgb,
  161. pl110_draw_line8_lblp_rgb,
  162. pl110_draw_line16_555_lblp_rgb,
  163. pl110_draw_line32_lblp_rgb,
  164. pl110_draw_line16_lblp_rgb,
  165. pl110_draw_line12_lblp_rgb,
  166. pl110_draw_line1_bbbp_rgb,
  167. pl110_draw_line2_bbbp_rgb,
  168. pl110_draw_line4_bbbp_rgb,
  169. pl110_draw_line8_bbbp_rgb,
  170. pl110_draw_line16_555_bbbp_rgb,
  171. pl110_draw_line32_bbbp_rgb,
  172. pl110_draw_line16_bbbp_rgb,
  173. pl110_draw_line12_bbbp_rgb,
  174. pl110_draw_line1_lbbp_rgb,
  175. pl110_draw_line2_lbbp_rgb,
  176. pl110_draw_line4_lbbp_rgb,
  177. pl110_draw_line8_lbbp_rgb,
  178. pl110_draw_line16_555_lbbp_rgb,
  179. pl110_draw_line32_lbbp_rgb,
  180. pl110_draw_line16_lbbp_rgb,
  181. pl110_draw_line12_lbbp_rgb,
  182. };
  183. static int pl110_enabled(PL110State *s)
  184. {
  185. return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
  186. }
  187. static void pl110_update_display(void *opaque)
  188. {
  189. PL110State *s = (PL110State *)opaque;
  190. DisplaySurface *surface = qemu_console_surface(s->con);
  191. drawfn fn;
  192. int src_width;
  193. int bpp_offset;
  194. int first;
  195. int last;
  196. if (!pl110_enabled(s)) {
  197. return;
  198. }
  199. if (s->cr & PL110_CR_BGR)
  200. bpp_offset = 0;
  201. else
  202. bpp_offset = 24;
  203. if ((s->version != VERSION_PL111) && (s->bpp == BPP_16)) {
  204. /* The PL110's native 16 bit mode is 5551; however
  205. * most boards with a PL110 implement an external
  206. * mux which allows bits to be reshuffled to give
  207. * 565 format. The mux is typically controlled by
  208. * an external system register.
  209. * This is controlled by a GPIO input pin
  210. * so boards can wire it up to their register.
  211. *
  212. * The PL111 straightforwardly implements both
  213. * 5551 and 565 under control of the bpp field
  214. * in the LCDControl register.
  215. */
  216. switch (s->mux_ctrl) {
  217. case 3: /* 565 BGR */
  218. bpp_offset = (BPP_16_565 - BPP_16);
  219. break;
  220. case 1: /* 5551 */
  221. break;
  222. case 0: /* 888; also if we have loaded vmstate from an old version */
  223. case 2: /* 565 RGB */
  224. default:
  225. /* treat as 565 but honour BGR bit */
  226. bpp_offset += (BPP_16_565 - BPP_16);
  227. break;
  228. }
  229. }
  230. if (s->cr & PL110_CR_BEBO) {
  231. fn = pl110_draw_fn_32[s->bpp + 8 + bpp_offset];
  232. } else if (s->cr & PL110_CR_BEPO) {
  233. fn = pl110_draw_fn_32[s->bpp + 16 + bpp_offset];
  234. } else {
  235. fn = pl110_draw_fn_32[s->bpp + bpp_offset];
  236. }
  237. src_width = s->cols;
  238. switch (s->bpp) {
  239. case BPP_1:
  240. src_width >>= 3;
  241. break;
  242. case BPP_2:
  243. src_width >>= 2;
  244. break;
  245. case BPP_4:
  246. src_width >>= 1;
  247. break;
  248. case BPP_8:
  249. break;
  250. case BPP_16:
  251. case BPP_16_565:
  252. case BPP_12:
  253. src_width <<= 1;
  254. break;
  255. case BPP_32:
  256. src_width <<= 2;
  257. break;
  258. }
  259. first = 0;
  260. if (s->invalidate) {
  261. framebuffer_update_memory_section(&s->fbsection,
  262. s->fbmem,
  263. s->upbase,
  264. s->rows, src_width);
  265. }
  266. framebuffer_update_display(surface, &s->fbsection,
  267. s->cols, s->rows,
  268. src_width, s->cols * 4, 0,
  269. s->invalidate,
  270. fn, s->palette,
  271. &first, &last);
  272. if (first >= 0) {
  273. dpy_gfx_update(s->con, 0, first, s->cols, last - first + 1);
  274. }
  275. s->invalidate = 0;
  276. }
  277. static void pl110_invalidate_display(void * opaque)
  278. {
  279. PL110State *s = (PL110State *)opaque;
  280. s->invalidate = 1;
  281. if (pl110_enabled(s)) {
  282. qemu_console_resize(s->con, s->cols, s->rows);
  283. }
  284. }
  285. static void pl110_update_palette(PL110State *s, int n)
  286. {
  287. DisplaySurface *surface = qemu_console_surface(s->con);
  288. int i;
  289. uint32_t raw;
  290. unsigned int r, g, b;
  291. raw = s->raw_palette[n];
  292. n <<= 1;
  293. for (i = 0; i < 2; i++) {
  294. r = (raw & 0x1f) << 3;
  295. raw >>= 5;
  296. g = (raw & 0x1f) << 3;
  297. raw >>= 5;
  298. b = (raw & 0x1f) << 3;
  299. /* The I bit is ignored. */
  300. raw >>= 6;
  301. switch (surface_bits_per_pixel(surface)) {
  302. case 8:
  303. s->palette[n] = rgb_to_pixel8(r, g, b);
  304. break;
  305. case 15:
  306. s->palette[n] = rgb_to_pixel15(r, g, b);
  307. break;
  308. case 16:
  309. s->palette[n] = rgb_to_pixel16(r, g, b);
  310. break;
  311. case 24:
  312. case 32:
  313. s->palette[n] = rgb_to_pixel32(r, g, b);
  314. break;
  315. }
  316. n++;
  317. }
  318. }
  319. static void pl110_resize(PL110State *s, int width, int height)
  320. {
  321. if (width != s->cols || height != s->rows) {
  322. if (pl110_enabled(s)) {
  323. qemu_console_resize(s->con, width, height);
  324. }
  325. }
  326. s->cols = width;
  327. s->rows = height;
  328. }
  329. /* Update interrupts. */
  330. static void pl110_update(PL110State *s)
  331. {
  332. /* Raise IRQ if enabled and any status bit is 1 */
  333. if (s->int_status & s->int_mask) {
  334. qemu_irq_raise(s->irq);
  335. } else {
  336. qemu_irq_lower(s->irq);
  337. }
  338. }
  339. static void pl110_vblank_interrupt(void *opaque)
  340. {
  341. PL110State *s = opaque;
  342. /* Fire the vertical compare and next base IRQs and re-arm */
  343. s->int_status |= (PL110_IE_NB | PL110_IE_VC);
  344. timer_mod(s->vblank_timer,
  345. qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
  346. NANOSECONDS_PER_SECOND / 60);
  347. pl110_update(s);
  348. }
  349. static uint64_t pl110_read(void *opaque, hwaddr offset,
  350. unsigned size)
  351. {
  352. PL110State *s = (PL110State *)opaque;
  353. if (offset >= 0xfe0 && offset < 0x1000) {
  354. return idregs[s->version][(offset - 0xfe0) >> 2];
  355. }
  356. if (offset >= 0x200 && offset < 0x400) {
  357. return s->raw_palette[(offset - 0x200) >> 2];
  358. }
  359. switch (offset >> 2) {
  360. case 0: /* LCDTiming0 */
  361. return s->timing[0];
  362. case 1: /* LCDTiming1 */
  363. return s->timing[1];
  364. case 2: /* LCDTiming2 */
  365. return s->timing[2];
  366. case 3: /* LCDTiming3 */
  367. return s->timing[3];
  368. case 4: /* LCDUPBASE */
  369. return s->upbase;
  370. case 5: /* LCDLPBASE */
  371. return s->lpbase;
  372. case 6: /* LCDIMSC */
  373. if (s->version != VERSION_PL110) {
  374. return s->cr;
  375. }
  376. return s->int_mask;
  377. case 7: /* LCDControl */
  378. if (s->version != VERSION_PL110) {
  379. return s->int_mask;
  380. }
  381. return s->cr;
  382. case 8: /* LCDRIS */
  383. return s->int_status;
  384. case 9: /* LCDMIS */
  385. return s->int_status & s->int_mask;
  386. case 11: /* LCDUPCURR */
  387. /* TODO: Implement vertical refresh. */
  388. return s->upbase;
  389. case 12: /* LCDLPCURR */
  390. return s->lpbase;
  391. default:
  392. qemu_log_mask(LOG_GUEST_ERROR,
  393. "pl110_read: Bad offset %x\n", (int)offset);
  394. return 0;
  395. }
  396. }
  397. static void pl110_write(void *opaque, hwaddr offset,
  398. uint64_t val, unsigned size)
  399. {
  400. PL110State *s = (PL110State *)opaque;
  401. int n;
  402. /* For simplicity invalidate the display whenever a control register
  403. is written to. */
  404. s->invalidate = 1;
  405. if (offset >= 0x200 && offset < 0x400) {
  406. /* Palette. */
  407. n = (offset - 0x200) >> 2;
  408. s->raw_palette[(offset - 0x200) >> 2] = val;
  409. pl110_update_palette(s, n);
  410. return;
  411. }
  412. switch (offset >> 2) {
  413. case 0: /* LCDTiming0 */
  414. s->timing[0] = val;
  415. n = ((val & 0xfc) + 4) * 4;
  416. pl110_resize(s, n, s->rows);
  417. break;
  418. case 1: /* LCDTiming1 */
  419. s->timing[1] = val;
  420. n = (val & 0x3ff) + 1;
  421. pl110_resize(s, s->cols, n);
  422. break;
  423. case 2: /* LCDTiming2 */
  424. s->timing[2] = val;
  425. break;
  426. case 3: /* LCDTiming3 */
  427. s->timing[3] = val;
  428. break;
  429. case 4: /* LCDUPBASE */
  430. s->upbase = val;
  431. break;
  432. case 5: /* LCDLPBASE */
  433. s->lpbase = val;
  434. break;
  435. case 6: /* LCDIMSC */
  436. if (s->version != VERSION_PL110) {
  437. goto control;
  438. }
  439. imsc:
  440. s->int_mask = val;
  441. pl110_update(s);
  442. break;
  443. case 7: /* LCDControl */
  444. if (s->version != VERSION_PL110) {
  445. goto imsc;
  446. }
  447. control:
  448. s->cr = val;
  449. s->bpp = (val >> 1) & 7;
  450. if (pl110_enabled(s)) {
  451. qemu_console_resize(s->con, s->cols, s->rows);
  452. timer_mod(s->vblank_timer,
  453. qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) +
  454. NANOSECONDS_PER_SECOND / 60);
  455. } else {
  456. timer_del(s->vblank_timer);
  457. }
  458. break;
  459. case 10: /* LCDICR */
  460. s->int_status &= ~val;
  461. pl110_update(s);
  462. break;
  463. default:
  464. qemu_log_mask(LOG_GUEST_ERROR,
  465. "pl110_write: Bad offset %x\n", (int)offset);
  466. }
  467. }
  468. static const MemoryRegionOps pl110_ops = {
  469. .read = pl110_read,
  470. .write = pl110_write,
  471. .endianness = DEVICE_NATIVE_ENDIAN,
  472. };
  473. static void pl110_mux_ctrl_set(void *opaque, int line, int level)
  474. {
  475. PL110State *s = (PL110State *)opaque;
  476. s->mux_ctrl = level;
  477. }
  478. static int vmstate_pl110_post_load(void *opaque, int version_id)
  479. {
  480. PL110State *s = opaque;
  481. /* Make sure we redraw, and at the right size */
  482. pl110_invalidate_display(s);
  483. return 0;
  484. }
  485. static const GraphicHwOps pl110_gfx_ops = {
  486. .invalidate = pl110_invalidate_display,
  487. .gfx_update = pl110_update_display,
  488. };
  489. static const Property pl110_properties[] = {
  490. DEFINE_PROP_LINK("framebuffer-memory", PL110State, fbmem,
  491. TYPE_MEMORY_REGION, MemoryRegion *),
  492. };
  493. static void pl110_realize(DeviceState *dev, Error **errp)
  494. {
  495. PL110State *s = PL110(dev);
  496. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  497. if (!s->fbmem) {
  498. error_setg(errp, "'framebuffer-memory' property was not set");
  499. return;
  500. }
  501. memory_region_init_io(&s->iomem, OBJECT(s), &pl110_ops, s, "pl110", 0x1000);
  502. sysbus_init_mmio(sbd, &s->iomem);
  503. sysbus_init_irq(sbd, &s->irq);
  504. s->vblank_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
  505. pl110_vblank_interrupt, s);
  506. qdev_init_gpio_in(dev, pl110_mux_ctrl_set, 1);
  507. s->con = graphic_console_init(dev, 0, &pl110_gfx_ops, s);
  508. }
  509. static void pl110_init(Object *obj)
  510. {
  511. PL110State *s = PL110(obj);
  512. s->version = VERSION_PL110;
  513. }
  514. static void pl110_versatile_init(Object *obj)
  515. {
  516. PL110State *s = PL110(obj);
  517. s->version = VERSION_PL110_VERSATILE;
  518. }
  519. static void pl111_init(Object *obj)
  520. {
  521. PL110State *s = PL110(obj);
  522. s->version = VERSION_PL111;
  523. }
  524. static void pl110_class_init(ObjectClass *klass, void *data)
  525. {
  526. DeviceClass *dc = DEVICE_CLASS(klass);
  527. set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
  528. dc->vmsd = &vmstate_pl110;
  529. dc->realize = pl110_realize;
  530. device_class_set_props(dc, pl110_properties);
  531. }
  532. static const TypeInfo pl110_info = {
  533. .name = TYPE_PL110,
  534. .parent = TYPE_SYS_BUS_DEVICE,
  535. .instance_size = sizeof(PL110State),
  536. .instance_init = pl110_init,
  537. .class_init = pl110_class_init,
  538. };
  539. static const TypeInfo pl110_versatile_info = {
  540. .name = "pl110_versatile",
  541. .parent = TYPE_PL110,
  542. .instance_init = pl110_versatile_init,
  543. };
  544. static const TypeInfo pl111_info = {
  545. .name = "pl111",
  546. .parent = TYPE_PL110,
  547. .instance_init = pl111_init,
  548. };
  549. static void pl110_register_types(void)
  550. {
  551. type_register_static(&pl110_info);
  552. type_register_static(&pl110_versatile_info);
  553. type_register_static(&pl111_info);
  554. }
  555. type_init(pl110_register_types)