PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/hw/display/pl110.c

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