sii9022.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. #include "qom/object.h"
  22. #define SII9022_SYS_CTRL_DATA 0x1a
  23. #define SII9022_SYS_CTRL_PWR_DWN 0x10
  24. #define SII9022_SYS_CTRL_AV_MUTE 0x08
  25. #define SII9022_SYS_CTRL_DDC_BUS_REQ 0x04
  26. #define SII9022_SYS_CTRL_DDC_BUS_GRTD 0x02
  27. #define SII9022_SYS_CTRL_OUTPUT_MODE 0x01
  28. #define SII9022_SYS_CTRL_OUTPUT_HDMI 1
  29. #define SII9022_SYS_CTRL_OUTPUT_DVI 0
  30. #define SII9022_REG_CHIPID 0x1b
  31. #define SII9022_INT_ENABLE 0x3c
  32. #define SII9022_INT_STATUS 0x3d
  33. #define SII9022_INT_STATUS_HOTPLUG 0x01;
  34. #define SII9022_INT_STATUS_PLUGGED 0x04;
  35. #define TYPE_SII9022 "sii9022"
  36. OBJECT_DECLARE_SIMPLE_TYPE(sii9022_state, SII9022)
  37. struct sii9022_state {
  38. I2CSlave parent_obj;
  39. uint8_t ptr;
  40. bool addr_byte;
  41. bool ddc_req;
  42. bool ddc_skip_finish;
  43. bool ddc;
  44. };
  45. static const VMStateDescription vmstate_sii9022 = {
  46. .name = "sii9022",
  47. .version_id = 1,
  48. .minimum_version_id = 1,
  49. .fields = (VMStateField[]) {
  50. VMSTATE_I2C_SLAVE(parent_obj, sii9022_state),
  51. VMSTATE_UINT8(ptr, sii9022_state),
  52. VMSTATE_BOOL(addr_byte, sii9022_state),
  53. VMSTATE_BOOL(ddc_req, sii9022_state),
  54. VMSTATE_BOOL(ddc_skip_finish, sii9022_state),
  55. VMSTATE_BOOL(ddc, sii9022_state),
  56. VMSTATE_END_OF_LIST()
  57. }
  58. };
  59. static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
  60. {
  61. sii9022_state *s = SII9022(i2c);
  62. switch (event) {
  63. case I2C_START_SEND:
  64. s->addr_byte = true;
  65. break;
  66. case I2C_START_RECV:
  67. break;
  68. case I2C_FINISH:
  69. break;
  70. case I2C_NACK:
  71. break;
  72. default:
  73. return -1;
  74. }
  75. return 0;
  76. }
  77. static uint8_t sii9022_rx(I2CSlave *i2c)
  78. {
  79. sii9022_state *s = SII9022(i2c);
  80. uint8_t res = 0x00;
  81. switch (s->ptr) {
  82. case SII9022_SYS_CTRL_DATA:
  83. if (s->ddc_req) {
  84. /* Acknowledge DDC bus request */
  85. res = SII9022_SYS_CTRL_DDC_BUS_GRTD | SII9022_SYS_CTRL_DDC_BUS_REQ;
  86. }
  87. break;
  88. case SII9022_REG_CHIPID:
  89. res = 0xb0;
  90. break;
  91. case SII9022_INT_STATUS:
  92. /* Something is cold-plugged in, no interrupts */
  93. res = SII9022_INT_STATUS_PLUGGED;
  94. break;
  95. default:
  96. break;
  97. }
  98. trace_sii9022_read_reg(s->ptr, res);
  99. s->ptr++;
  100. return res;
  101. }
  102. static int sii9022_tx(I2CSlave *i2c, uint8_t data)
  103. {
  104. sii9022_state *s = SII9022(i2c);
  105. if (s->addr_byte) {
  106. s->ptr = data;
  107. s->addr_byte = false;
  108. return 0;
  109. }
  110. switch (s->ptr) {
  111. case SII9022_SYS_CTRL_DATA:
  112. if (data & SII9022_SYS_CTRL_DDC_BUS_REQ) {
  113. s->ddc_req = true;
  114. if (data & SII9022_SYS_CTRL_DDC_BUS_GRTD) {
  115. s->ddc = true;
  116. /* Skip this finish since we just switched to DDC */
  117. s->ddc_skip_finish = true;
  118. trace_sii9022_switch_mode("DDC");
  119. }
  120. } else {
  121. s->ddc_req = false;
  122. s->ddc = false;
  123. trace_sii9022_switch_mode("normal");
  124. }
  125. break;
  126. default:
  127. break;
  128. }
  129. trace_sii9022_write_reg(s->ptr, data);
  130. s->ptr++;
  131. return 0;
  132. }
  133. static void sii9022_reset(DeviceState *dev)
  134. {
  135. sii9022_state *s = SII9022(dev);
  136. s->ptr = 0;
  137. s->addr_byte = false;
  138. s->ddc_req = false;
  139. s->ddc_skip_finish = false;
  140. s->ddc = false;
  141. }
  142. static void sii9022_realize(DeviceState *dev, Error **errp)
  143. {
  144. I2CBus *bus;
  145. bus = I2C_BUS(qdev_get_parent_bus(dev));
  146. i2c_slave_create_simple(bus, TYPE_I2CDDC, 0x50);
  147. }
  148. static void sii9022_class_init(ObjectClass *klass, void *data)
  149. {
  150. DeviceClass *dc = DEVICE_CLASS(klass);
  151. I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
  152. k->event = sii9022_event;
  153. k->recv = sii9022_rx;
  154. k->send = sii9022_tx;
  155. dc->reset = sii9022_reset;
  156. dc->realize = sii9022_realize;
  157. dc->vmsd = &vmstate_sii9022;
  158. }
  159. static const TypeInfo sii9022_info = {
  160. .name = TYPE_SII9022,
  161. .parent = TYPE_I2C_SLAVE,
  162. .instance_size = sizeof(sii9022_state),
  163. .class_init = sii9022_class_init,
  164. };
  165. static void sii9022_register_types(void)
  166. {
  167. type_register_static(&sii9022_info);
  168. }
  169. type_init(sii9022_register_types)