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
Paul Brook 已提交
13
#include "ssi.h"
P
pbrook 已提交
14
#include "console.h"
B
balrog 已提交
15

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

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

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

/* 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)

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

#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 已提交
52
static void ads7846_int_update(ADS7846State *s)
B
balrog 已提交
53 54 55 56 57
{
    if (s->interrupt)
        qemu_set_irq(s->interrupt, s->pressure == 0);
}

P
Paul Brook 已提交
58
static uint32_t ads7846_transfer(SSISlave *dev, uint32_t value)
B
balrog 已提交
59
{
P
Paul Brook 已提交
60
    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
B
balrog 已提交
61 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

    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 已提交
88
    return s->output;
B
balrog 已提交
89 90 91 92 93
}

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

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

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

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

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

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

J
Juan Quintela 已提交
120 121
static const VMStateDescription vmstate_ads7846 = {
    .name = "ads7846",
122 123 124
    .version_id = 1,
    .minimum_version_id = 1,
    .minimum_version_id_old = 1,
J
Juan Quintela 已提交
125 126
    .post_load = ads7856_post_load,
    .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 *dev)
B
balrog 已提交
137
{
P
Paul Brook 已提交
138
    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
B
balrog 已提交
139

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

    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);
152

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

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

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

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

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

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