ads7846.c 3.9 KB
Newer Older
B
balrog 已提交
1
/*
2
 * TI ADS7846 / TSC2046 chip emulation.
B
balrog 已提交
3 4 5 6 7 8 9
 *
 * Copyright (c) 2006 Openedhand Ltd.
 * Written by Andrzej Zaborowski <balrog@zabor.org>
 *
 * This code is licensed under the GNU GPL v2.
 */

P
Paul Brook 已提交
10
#include "ssi.h"
P
pbrook 已提交
11
#include "console.h"
B
balrog 已提交
12

P
Paul Brook 已提交
13 14
typedef struct {
    SSISlave ssidev;
B
balrog 已提交
15 16 17 18 19 20 21 22
    qemu_irq interrupt;

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

    int cycle;
    int output;
P
Paul Brook 已提交
23
} ADS7846State;
B
balrog 已提交
24 25 26 27 28 29 30 31 32 33 34

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

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

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

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

    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 已提交
85
    return s->output;
B
balrog 已提交
86 87 88 89 90
}

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

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

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

104
        ads7846_int_update(s);
B
balrog 已提交
105 106 107
    }
}

108 109
static void ads7846_save(QEMUFile *f, void *opaque)
{
P
Paul Brook 已提交
110
    ADS7846State *s = (ADS7846State *) opaque;
111 112 113 114 115 116 117 118 119 120 121
    int i;

    for (i = 0; i < 8; i ++)
        qemu_put_be32(f, s->input[i]);
    qemu_put_be32(f, s->noise);
    qemu_put_be32(f, s->cycle);
    qemu_put_be32(f, s->output);
}

static int ads7846_load(QEMUFile *f, void *opaque, int version_id)
{
P
Paul Brook 已提交
122
    ADS7846State *s = (ADS7846State *) opaque;
123 124 125 126 127 128 129 130 131 132 133 134 135 136
    int i;

    for (i = 0; i < 8; i ++)
        s->input[i] = qemu_get_be32(f);
    s->noise = qemu_get_be32(f);
    s->cycle = qemu_get_be32(f);
    s->output = qemu_get_be32(f);

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

    return 0;
}

137
static int ads7846_init(SSISlave *dev)
B
balrog 已提交
138
{
P
Paul Brook 已提交
139
    ADS7846State *s = FROM_SSI_SLAVE(ADS7846State, dev);
B
balrog 已提交
140

P
Paul Brook 已提交
141
    qdev_init_gpio_out(&dev->qdev, &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

A
Alex Williamson 已提交
154
    register_savevm(NULL, "ads7846", -1, 0, ads7846_save, ads7846_load, s);
155
    return 0;
P
Paul Brook 已提交
156
}
157

P
Paul Brook 已提交
158
static SSISlaveInfo ads7846_info = {
159 160
    .qdev.name ="ads7846",
    .qdev.size = sizeof(ADS7846State),
P
Paul Brook 已提交
161 162 163 164 165 166
    .init = ads7846_init,
    .transfer = ads7846_transfer
};

static void ads7846_register_devices(void)
{
167
    ssi_register_slave(&ads7846_info);
B
balrog 已提交
168
}
P
Paul Brook 已提交
169 170

device_init(ads7846_register_devices)