dm163.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. /*
  2. * QEMU DM163 8x3-channel constant current led driver
  3. * driving columns of associated 8x8 RGB matrix.
  4. *
  5. * Copyright (C) 2024 Samuel Tardieu <sam@rfc1149.net>
  6. * Copyright (C) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
  7. * Copyright (C) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
  8. *
  9. * SPDX-License-Identifier: GPL-2.0-or-later
  10. */
  11. /*
  12. * The reference used for the DM163 is the following :
  13. * http://www.siti.com.tw/product/spec/LED/DM163.pdf
  14. */
  15. #include "qemu/osdep.h"
  16. #include "qapi/error.h"
  17. #include "migration/vmstate.h"
  18. #include "hw/irq.h"
  19. #include "hw/qdev-properties.h"
  20. #include "hw/display/dm163.h"
  21. #include "ui/console.h"
  22. #include "trace.h"
  23. #define LED_SQUARE_SIZE 100
  24. /* Number of frames a row stays visible after being turned off. */
  25. #define ROW_PERSISTENCE 3
  26. #define TURNED_OFF_ROW (COLOR_BUFFER_SIZE - 1)
  27. static const VMStateDescription vmstate_dm163 = {
  28. .name = TYPE_DM163,
  29. .version_id = 1,
  30. .minimum_version_id = 1,
  31. .fields = (const VMStateField[]) {
  32. VMSTATE_UINT64_ARRAY(bank0_shift_register, DM163State, 3),
  33. VMSTATE_UINT64_ARRAY(bank1_shift_register, DM163State, 3),
  34. VMSTATE_UINT16_ARRAY(latched_outputs, DM163State, DM163_NUM_LEDS),
  35. VMSTATE_UINT16_ARRAY(outputs, DM163State, DM163_NUM_LEDS),
  36. VMSTATE_UINT8(dck, DM163State),
  37. VMSTATE_UINT8(en_b, DM163State),
  38. VMSTATE_UINT8(lat_b, DM163State),
  39. VMSTATE_UINT8(rst_b, DM163State),
  40. VMSTATE_UINT8(selbk, DM163State),
  41. VMSTATE_UINT8(sin, DM163State),
  42. VMSTATE_UINT8(activated_rows, DM163State),
  43. VMSTATE_UINT32_2DARRAY(buffer, DM163State, COLOR_BUFFER_SIZE,
  44. RGB_MATRIX_NUM_COLS),
  45. VMSTATE_UINT8(last_buffer_idx, DM163State),
  46. VMSTATE_UINT8_ARRAY(buffer_idx_of_row, DM163State, RGB_MATRIX_NUM_ROWS),
  47. VMSTATE_UINT8_ARRAY(row_persistence_delay, DM163State,
  48. RGB_MATRIX_NUM_ROWS),
  49. VMSTATE_END_OF_LIST()
  50. }
  51. };
  52. static void dm163_reset_hold(Object *obj, ResetType type)
  53. {
  54. DM163State *s = DM163(obj);
  55. s->sin = 0;
  56. s->dck = 0;
  57. s->rst_b = 0;
  58. /* Ensuring the first falling edge of lat_b isn't missed */
  59. s->lat_b = 1;
  60. s->selbk = 0;
  61. s->en_b = 0;
  62. /* Reset stops the PWM, not the shift and latched registers. */
  63. memset(s->outputs, 0, sizeof(s->outputs));
  64. s->activated_rows = 0;
  65. s->redraw = 0;
  66. trace_dm163_redraw(s->redraw);
  67. for (unsigned i = 0; i < COLOR_BUFFER_SIZE; i++) {
  68. memset(s->buffer[i], 0, sizeof(s->buffer[0]));
  69. }
  70. s->last_buffer_idx = 0;
  71. memset(s->buffer_idx_of_row, TURNED_OFF_ROW, sizeof(s->buffer_idx_of_row));
  72. memset(s->row_persistence_delay, 0, sizeof(s->row_persistence_delay));
  73. }
  74. static void dm163_dck_gpio_handler(void *opaque, int line, int new_state)
  75. {
  76. DM163State *s = opaque;
  77. if (new_state && !s->dck) {
  78. /*
  79. * On raising dck, sample selbk to get the bank to use, and
  80. * sample sin for the bit to enter into the bank shift buffer.
  81. */
  82. uint64_t *sb =
  83. s->selbk ? s->bank1_shift_register : s->bank0_shift_register;
  84. /* Output the outgoing bit on sout */
  85. const bool sout = (s->selbk ? sb[2] & MAKE_64BIT_MASK(63, 1) :
  86. sb[2] & MAKE_64BIT_MASK(15, 1)) != 0;
  87. qemu_set_irq(s->sout, sout);
  88. /* Enter sin into the shift buffer */
  89. sb[2] = (sb[2] << 1) | ((sb[1] >> 63) & 1);
  90. sb[1] = (sb[1] << 1) | ((sb[0] >> 63) & 1);
  91. sb[0] = (sb[0] << 1) | s->sin;
  92. }
  93. s->dck = new_state;
  94. trace_dm163_dck(new_state);
  95. }
  96. static void dm163_propagate_outputs(DM163State *s)
  97. {
  98. s->last_buffer_idx = (s->last_buffer_idx + 1) % RGB_MATRIX_NUM_ROWS;
  99. /* Values are output when reset is high and enable is low. */
  100. if (s->rst_b && !s->en_b) {
  101. memcpy(s->outputs, s->latched_outputs, sizeof(s->outputs));
  102. } else {
  103. memset(s->outputs, 0, sizeof(s->outputs));
  104. }
  105. for (unsigned x = 0; x < RGB_MATRIX_NUM_COLS; x++) {
  106. /* Grouping the 3 RGB channels in a pixel value */
  107. const uint16_t b = extract16(s->outputs[3 * x + 0], 6, 8);
  108. const uint16_t g = extract16(s->outputs[3 * x + 1], 6, 8);
  109. const uint16_t r = extract16(s->outputs[3 * x + 2], 6, 8);
  110. uint32_t rgba = 0;
  111. trace_dm163_channels(3 * x + 2, r);
  112. trace_dm163_channels(3 * x + 1, g);
  113. trace_dm163_channels(3 * x + 0, b);
  114. rgba = deposit32(rgba, 0, 8, r);
  115. rgba = deposit32(rgba, 8, 8, g);
  116. rgba = deposit32(rgba, 16, 8, b);
  117. /* Led values are sent from the last one to the first one */
  118. s->buffer[s->last_buffer_idx][RGB_MATRIX_NUM_COLS - x - 1] = rgba;
  119. }
  120. for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) {
  121. if (s->activated_rows & (1 << row)) {
  122. s->buffer_idx_of_row[row] = s->last_buffer_idx;
  123. s->redraw |= (1 << row);
  124. trace_dm163_redraw(s->redraw);
  125. }
  126. }
  127. }
  128. static void dm163_en_b_gpio_handler(void *opaque, int line, int new_state)
  129. {
  130. DM163State *s = opaque;
  131. s->en_b = new_state;
  132. dm163_propagate_outputs(s);
  133. trace_dm163_en_b(new_state);
  134. }
  135. static uint8_t dm163_bank0(const DM163State *s, uint8_t led)
  136. {
  137. /*
  138. * Bank 0 uses 6 bits per led, so a value may be stored accross
  139. * two uint64_t entries.
  140. */
  141. const uint8_t low_bit = 6 * led;
  142. const uint8_t low_word = low_bit / 64;
  143. const uint8_t high_word = (low_bit + 5) / 64;
  144. const uint8_t low_shift = low_bit % 64;
  145. if (low_word == high_word) {
  146. /* Simple case: the value belongs to one entry. */
  147. return extract64(s->bank0_shift_register[low_word], low_shift, 6);
  148. }
  149. const uint8_t nb_bits_in_low_word = 64 - low_shift;
  150. const uint8_t nb_bits_in_high_word = 6 - nb_bits_in_low_word;
  151. const uint64_t bits_in_low_word = \
  152. extract64(s->bank0_shift_register[low_word], low_shift,
  153. nb_bits_in_low_word);
  154. const uint64_t bits_in_high_word = \
  155. extract64(s->bank0_shift_register[high_word], 0,
  156. nb_bits_in_high_word);
  157. uint8_t val = 0;
  158. val = deposit32(val, 0, nb_bits_in_low_word, bits_in_low_word);
  159. val = deposit32(val, nb_bits_in_low_word, nb_bits_in_high_word,
  160. bits_in_high_word);
  161. return val;
  162. }
  163. static uint8_t dm163_bank1(const DM163State *s, uint8_t led)
  164. {
  165. const uint64_t entry = s->bank1_shift_register[led / RGB_MATRIX_NUM_COLS];
  166. return extract64(entry, 8 * (led % RGB_MATRIX_NUM_COLS), 8);
  167. }
  168. static void dm163_lat_b_gpio_handler(void *opaque, int line, int new_state)
  169. {
  170. DM163State *s = opaque;
  171. if (s->lat_b && !new_state) {
  172. for (int led = 0; led < DM163_NUM_LEDS; led++) {
  173. s->latched_outputs[led] = dm163_bank0(s, led) * dm163_bank1(s, led);
  174. }
  175. dm163_propagate_outputs(s);
  176. }
  177. s->lat_b = new_state;
  178. trace_dm163_lat_b(new_state);
  179. }
  180. static void dm163_rst_b_gpio_handler(void *opaque, int line, int new_state)
  181. {
  182. DM163State *s = opaque;
  183. s->rst_b = new_state;
  184. dm163_propagate_outputs(s);
  185. trace_dm163_rst_b(new_state);
  186. }
  187. static void dm163_selbk_gpio_handler(void *opaque, int line, int new_state)
  188. {
  189. DM163State *s = opaque;
  190. s->selbk = new_state;
  191. trace_dm163_selbk(new_state);
  192. }
  193. static void dm163_sin_gpio_handler(void *opaque, int line, int new_state)
  194. {
  195. DM163State *s = opaque;
  196. s->sin = new_state;
  197. trace_dm163_sin(new_state);
  198. }
  199. static void dm163_rows_gpio_handler(void *opaque, int line, int new_state)
  200. {
  201. DM163State *s = opaque;
  202. if (new_state) {
  203. s->activated_rows |= (1 << line);
  204. s->buffer_idx_of_row[line] = s->last_buffer_idx;
  205. s->redraw |= (1 << line);
  206. trace_dm163_redraw(s->redraw);
  207. } else {
  208. s->activated_rows &= ~(1 << line);
  209. s->row_persistence_delay[line] = ROW_PERSISTENCE;
  210. }
  211. trace_dm163_activated_rows(s->activated_rows);
  212. }
  213. static void dm163_invalidate_display(void *opaque)
  214. {
  215. DM163State *s = (DM163State *)opaque;
  216. s->redraw = 0xFF;
  217. trace_dm163_redraw(s->redraw);
  218. }
  219. static void update_row_persistence_delay(DM163State *s, unsigned row)
  220. {
  221. if (s->row_persistence_delay[row]) {
  222. s->row_persistence_delay[row]--;
  223. } else {
  224. /*
  225. * If the ROW_PERSISTENCE delay is up,
  226. * the row is turned off.
  227. */
  228. s->buffer_idx_of_row[row] = TURNED_OFF_ROW;
  229. s->redraw |= (1 << row);
  230. trace_dm163_redraw(s->redraw);
  231. }
  232. }
  233. static uint32_t *update_display_of_row(DM163State *s, uint32_t *dest,
  234. unsigned row)
  235. {
  236. for (unsigned _ = 0; _ < LED_SQUARE_SIZE; _++) {
  237. for (int x = RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE - 1; x >= 0; x--) {
  238. /* UI layer guarantees that there's 32 bits per pixel (Mar 2024) */
  239. *dest++ = s->buffer[s->buffer_idx_of_row[row]][x / LED_SQUARE_SIZE];
  240. }
  241. }
  242. dpy_gfx_update(s->console, 0, LED_SQUARE_SIZE * row,
  243. RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE, LED_SQUARE_SIZE);
  244. s->redraw &= ~(1 << row);
  245. trace_dm163_redraw(s->redraw);
  246. return dest;
  247. }
  248. static void dm163_update_display(void *opaque)
  249. {
  250. DM163State *s = (DM163State *)opaque;
  251. DisplaySurface *surface = qemu_console_surface(s->console);
  252. uint32_t *dest;
  253. dest = surface_data(surface);
  254. for (unsigned row = 0; row < RGB_MATRIX_NUM_ROWS; row++) {
  255. update_row_persistence_delay(s, row);
  256. if (!extract8(s->redraw, row, 1)) {
  257. dest += LED_SQUARE_SIZE * LED_SQUARE_SIZE * RGB_MATRIX_NUM_COLS;
  258. continue;
  259. }
  260. dest = update_display_of_row(s, dest, row);
  261. }
  262. }
  263. static const GraphicHwOps dm163_ops = {
  264. .invalidate = dm163_invalidate_display,
  265. .gfx_update = dm163_update_display,
  266. };
  267. static void dm163_realize(DeviceState *dev, Error **errp)
  268. {
  269. DM163State *s = DM163(dev);
  270. qdev_init_gpio_in(dev, dm163_rows_gpio_handler, RGB_MATRIX_NUM_ROWS);
  271. qdev_init_gpio_in(dev, dm163_sin_gpio_handler, 1);
  272. qdev_init_gpio_in(dev, dm163_dck_gpio_handler, 1);
  273. qdev_init_gpio_in(dev, dm163_rst_b_gpio_handler, 1);
  274. qdev_init_gpio_in(dev, dm163_lat_b_gpio_handler, 1);
  275. qdev_init_gpio_in(dev, dm163_selbk_gpio_handler, 1);
  276. qdev_init_gpio_in(dev, dm163_en_b_gpio_handler, 1);
  277. qdev_init_gpio_out_named(dev, &s->sout, "sout", 1);
  278. s->console = graphic_console_init(dev, 0, &dm163_ops, s);
  279. qemu_console_resize(s->console, RGB_MATRIX_NUM_COLS * LED_SQUARE_SIZE,
  280. RGB_MATRIX_NUM_ROWS * LED_SQUARE_SIZE);
  281. }
  282. static void dm163_class_init(ObjectClass *klass, void *data)
  283. {
  284. DeviceClass *dc = DEVICE_CLASS(klass);
  285. ResettableClass *rc = RESETTABLE_CLASS(klass);
  286. dc->desc = "DM163 8x3-channel constant current LED driver";
  287. dc->vmsd = &vmstate_dm163;
  288. dc->realize = dm163_realize;
  289. rc->phases.hold = dm163_reset_hold;
  290. set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
  291. }
  292. static const TypeInfo dm163_types[] = {
  293. {
  294. .name = TYPE_DM163,
  295. .parent = TYPE_DEVICE,
  296. .instance_size = sizeof(DM163State),
  297. .class_init = dm163_class_init
  298. }
  299. };
  300. DEFINE_TYPES(dm163_types)