sii9022.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * Silicon Image SiI9022
  3. *
  4. * This is a pretty hollow emulation: all we do is acknowledge that we
  5. * exist (chip ID) and confirm that we get switched over into DDC mode
  6. * so the emulated host can proceed to read out EDID data. All subsequent
  7. * set-up of connectors etc will be acknowledged and ignored.
  8. *
  9. * Copyright (C) 2018 Linus Walleij
  10. *
  11. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  12. * See the COPYING file in the top-level directory.
  13. * SPDX-License-Identifier: GPL-2.0-or-later
  14. */
  15. #include "qemu/osdep.h"
  16. #include "qemu/module.h"
  17. #include "hw/i2c/i2c.h"
  18. #include "migration/vmstate.h"
  19. #include "hw/display/i2c-ddc.h"
  20. #include "trace.h"
  21. #define SII9022_SYS_CTRL_DATA 0x1a
  22. #define SII9022_SYS_CTRL_PWR_DWN 0x10
  23. #define SII9022_SYS_CTRL_AV_MUTE 0x08
  24. #define SII9022_SYS_CTRL_DDC_BUS_REQ 0x04
  25. #define SII9022_SYS_CTRL_DDC_BUS_GRTD 0x02
  26. #define SII9022_SYS_CTRL_OUTPUT_MODE 0x01
  27. #define SII9022_SYS_CTRL_OUTPUT_HDMI 1
  28. #define SII9022_SYS_CTRL_OUTPUT_DVI 0
  29. #define SII9022_REG_CHIPID 0x1b
  30. #define SII9022_INT_ENABLE 0x3c
  31. #define SII9022_INT_STATUS 0x3d
  32. #define SII9022_INT_STATUS_HOTPLUG 0x01;
  33. #define SII9022_INT_STATUS_PLUGGED 0x04;
  34. #define TYPE_SII9022 "sii9022"
  35. #define SII9022(obj) OBJECT_CHECK(sii9022_state, (obj), TYPE_SII9022)
  36. typedef struct sii9022_state {
  37. I2CSlave parent_obj;
  38. uint8_t ptr;
  39. bool addr_byte;
  40. bool ddc_req;
  41. bool ddc_skip_finish;
  42. bool ddc;
  43. } sii9022_state;
  44. static const VMStateDescription vmstate_sii9022 = {
  45. .name = "sii9022",
  46. .version_id = 1,
  47. .minimum_version_id = 1,
  48. .fields = (VMStateField[]) {
  49. VMSTATE_I2C_SLAVE(parent_obj, sii9022_state),
  50. VMSTATE_UINT8(ptr, sii9022_state),
  51. VMSTATE_BOOL(addr_byte, sii9022_state),
  52. VMSTATE_BOOL(ddc_req, sii9022_state),
  53. VMSTATE_BOOL(ddc_skip_finish, sii9022_state),
  54. VMSTATE_BOOL(ddc, sii9022_state),
  55. VMSTATE_END_OF_LIST()
  56. }
  57. };
  58. static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
  59. {
  60. sii9022_state *s = SII9022(i2c);
  61. switch (event) {
  62. case I2C_START_SEND:
  63. s->addr_byte = true;
  64. break;
  65. case I2C_START_RECV:
  66. break;
  67. case I2C_FINISH:
  68. break;
  69. case I2C_NACK:
  70. break;
  71. }
  72. return 0;
  73. }
  74. static uint8_t sii9022_rx(I2CSlave *i2c)
  75. {
  76. sii9022_state *s = SII9022(i2c);
  77. uint8_t res = 0x00;
  78. switch (s->ptr) {
  79. case SII9022_SYS_CTRL_DATA:
  80. if (s->ddc_req) {
  81. /* Acknowledge DDC bus request */
  82. res = SII9022_SYS_CTRL_DDC_BUS_GRTD | SII9022_SYS_CTRL_DDC_BUS_REQ;
  83. }
  84. break;
  85. case SII9022_REG_CHIPID:
  86. res = 0xb0;
  87. break;
  88. case SII9022_INT_STATUS:
  89. /* Something is cold-plugged in, no interrupts */
  90. res = SII9022_INT_STATUS_PLUGGED;
  91. break;
  92. default:
  93. break;
  94. }
  95. trace_sii9022_read_reg(s->ptr, res);
  96. s->ptr++;
  97. return res;
  98. }
  99. static int sii9022_tx(I2CSlave *i2c, uint8_t data)
  100. {
  101. sii9022_state *s = SII9022(i2c);
  102. if (s->addr_byte) {
  103. s->ptr = data;
  104. s->addr_byte = false;
  105. return 0;
  106. }
  107. switch (s->ptr) {
  108. case SII9022_SYS_CTRL_DATA:
  109. if (data & SII9022_SYS_CTRL_DDC_BUS_REQ) {
  110. s->ddc_req = true;
  111. if (data & SII9022_SYS_CTRL_DDC_BUS_GRTD) {
  112. s->ddc = true;
  113. /* Skip this finish since we just switched to DDC */
  114. s->ddc_skip_finish = true;
  115. trace_sii9022_switch_mode("DDC");
  116. }
  117. } else {
  118. s->ddc_req = false;
  119. s->ddc = false;
  120. trace_sii9022_switch_mode("normal");
  121. }
  122. break;
  123. default:
  124. break;
  125. }
  126. trace_sii9022_write_reg(s->ptr, data);
  127. s->ptr++;
  128. return 0;
  129. }
  130. static void sii9022_reset(DeviceState *dev)
  131. {
  132. sii9022_state *s = SII9022(dev);
  133. s->ptr = 0;
  134. s->addr_byte = false;
  135. s->ddc_req = false;
  136. s->ddc_skip_finish = false;
  137. s->ddc = false;
  138. }
  139. static void sii9022_realize(DeviceState *dev, Error **errp)
  140. {
  141. I2CBus *bus;
  142. bus = I2C_BUS(qdev_get_parent_bus(dev));
  143. i2c_create_slave(bus, TYPE_I2CDDC, 0x50);
  144. }
  145. static void sii9022_class_init(ObjectClass *klass, void *data)
  146. {
  147. DeviceClass *dc = DEVICE_CLASS(klass);
  148. I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
  149. k->event = sii9022_event;
  150. k->recv = sii9022_rx;
  151. k->send = sii9022_tx;
  152. dc->reset = sii9022_reset;
  153. dc->realize = sii9022_realize;
  154. dc->vmsd = &vmstate_sii9022;
  155. }
  156. static const TypeInfo sii9022_info = {
  157. .name = TYPE_SII9022,
  158. .parent = TYPE_I2C_SLAVE,
  159. .instance_size = sizeof(sii9022_state),
  160. .class_init = sii9022_class_init,
  161. };
  162. static void sii9022_register_types(void)
  163. {
  164. type_register_static(&sii9022_info);
  165. }
  166. type_init(sii9022_register_types)