qemu/hw/display/dpcd.c
<<
>>
Prefs
   1/*
   2 * dpcd.c
   3 *
   4 *  Copyright (C) 2015 : GreenSocs Ltd
   5 *      http://www.greensocs.com/ , email: info@greensocs.com
   6 *
   7 *  Developed by :
   8 *  Frederic Konrad   <fred.konrad@greensocs.com>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation, either version 2 of the License, or
  13 * (at your option)any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License along
  21 * with this program; if not, see <http://www.gnu.org/licenses/>.
  22 *
  23 */
  24
  25/*
  26 * This is a simple AUX slave which emulates a connected screen.
  27 */
  28
  29#include "qemu/osdep.h"
  30#include "qemu/log.h"
  31#include "hw/misc/auxbus.h"
  32#include "hw/display/dpcd.h"
  33
  34#ifndef DEBUG_DPCD
  35#define DEBUG_DPCD 0
  36#endif
  37
  38#define DPRINTF(fmt, ...) do {                                                 \
  39    if (DEBUG_DPCD) {                                                          \
  40        qemu_log("dpcd: " fmt, ## __VA_ARGS__);                                \
  41    }                                                                          \
  42} while (0)
  43
  44#define DPCD_READABLE_AREA                      0x600
  45
  46struct DPCDState {
  47    /*< private >*/
  48    AUXSlave parent_obj;
  49
  50    /*< public >*/
  51    /*
  52     * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF.
  53     */
  54    uint8_t dpcd_info[DPCD_READABLE_AREA];
  55
  56    MemoryRegion iomem;
  57};
  58
  59static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size)
  60{
  61    uint8_t ret;
  62    DPCDState *e = DPCD(opaque);
  63
  64    if (offset < DPCD_READABLE_AREA) {
  65        ret = e->dpcd_info[offset];
  66    } else {
  67        qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
  68                                       offset);
  69        ret = 0;
  70    }
  71
  72    DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset);
  73    return ret;
  74}
  75
  76static void dpcd_write(void *opaque, hwaddr offset, uint64_t value,
  77                       unsigned size)
  78{
  79    DPCDState *e = DPCD(opaque);
  80
  81    DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset);
  82
  83    if (offset < DPCD_READABLE_AREA) {
  84        e->dpcd_info[offset] = value;
  85    } else {
  86        qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
  87                                       offset);
  88    }
  89}
  90
  91static const MemoryRegionOps aux_ops = {
  92    .read = dpcd_read,
  93    .write = dpcd_write,
  94    .valid = {
  95        .min_access_size = 1,
  96        .max_access_size = 1,
  97    },
  98    .impl = {
  99        .min_access_size = 1,
 100        .max_access_size = 1,
 101    },
 102};
 103
 104static void dpcd_reset(DeviceState *dev)
 105{
 106    DPCDState *s = DPCD(dev);
 107
 108    memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info));
 109
 110    s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0;
 111    s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS;
 112    s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES;
 113    s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT;
 114    /* buffer size */
 115    s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF;
 116
 117    s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE
 118                                      | DPCD_LANE0_CHANNEL_EQ_DONE
 119                                      | DPCD_LANE0_SYMBOL_LOCKED
 120                                      | DPCD_LANE1_CR_DONE
 121                                      | DPCD_LANE1_CHANNEL_EQ_DONE
 122                                      | DPCD_LANE1_SYMBOL_LOCKED;
 123    s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE
 124                                      | DPCD_LANE2_CHANNEL_EQ_DONE
 125                                      | DPCD_LANE2_SYMBOL_LOCKED
 126                                      | DPCD_LANE3_CR_DONE
 127                                      | DPCD_LANE3_CHANNEL_EQ_DONE
 128                                      | DPCD_LANE3_SYMBOL_LOCKED;
 129
 130    s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE;
 131    s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS;
 132}
 133
 134static void dpcd_init(Object *obj)
 135{
 136    DPCDState *s = DPCD(obj);
 137
 138    memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF);
 139    aux_init_mmio(AUX_SLAVE(obj), &s->iomem);
 140}
 141
 142static const VMStateDescription vmstate_dpcd = {
 143    .name = TYPE_DPCD,
 144    .version_id = 0,
 145    .minimum_version_id = 0,
 146    .fields = (VMStateField[]) {
 147        VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0),
 148        VMSTATE_END_OF_LIST()
 149    }
 150};
 151
 152static void dpcd_class_init(ObjectClass *oc, void *data)
 153{
 154    DeviceClass *dc = DEVICE_CLASS(oc);
 155
 156    dc->reset = dpcd_reset;
 157    dc->vmsd = &vmstate_dpcd;
 158}
 159
 160static const TypeInfo dpcd_info = {
 161    .name          = TYPE_DPCD,
 162    .parent        = TYPE_AUX_SLAVE,
 163    .instance_size = sizeof(DPCDState),
 164    .class_init    = dpcd_class_init,
 165    .instance_init = dpcd_init,
 166};
 167
 168static void dpcd_register_types(void)
 169{
 170    type_register_static(&dpcd_info);
 171}
 172
 173type_init(dpcd_register_types)
 174