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