pl110.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. };
  26. typedef struct {
  27. SysBusDevice busdev;
  28. DisplayState *ds;
  29. /* The Versatile/PB uses a slightly modified PL110 controller. */
  30. int versatile;
  31. uint32_t timing[4];
  32. uint32_t cr;
  33. uint32_t upbase;
  34. uint32_t lpbase;
  35. uint32_t int_status;
  36. uint32_t int_mask;
  37. int cols;
  38. int rows;
  39. enum pl110_bppmode bpp;
  40. int invalidate;
  41. uint32_t pallette[256];
  42. uint32_t raw_pallette[128];
  43. qemu_irq irq;
  44. } pl110_state;
  45. static const VMStateDescription vmstate_pl110 = {
  46. .name = "pl110",
  47. .version_id = 1,
  48. .minimum_version_id = 1,
  49. .fields = (VMStateField[]) {
  50. VMSTATE_INT32(versatile, pl110_state),
  51. VMSTATE_UINT32_ARRAY(timing, pl110_state, 4),
  52. VMSTATE_UINT32(cr, pl110_state),
  53. VMSTATE_UINT32(upbase, pl110_state),
  54. VMSTATE_UINT32(lpbase, pl110_state),
  55. VMSTATE_UINT32(int_status, pl110_state),
  56. VMSTATE_UINT32(int_mask, pl110_state),
  57. VMSTATE_INT32(cols, pl110_state),
  58. VMSTATE_INT32(rows, pl110_state),
  59. VMSTATE_UINT32(bpp, pl110_state),
  60. VMSTATE_INT32(invalidate, pl110_state),
  61. VMSTATE_UINT32_ARRAY(pallette, pl110_state, 256),
  62. VMSTATE_UINT32_ARRAY(raw_pallette, pl110_state, 128),
  63. VMSTATE_END_OF_LIST()
  64. }
  65. };
  66. static const unsigned char pl110_id[] =
  67. { 0x10, 0x11, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  68. /* The Arm documentation (DDI0224C) says the CLDC on the Versatile board
  69. has a different ID. However Linux only looks for the normal ID. */
  70. #if 0
  71. static const unsigned char pl110_versatile_id[] =
  72. { 0x93, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
  73. #else
  74. #define pl110_versatile_id pl110_id
  75. #endif
  76. #include "pixel_ops.h"
  77. #define BITS 8
  78. #include "pl110_template.h"
  79. #define BITS 15
  80. #include "pl110_template.h"
  81. #define BITS 16
  82. #include "pl110_template.h"
  83. #define BITS 24
  84. #include "pl110_template.h"
  85. #define BITS 32
  86. #include "pl110_template.h"
  87. static int pl110_enabled(pl110_state *s)
  88. {
  89. return (s->cr & PL110_CR_EN) && (s->cr & PL110_CR_PWR);
  90. }
  91. static void pl110_update_display(void *opaque)
  92. {
  93. pl110_state *s = (pl110_state *)opaque;
  94. drawfn* fntable;
  95. drawfn fn;
  96. int dest_width;
  97. int src_width;
  98. int bpp_offset;
  99. int first;
  100. int last;
  101. if (!pl110_enabled(s))
  102. return;
  103. switch (ds_get_bits_per_pixel(s->ds)) {
  104. case 0:
  105. return;
  106. case 8:
  107. fntable = pl110_draw_fn_8;
  108. dest_width = 1;
  109. break;
  110. case 15:
  111. fntable = pl110_draw_fn_15;
  112. dest_width = 2;
  113. break;
  114. case 16:
  115. fntable = pl110_draw_fn_16;
  116. dest_width = 2;
  117. break;
  118. case 24:
  119. fntable = pl110_draw_fn_24;
  120. dest_width = 3;
  121. break;
  122. case 32:
  123. fntable = pl110_draw_fn_32;
  124. dest_width = 4;
  125. break;
  126. default:
  127. fprintf(stderr, "pl110: Bad color depth\n");
  128. exit(1);
  129. }
  130. if (s->cr & PL110_CR_BGR)
  131. bpp_offset = 0;
  132. else
  133. bpp_offset = 18;
  134. if (s->cr & PL110_CR_BEBO)
  135. fn = fntable[s->bpp + 6 + bpp_offset];
  136. else if (s->cr & PL110_CR_BEPO)
  137. fn = fntable[s->bpp + 12 + bpp_offset];
  138. else
  139. fn = fntable[s->bpp + bpp_offset];
  140. src_width = s->cols;
  141. switch (s->bpp) {
  142. case BPP_1:
  143. src_width >>= 3;
  144. break;
  145. case BPP_2:
  146. src_width >>= 2;
  147. break;
  148. case BPP_4:
  149. src_width >>= 1;
  150. break;
  151. case BPP_8:
  152. break;
  153. case BPP_16:
  154. src_width <<= 1;
  155. break;
  156. case BPP_32:
  157. src_width <<= 2;
  158. break;
  159. }
  160. dest_width *= s->cols;
  161. first = 0;
  162. framebuffer_update_display(s->ds,
  163. s->upbase, s->cols, s->rows,
  164. src_width, dest_width, 0,
  165. s->invalidate,
  166. fn, s->pallette,
  167. &first, &last);
  168. if (first >= 0) {
  169. dpy_update(s->ds, 0, first, s->cols, last - first + 1);
  170. }
  171. s->invalidate = 0;
  172. }
  173. static void pl110_invalidate_display(void * opaque)
  174. {
  175. pl110_state *s = (pl110_state *)opaque;
  176. s->invalidate = 1;
  177. if (pl110_enabled(s)) {
  178. qemu_console_resize(s->ds, s->cols, s->rows);
  179. }
  180. }
  181. static void pl110_update_pallette(pl110_state *s, int n)
  182. {
  183. int i;
  184. uint32_t raw;
  185. unsigned int r, g, b;
  186. raw = s->raw_pallette[n];
  187. n <<= 1;
  188. for (i = 0; i < 2; i++) {
  189. r = (raw & 0x1f) << 3;
  190. raw >>= 5;
  191. g = (raw & 0x1f) << 3;
  192. raw >>= 5;
  193. b = (raw & 0x1f) << 3;
  194. /* The I bit is ignored. */
  195. raw >>= 6;
  196. switch (ds_get_bits_per_pixel(s->ds)) {
  197. case 8:
  198. s->pallette[n] = rgb_to_pixel8(r, g, b);
  199. break;
  200. case 15:
  201. s->pallette[n] = rgb_to_pixel15(r, g, b);
  202. break;
  203. case 16:
  204. s->pallette[n] = rgb_to_pixel16(r, g, b);
  205. break;
  206. case 24:
  207. case 32:
  208. s->pallette[n] = rgb_to_pixel32(r, g, b);
  209. break;
  210. }
  211. n++;
  212. }
  213. }
  214. static void pl110_resize(pl110_state *s, int width, int height)
  215. {
  216. if (width != s->cols || height != s->rows) {
  217. if (pl110_enabled(s)) {
  218. qemu_console_resize(s->ds, width, height);
  219. }
  220. }
  221. s->cols = width;
  222. s->rows = height;
  223. }
  224. /* Update interrupts. */
  225. static void pl110_update(pl110_state *s)
  226. {
  227. /* TODO: Implement interrupts. */
  228. }
  229. static uint32_t pl110_read(void *opaque, target_phys_addr_t offset)
  230. {
  231. pl110_state *s = (pl110_state *)opaque;
  232. if (offset >= 0xfe0 && offset < 0x1000) {
  233. if (s->versatile)
  234. return pl110_versatile_id[(offset - 0xfe0) >> 2];
  235. else
  236. return pl110_id[(offset - 0xfe0) >> 2];
  237. }
  238. if (offset >= 0x200 && offset < 0x400) {
  239. return s->raw_pallette[(offset - 0x200) >> 2];
  240. }
  241. switch (offset >> 2) {
  242. case 0: /* LCDTiming0 */
  243. return s->timing[0];
  244. case 1: /* LCDTiming1 */
  245. return s->timing[1];
  246. case 2: /* LCDTiming2 */
  247. return s->timing[2];
  248. case 3: /* LCDTiming3 */
  249. return s->timing[3];
  250. case 4: /* LCDUPBASE */
  251. return s->upbase;
  252. case 5: /* LCDLPBASE */
  253. return s->lpbase;
  254. case 6: /* LCDIMSC */
  255. if (s->versatile)
  256. return s->cr;
  257. return s->int_mask;
  258. case 7: /* LCDControl */
  259. if (s->versatile)
  260. return s->int_mask;
  261. return s->cr;
  262. case 8: /* LCDRIS */
  263. return s->int_status;
  264. case 9: /* LCDMIS */
  265. return s->int_status & s->int_mask;
  266. case 11: /* LCDUPCURR */
  267. /* TODO: Implement vertical refresh. */
  268. return s->upbase;
  269. case 12: /* LCDLPCURR */
  270. return s->lpbase;
  271. default:
  272. hw_error("pl110_read: Bad offset %x\n", (int)offset);
  273. return 0;
  274. }
  275. }
  276. static void pl110_write(void *opaque, target_phys_addr_t offset,
  277. uint32_t val)
  278. {
  279. pl110_state *s = (pl110_state *)opaque;
  280. int n;
  281. /* For simplicity invalidate the display whenever a control register
  282. is writen to. */
  283. s->invalidate = 1;
  284. if (offset >= 0x200 && offset < 0x400) {
  285. /* Pallette. */
  286. n = (offset - 0x200) >> 2;
  287. s->raw_pallette[(offset - 0x200) >> 2] = val;
  288. pl110_update_pallette(s, n);
  289. return;
  290. }
  291. switch (offset >> 2) {
  292. case 0: /* LCDTiming0 */
  293. s->timing[0] = val;
  294. n = ((val & 0xfc) + 4) * 4;
  295. pl110_resize(s, n, s->rows);
  296. break;
  297. case 1: /* LCDTiming1 */
  298. s->timing[1] = val;
  299. n = (val & 0x3ff) + 1;
  300. pl110_resize(s, s->cols, n);
  301. break;
  302. case 2: /* LCDTiming2 */
  303. s->timing[2] = val;
  304. break;
  305. case 3: /* LCDTiming3 */
  306. s->timing[3] = val;
  307. break;
  308. case 4: /* LCDUPBASE */
  309. s->upbase = val;
  310. break;
  311. case 5: /* LCDLPBASE */
  312. s->lpbase = val;
  313. break;
  314. case 6: /* LCDIMSC */
  315. if (s->versatile)
  316. goto control;
  317. imsc:
  318. s->int_mask = val;
  319. pl110_update(s);
  320. break;
  321. case 7: /* LCDControl */
  322. if (s->versatile)
  323. goto imsc;
  324. control:
  325. s->cr = val;
  326. s->bpp = (val >> 1) & 7;
  327. if (pl110_enabled(s)) {
  328. qemu_console_resize(s->ds, s->cols, s->rows);
  329. }
  330. break;
  331. case 10: /* LCDICR */
  332. s->int_status &= ~val;
  333. pl110_update(s);
  334. break;
  335. default:
  336. hw_error("pl110_write: Bad offset %x\n", (int)offset);
  337. }
  338. }
  339. static CPUReadMemoryFunc * const pl110_readfn[] = {
  340. pl110_read,
  341. pl110_read,
  342. pl110_read
  343. };
  344. static CPUWriteMemoryFunc * const pl110_writefn[] = {
  345. pl110_write,
  346. pl110_write,
  347. pl110_write
  348. };
  349. static int pl110_init(SysBusDevice *dev)
  350. {
  351. pl110_state *s = FROM_SYSBUS(pl110_state, dev);
  352. int iomemtype;
  353. iomemtype = cpu_register_io_memory(pl110_readfn,
  354. pl110_writefn, s,
  355. DEVICE_NATIVE_ENDIAN);
  356. sysbus_init_mmio(dev, 0x1000, iomemtype);
  357. sysbus_init_irq(dev, &s->irq);
  358. s->ds = graphic_console_init(pl110_update_display,
  359. pl110_invalidate_display,
  360. NULL, NULL, s);
  361. return 0;
  362. }
  363. static int pl110_versatile_init(SysBusDevice *dev)
  364. {
  365. pl110_state *s = FROM_SYSBUS(pl110_state, dev);
  366. s->versatile = 1;
  367. return pl110_init(dev);
  368. }
  369. static SysBusDeviceInfo pl110_info = {
  370. .init = pl110_init,
  371. .qdev.name = "pl110",
  372. .qdev.size = sizeof(pl110_state),
  373. .qdev.vmsd = &vmstate_pl110,
  374. .qdev.no_user = 1,
  375. };
  376. static SysBusDeviceInfo pl110_versatile_info = {
  377. .init = pl110_versatile_init,
  378. .qdev.name = "pl110_versatile",
  379. .qdev.size = sizeof(pl110_state),
  380. .qdev.vmsd = &vmstate_pl110,
  381. .qdev.no_user = 1,
  382. };
  383. static void pl110_register_devices(void)
  384. {
  385. sysbus_register_withprop(&pl110_info);
  386. sysbus_register_withprop(&pl110_versatile_info);
  387. }
  388. device_init(pl110_register_devices)