ads7846.c 4.2 KB
Newer Older
B
balrog 已提交
1
/*
2
 * TI ADS7846 / TSC2046 chip emulation.
B
balrog 已提交
3 4 5 6 7
 *
 * Copyright (c) 2006 Openedhand Ltd.
 * Written by Andrzej Zaborowski <balrog@zabor.org>
 *
 * This code is licensed under the GNU GPL v2.
8 9 10
 *
 * Contributions after 2012-01-13 are licensed under the terms of the
 * GNU GPL, version 2 or (at your option) any later version.
B
balrog 已提交
11 12
 */

P
Peter Maydell 已提交
13
#include "qemu/osdep.h"
14
#include "hw/ssi/ssi.h"
15
#include "ui/console.h"
B
balrog 已提交
16

P
Paul Brook 已提交
17 18
typedef struct {
    SSISlave ssidev;
B
balrog 已提交
19 20 21 22 23 24 25 26
    qemu_irq interrupt;

    int input[8];
    int pressure;
    int noise;

    int cycle;
    int output;
P
Paul Brook 已提交
27
} ADS7846State;
B
balrog 已提交
28 29 30 31 32 33 34 35 36 37 38

/* Control-byte bitfields */
#define CB_PD0		(1 << 0)
#define CB_PD1		(1 << 1)
#define CB_SER		(1 << 2)
#define CB_MODE		(1 << 3)
#define CB_A0		(1 << 4)
#define CB_A1		(1 << 5)
#define CB_A2		(1 << 6)
#define CB_START	(1 << 7)

39 40 41 42
#define X_AXIS_DMAX	3470
#define X_AXIS_MIN	290
#define Y_AXIS_DMAX	3450
#define Y_AXIS_MIN	200
B
balrog 已提交
43 44 45 46 47 48 49 50 51 52

#define ADS_VBAT	2000
#define ADS_VAUX	2000
#define ADS_TEMP0	2000
#define ADS_TEMP1	3000
#define ADS_XPOS(x, y)	(X_AXIS_MIN + ((X_AXIS_DMAX * (x)) >> 15))
#define ADS_YPOS(x, y)	(Y_AXIS_MIN + ((Y_AXIS_DMAX * (y)) >> 15))
#define ADS_Z1POS(x, y)	600
#define ADS_Z2POS(x, y)	(600 + 6000 / ADS_XPOS(x, y))

P
Paul Brook 已提交
53
static void ads7846_int_update(ADS7846State *s)
B
balrog 已提交
54 55 56 57 58
{
    if (s->interrupt)
        qemu_set_irq(s->interrupt, s->pressure == 0);
}

P
Paul Brook 已提交
59
static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
B
balrog 已提交
60
{
P
Paul Brook 已提交
61
    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
B
balrog 已提交
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

    switch (s->cycle ++) {
    case 0:
        if (!(value & CB_START)) {
            s->cycle = 0;
            break;
        }

        s->output = s->input[(value >> 4) & 7];

        /* Imitate the ADC noise, some drivers expect this.  */
        s->noise = (s->noise + 3) & 7;
        switch ((value >> 4) & 7) {
        case 1: s->output += s->noise ^ 2; break;
        case 3: s->output += s->noise ^ 0; break;
        case 4: s->output += s->noise ^ 7; break;
        case 5: s->output += s->noise ^ 5; break;
        }

        if (value & CB_MODE)
            s->output >>= 4;	/* 8 bits instead of 12 */

        break;
    case 1:
        s->cycle = 0;
        break;
    }
P
Paul Brook 已提交
89
    return s->output;
B
balrog 已提交
90 91 92 93 94
}

static void ads7846_ts_event(void *opaque,
                int x, int y, int z, int buttons_state)
{
P
Paul Brook 已提交
95
    ADS7846State *s = opaque;
B
balrog 已提交
96 97

    if (buttons_state) {
98 99
        x = 0x7fff - x;
        s->input[1] = ADS_XPOS(x, y);
B
balrog 已提交
100 101
        s->input[3] = ADS_Z1POS(x, y);
        s->input[4] = ADS_Z2POS(x, y);
102
        s->input[5] = ADS_YPOS(x, y);
B
balrog 已提交
103 104 105 106 107
    }

    if (s->pressure == !buttons_state) {
        s->pressure = !!buttons_state;

108
        ads7846_int_update(s);
B
balrog 已提交
109 110 111
    }
}

J
Juan Quintela 已提交
112
static int ads7856_post_load(void *opaque, int version_id)
113
{
J
Juan Quintela 已提交
114
    ADS7846State *s = opaque;
115 116 117 118 119 120

    s->pressure = 0;
    ads7846_int_update(s);
    return 0;
}

J
Juan Quintela 已提交
121 122
static const VMStateDescription vmstate_ads7846 = {
    .name = "ads7846",
123 124
    .version_id = 1,
    .minimum_version_id = 1,
J
Juan Quintela 已提交
125
    .post_load = ads7856_post_load,
126
    .fields = (VMStateField[]) {
127
        VMSTATE_SSI_SLAVE(ssidev, ADS7846State),
J
Juan Quintela 已提交
128 129 130 131 132 133 134 135
        VMSTATE_INT32_ARRAY(input, ADS7846State, 8),
        VMSTATE_INT32(noise, ADS7846State),
        VMSTATE_INT32(cycle, ADS7846State),
        VMSTATE_INT32(output, ADS7846State),
        VMSTATE_END_OF_LIST()
    }
};

136
static int ads7846_init(SSISlave *d)
B
balrog 已提交
137
{
138 139
    DeviceState *dev = DEVICE(d);
    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, d);
B
balrog 已提交
140

141
    qdev_init_gpio_out(dev, &s->interrupt, 1);
B
balrog 已提交
142 143 144 145 146 147 148 149 150 151 152

    s->input[0] = ADS_TEMP0;	/* TEMP0 */
    s->input[2] = ADS_VBAT;	/* VBAT */
    s->input[6] = ADS_VAUX;	/* VAUX */
    s->input[7] = ADS_TEMP1;	/* TEMP1 */

    /* We want absolute coordinates */
    qemu_add_mouse_event_handler(ads7846_ts_event, s, 1,
                    "QEMU ADS7846-driven Touchscreen");

    ads7846_int_update(s);
153

J
Juan Quintela 已提交
154
    vmstate_register(NULL, -1, &vmstate_ads7846, s);
155
    return 0;
P
Paul Brook 已提交
156
}
157

158 159 160 161 162 163 164 165
static void ads7846_class_init(ObjectClass *klass, void *data)
{
    SSISlaveClass *k = SSI_SLAVE_CLASS(klass);

    k->init = ads7846_init;
    k->transfer = ads7846_transfer;
}

166
static const TypeInfo ads7846_info = {
167 168 169 170
    .name          = "ads7846",
    .parent        = TYPE_SSI_SLAVE,
    .instance_size = sizeof(ADS7846State),
    .class_init    = ads7846_class_init,
P
Paul Brook 已提交
171 172
};

A
Andreas Färber 已提交
173
static void ads7846_register_types(void)
P
Paul Brook 已提交
174
{
175
    type_register_static(&ads7846_info);
B
balrog 已提交
176
}
P
Paul Brook 已提交
177

A
Andreas Färber 已提交
178
type_init(ads7846_register_types)