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 "qemu/module.h"
  32#include "hw/misc/auxbus.h"
  33#include "hw/display/dpcd.h"
  34
  35#ifndef DEBUG_DPCD
  36#define DEBUG_DPCD 0
  37#endif
  38
  39#define DPRINTF(fmt, ...) do {                                                 \
  40    if (DEBUG_DPCD) {                                                          \
  41        qemu_log("dpcd: " fmt, ## __VA_ARGS__);                                \
  42    }                                                                          \
  43} while (0)
  44
  45#define DPCD_READABLE_AREA                      0x600
  46
  47struct DPCDState {
  48    /*< private >*/
  49    AUXSlave parent_obj;
  50
  51    /*< public >*/
  52    /*
  53     * The DCPD is 0x7FFFF length but read as 0 after offset 0x5FF.
  54     */
  55    uint8_t dpcd_info[DPCD_READABLE_AREA];
  56
  57    MemoryRegion iomem;
  58};
  59
  60static uint64_t dpcd_read(void *opaque, hwaddr offset, unsigned size)
  61{
  62    uint8_t ret;
  63    DPCDState *e = DPCD(opaque);
  64
  65    if (offset < DPCD_READABLE_AREA) {
  66        ret = e->dpcd_info[offset];
  67    } else {
  68        qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
  69                                       offset);
  70        ret = 0;
  71    }
  72
  73    DPRINTF("read 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", ret, offset);
  74    return ret;
  75}
  76
  77static void dpcd_write(void *opaque, hwaddr offset, uint64_t value,
  78                       unsigned size)
  79{
  80    DPCDState *e = DPCD(opaque);
  81
  82    DPRINTF("write 0x%" PRIX8 " @0x%" HWADDR_PRIX "\n", (uint8_t)value, offset);
  83
  84    if (offset < DPCD_READABLE_AREA) {
  85        e->dpcd_info[offset] = value;
  86    } else {
  87        qemu_log_mask(LOG_GUEST_ERROR, "dpcd: Bad offset 0x%" HWADDR_PRIX "\n",
  88                                       offset);
  89    }
  90}
  91
  92static const MemoryRegionOps aux_ops = {
  93    .read = dpcd_read,
  94    .write = dpcd_write,
  95    .valid = {
  96        .min_access_size = 1,
  97        .max_access_size = 1,
  98    },
  99    .impl = {
 100        .min_access_size = 1,
 101        .max_access_size = 1,
 102    },
 103};
 104
 105static void dpcd_reset(DeviceState *dev)
 106{
 107    DPCDState *s = DPCD(dev);
 108
 109    memset(&(s->dpcd_info), 0, sizeof(s->dpcd_info));
 110
 111    s->dpcd_info[DPCD_REVISION] = DPCD_REV_1_0;
 112    s->dpcd_info[DPCD_MAX_LINK_RATE] = DPCD_5_4GBPS;
 113    s->dpcd_info[DPCD_MAX_LANE_COUNT] = DPCD_FOUR_LANES;
 114    s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_0] = DPCD_EDID_PRESENT;
 115    /* buffer size */
 116    s->dpcd_info[DPCD_RECEIVE_PORT0_CAP_1] = 0xFF;
 117
 118    s->dpcd_info[DPCD_LANE0_1_STATUS] = DPCD_LANE0_CR_DONE
 119                                      | DPCD_LANE0_CHANNEL_EQ_DONE
 120                                      | DPCD_LANE0_SYMBOL_LOCKED
 121                                      | DPCD_LANE1_CR_DONE
 122                                      | DPCD_LANE1_CHANNEL_EQ_DONE
 123                                      | DPCD_LANE1_SYMBOL_LOCKED;
 124    s->dpcd_info[DPCD_LANE2_3_STATUS] = DPCD_LANE2_CR_DONE
 125                                      | DPCD_LANE2_CHANNEL_EQ_DONE
 126                                      | DPCD_LANE2_SYMBOL_LOCKED
 127                                      | DPCD_LANE3_CR_DONE
 128                                      | DPCD_LANE3_CHANNEL_EQ_DONE
 129                                      | DPCD_LANE3_SYMBOL_LOCKED;
 130
 131    s->dpcd_info[DPCD_LANE_ALIGN_STATUS_UPDATED] = DPCD_INTERLANE_ALIGN_DONE;
 132    s->dpcd_info[DPCD_SINK_STATUS] = DPCD_RECEIVE_PORT_0_STATUS;
 133}
 134
 135static void dpcd_init(Object *obj)
 136{
 137    DPCDState *s = DPCD(obj);
 138
 139    memory_region_init_io(&s->iomem, obj, &aux_ops, s, TYPE_DPCD, 0x7FFFF);
 140    aux_init_mmio(AUX_SLAVE(obj), &s->iomem);
 141}
 142
 143static const VMStateDescription vmstate_dpcd = {
 144    .name = TYPE_DPCD,
 145    .version_id = 0,
 146    .minimum_version_id = 0,
 147    .fields = (VMStateField[]) {
 148        VMSTATE_UINT8_ARRAY_V(dpcd_info, DPCDState, DPCD_READABLE_AREA, 0),
 149        VMSTATE_END_OF_LIST()
 150    }
 151};
 152
 153static void dpcd_class_init(ObjectClass *oc, void *data)
 154{
 155    DeviceClass *dc = DEVICE_CLASS(oc);
 156
 157    dc->reset = dpcd_reset;
 158    dc->vmsd = &vmstate_dpcd;
 159}
 160
 161static const TypeInfo dpcd_info = {
 162    .name          = TYPE_DPCD,
 163    .parent        = TYPE_AUX_SLAVE,
 164    .instance_size = sizeof(DPCDState),
 165    .class_init    = dpcd_class_init,
 166    .instance_init = dpcd_init,
 167};
 168
 169static void dpcd_register_types(void)
 170{
 171    type_register_static(&dpcd_info);
 172}
 173
 174type_init(dpcd_register_types)
 175